diff options
37 files changed, 1030 insertions, 1 deletions
diff --git a/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc b/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc index e71688d..a191e75 100644 --- a/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc +++ b/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc @@ -80,4 +80,10 @@ FileSystemBackendDelegate::CreateFileStreamWriter( offset)); } +storage::WatcherManager* FileSystemBackendDelegate::GetWatcherManager( + const storage::FileSystemURL& url) { + NOTIMPLEMENTED(); + return NULL; +} + } // namespace drive diff --git a/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h b/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h index dae45ff..ea701cb 100644 --- a/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h +++ b/chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h @@ -11,6 +11,11 @@ namespace storage { class AsyncFileUtil; +class FileSystemContext; +class FileStreamReader; +class FileSystemURL; +class FileStreamWriter; +class WatcherManager; } // namespace storage namespace drive { @@ -34,6 +39,8 @@ class FileSystemBackendDelegate : public chromeos::FileSystemBackendDelegate { const storage::FileSystemURL& url, int64 offset, storage::FileSystemContext* context) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + const storage::FileSystemURL& url) OVERRIDE; private: scoped_ptr<storage::AsyncFileUtil> async_file_util_; diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc b/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc index 5cd5e30..df95444 100644 --- a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc +++ b/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc @@ -63,5 +63,11 @@ scoped_ptr<storage::FileStreamWriter> BackendDelegate::CreateFileStreamWriter( new FileStreamWriter(url, offset)); } +storage::WatcherManager* BackendDelegate::GetWatcherManager( + const storage::FileSystemURL& url) { + NOTIMPLEMENTED(); + return NULL; +} + } // namespace file_system_provider } // namespace chromeos diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h b/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h index 2362eaa..96618a2 100644 --- a/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h +++ b/chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h @@ -6,11 +6,17 @@ #define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_FILEAPI_BACKEND_DELEGATE_H_ #include "base/basictypes.h" +#include "base/file_util.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h" namespace storage { class AsyncFileUtil; +class FileSystemContext; +class FileStreamReader; +class FileSystemURL; +class FileStreamWriter; +class WatcherManager; } // namespace storage namespace chromeos { @@ -35,6 +41,8 @@ class BackendDelegate : public chromeos::FileSystemBackendDelegate { const storage::FileSystemURL& url, int64 offset, storage::FileSystemContext* context) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + const storage::FileSystemURL& url) OVERRIDE; private: scoped_ptr<storage::AsyncFileUtil> async_file_util_; diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc index ae2947d..5866c47 100644 --- a/chrome/browser/chromeos/fileapi/file_system_backend.cc +++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc @@ -249,6 +249,11 @@ storage::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil( return NULL; } +storage::WatcherManager* FileSystemBackend::GetWatcherManager( + storage::FileSystemType type) { + return NULL; +} + storage::CopyOrMoveFileValidatorFactory* FileSystemBackend::GetCopyOrMoveFileValidatorFactory( storage::FileSystemType type, diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.h b/chrome/browser/chromeos/fileapi/file_system_backend.h index a8109bf..8b5d50a 100644 --- a/chrome/browser/chromeos/fileapi/file_system_backend.h +++ b/chrome/browser/chromeos/fileapi/file_system_backend.h @@ -19,6 +19,7 @@ namespace storage { class CopyOrMoveFileValidatorFactory; class ExternalMountPoints; class FileSystemURL; +class WatcherManager; } // namespace storage namespace chromeos { @@ -93,6 +94,8 @@ class FileSystemBackend : public storage::ExternalFileSystemBackend { const OpenFileSystemCallback& callback) OVERRIDE; virtual storage::AsyncFileUtil* GetAsyncFileUtil( storage::FileSystemType type) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + storage::FileSystemType type) OVERRIDE; virtual storage::CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_delegate.h b/chrome/browser/chromeos/fileapi/file_system_backend_delegate.h index f083a91..b11c86a 100644 --- a/chrome/browser/chromeos/fileapi/file_system_backend_delegate.h +++ b/chrome/browser/chromeos/fileapi/file_system_backend_delegate.h @@ -16,8 +16,10 @@ class Time; namespace storage { class AsyncFileUtil; class FileSystemContext; +class FileStreamReader; class FileSystemURL; class FileStreamWriter; +class WatcherManager; } // namespace storage namespace storage { @@ -27,7 +29,7 @@ class FileStreamReader; namespace chromeos { // This is delegate interface to inject the implementation of the some methods -// of FileSystemBackend. The main goal is to inject Drive File System. +// of FileSystemBackend. class FileSystemBackendDelegate { public: virtual ~FileSystemBackendDelegate() {} @@ -48,6 +50,11 @@ class FileSystemBackendDelegate { const storage::FileSystemURL& url, int64 offset, storage::FileSystemContext* context) = 0; + + // Called from the FileSystemWatcherService class. The returned pointer must + // stay valid until shutdown. + virtual storage::WatcherManager* GetWatcherManager( + const storage::FileSystemURL& url) = 0; }; } // namespace chromeos diff --git a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc index 62cbf9f..21de202 100644 --- a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc +++ b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc @@ -50,4 +50,10 @@ MTPFileSystemBackendDelegate::CreateFileStreamWriter( return scoped_ptr<storage::FileStreamWriter>(); } +storage::WatcherManager* MTPFileSystemBackendDelegate::GetWatcherManager( + const storage::FileSystemURL& url) { + NOTIMPLEMENTED(); + return NULL; +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h index 2929194..06e751a 100644 --- a/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h +++ b/chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h @@ -12,6 +12,15 @@ namespace base { class FilePath; } // namespace base +namespace storage { +class AsyncFileUtil; +class FileSystemContext; +class FileStreamReader; +class FileSystemURL; +class FileStreamWriter; +class WatcherManager; +} // namespace storage + class DeviceMediaAsyncFileUtil; namespace chromeos { @@ -36,6 +45,8 @@ class MTPFileSystemBackendDelegate : public FileSystemBackendDelegate { const storage::FileSystemURL& url, int64 offset, storage::FileSystemContext* context) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + const storage::FileSystemURL& url) OVERRIDE; private: scoped_ptr<DeviceMediaAsyncFileUtil> device_media_async_file_util_; diff --git a/chrome/browser/extensions/api/file_system/OWNERS b/chrome/browser/extensions/api/file_system/OWNERS index 64f6677..ac81da7 100644 --- a/chrome/browser/extensions/api/file_system/OWNERS +++ b/chrome/browser/extensions/api/file_system/OWNERS @@ -1,2 +1,3 @@ benwells@chromium.org +mtomasz@chromium.org sammc@chromium.org diff --git a/chrome/browser/extensions/api/file_system/entry_watcher_service.cc b/chrome/browser/extensions/api/file_system/entry_watcher_service.cc new file mode 100644 index 0000000..1591869 --- /dev/null +++ b/chrome/browser/extensions/api/file_system/entry_watcher_service.cc @@ -0,0 +1,253 @@ +// 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/extensions/api/file_system/entry_watcher_service.h" + +#include "base/thread_task_runner_handle.h" +#include "chrome/browser/extensions/extension_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/file_system.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" +#include "extensions/browser/event_router.h" +#include "webkit/browser/fileapi/file_system_context.h" + +namespace extensions { +namespace { + +// Default implementation for dispatching an event. Can be replaced for unit +// tests by EntryWatcherService::SetDispatchEventImplForTesting(). +void DispatchEventImpl(EventRouter* event_router, + const std::string& extension_id, + scoped_ptr<Event> event) { + event_router->DispatchEventToExtension(extension_id, event.Pass()); +} + +// Default implementation for acquiring a file system context for a specific +// |extension_id| and |context|. +storage::FileSystemContext* GetFileSystemContextImpl( + const std::string& extension_id, + content::BrowserContext* context) { + const GURL site = util::GetSiteForExtensionId(extension_id, context); + return content::BrowserContext::GetStoragePartitionForSite(context, site) + ->GetFileSystemContext(); +} + +} // namespace + +EntryWatcherService::EntryWatcherService(content::BrowserContext* context) + : context_(context), + dispatch_event_impl_( + base::Bind(&DispatchEventImpl, EventRouter::Get(context))), + get_file_system_context_impl_(base::Bind(&GetFileSystemContextImpl)), + observing_(this), + weak_ptr_factory_(this) { + // TODO(mtomasz): Restore persistent watchers. +} + +EntryWatcherService::~EntryWatcherService() { +} + +void EntryWatcherService::SetDispatchEventImplForTesting( + const DispatchEventImplCallback& callback) { + dispatch_event_impl_ = callback; +} + +void EntryWatcherService::SetGetFileSystemContextImplForTesting( + const GetFileSystemContextImplCallback& callback) { + get_file_system_context_impl_ = callback; +} + +void EntryWatcherService::WatchDirectory( + const std::string& extension_id, + const storage::FileSystemURL& url, + bool recursive, + const storage::WatcherManager::StatusCallback& callback) { + // TODO(mtomasz): Add support for recursive watchers. + if (recursive) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); + return; + } + + storage::FileSystemContext* const context = + get_file_system_context_impl_.Run(extension_id, context_); + DCHECK(context); + + storage::WatcherManager* const watcher_manager = + context->GetWatcherManager(url.type()); + if (!watcher_manager) { + // Post a task instead of calling the callback directly, since the caller + // may expect the callback to be called asynchronously. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); + return; + } + + // Passing a pointer to WatcherManager is safe, since the pointer is required + // to be valid until shutdown. + context->default_file_task_runner()->PostNonNestableTask( + FROM_HERE, + base::Bind(&storage::WatcherManager::WatchDirectory, + base::Unretained(watcher_manager), // Outlives the service. + url, + recursive, + base::Bind(&EntryWatcherService::OnWatchDirectoryCompleted, + weak_ptr_factory_.GetWeakPtr(), + watcher_manager, // Outlives the service. + extension_id, + url, + recursive, + callback))); +} + +void EntryWatcherService::UnwatchEntry( + const std::string& extension_id, + const storage::FileSystemURL& url, + const storage::WatcherManager::StatusCallback& callback) { + storage::FileSystemContext* const context = + get_file_system_context_impl_.Run(extension_id, context_); + DCHECK(context); + + storage::WatcherManager* const watcher_manager = + context->GetWatcherManager(url.type()); + if (!watcher_manager) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); + return; + } + + // Passing a pointer to WatcherManager is safe, since the pointer is required + // to be valid until shutdown. + context->default_file_task_runner()->PostNonNestableTask( + FROM_HERE, + base::Bind(&storage::WatcherManager::UnwatchEntry, + base::Unretained(watcher_manager), // Outlives the service. + url, + base::Bind(&EntryWatcherService::OnUnwatchEntryCompleted, + weak_ptr_factory_.GetWeakPtr(), + extension_id, + url, + callback))); +} + +std::vector<storage::FileSystemURL> EntryWatcherService::GetWatchedEntries( + const std::string& extension_id) { + std::vector<storage::FileSystemURL> result; + for (WatcherMap::const_iterator it = watchers_.begin(); it != watchers_.end(); + ++it) { + const std::map<std::string, EntryWatcher>::const_iterator watcher_it = + it->second.find(extension_id); + if (watcher_it != it->second.end()) + result.push_back(watcher_it->second.url); + } + + return result; +} + +void EntryWatcherService::OnEntryChanged(const storage::FileSystemURL& url) { + const WatcherMap::const_iterator it = watchers_.find(url); + DCHECK(it != watchers_.end()); + for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = + it->second.begin(); + watcher_it != it->second.end(); + ++watcher_it) { + const std::string& extension_id = watcher_it->first; + api::file_system::EntryChangedEvent event; + dispatch_event_impl_.Run( + extension_id, + make_scoped_ptr( + new Event(api::file_system::OnEntryChanged::kEventName, + api::file_system::OnEntryChanged::Create(event)))); + } +} + +void EntryWatcherService::OnEntryRemoved(const storage::FileSystemURL& url) { + WatcherMap::const_iterator it = watchers_.find(url); + DCHECK(it != watchers_.end()); + for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = + it->second.begin(); + watcher_it != it->second.end(); + ++watcher_it) { + const std::string& extension_id = watcher_it->first; + api::file_system::EntryRemovedEvent event; + dispatch_event_impl_.Run( + extension_id, + make_scoped_ptr( + new Event(api::file_system::OnEntryRemoved::kEventName, + api::file_system::OnEntryRemoved::Create(event)))); + } +} + +void EntryWatcherService::OnWatchDirectoryCompleted( + storage::WatcherManager* watcher_manager, + const std::string& extension_id, + const storage::FileSystemURL& url, + bool recursive, + const storage::WatcherManager::StatusCallback& callback, + base::File::Error result) { + if (result != base::File::FILE_OK) { + callback.Run(result); + return; + } + + storage::FileSystemContext* const context = + get_file_system_context_impl_.Run(extension_id, context_); + DCHECK(context); + + // Observe the manager if not observed yet. + if (!observing_.IsObserving(watcher_manager)) + observing_.Add(watcher_manager); + + watchers_[url][extension_id] = + EntryWatcher(url, true /* directory */, recursive); + + // TODO(mtomasz): Save in preferences. + + callback.Run(base::File::FILE_OK); +} + +void EntryWatcherService::OnUnwatchEntryCompleted( + const std::string& extension_id, + const storage::FileSystemURL& url, + const storage::WatcherManager::StatusCallback& callback, + base::File::Error result) { + if (result != base::File::FILE_OK) { + callback.Run(result); + return; + } + + if (watchers_[url].erase(extension_id) == 0) { + callback.Run(base::File::FILE_ERROR_NOT_FOUND); + return; + } + + if (watchers_[url].empty()) + watchers_.erase(url); + + // TODO(mtomasz): Save in preferences. + + callback.Run(base::File::FILE_OK); +} + +EntryWatcherService::EntryWatcher::EntryWatcher() + : directory(false), recursive(false) { +} + +EntryWatcherService::EntryWatcher::EntryWatcher( + const storage::FileSystemURL& url, + bool directory, + bool recursive) + : url(url), directory(directory), recursive(recursive) { +} + +EntryWatcherService::EntryWatcher::~EntryWatcher() { +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/file_system/entry_watcher_service.h b/chrome/browser/extensions/api/file_system/entry_watcher_service.h new file mode 100644 index 0000000..0417992 --- /dev/null +++ b/chrome/browser/extensions/api/file_system/entry_watcher_service.h @@ -0,0 +1,128 @@ +// 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_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_H_ +#define CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/memory/singleton.h" +#include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "components/keyed_service/core/keyed_service.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/browser/fileapi/watcher_manager.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace storage { +class FileSystemContext; +} // namespace storage + +namespace extensions { +struct Event; +class EventRouter; + +// Watches entries (files and directories) for changes. Created per profile. +// TODO(mtomasz): Add support for watching files. +class EntryWatcherService : public KeyedService, + public storage::WatcherManager::Observer { + public: + typedef base::Callback< + void(const std::string& extension_id, scoped_ptr<Event> event)> + DispatchEventImplCallback; + + typedef base::Callback<storage::FileSystemContext*( + const std::string& extension_id, + content::BrowserContext* context)> GetFileSystemContextImplCallback; + + explicit EntryWatcherService(content::BrowserContext* context); + virtual ~EntryWatcherService(); + + // Watches a directory. Only one watcher can be set per the same |url| and + // |extension_id|. + void WatchDirectory(const std::string& extension_id, + const storage::FileSystemURL& url, + bool recursive, + const storage::WatcherManager::StatusCallback& callback); + + // Unwatches an entry (file or directory). + void UnwatchEntry(const std::string& extension_id, + const storage::FileSystemURL& url, + const storage::WatcherManager::StatusCallback& callback); + + std::vector<storage::FileSystemURL> GetWatchedEntries( + const std::string& extension_id); + + // storage::WatcherManager::Observer overrides. + virtual void OnEntryChanged(const storage::FileSystemURL& url) OVERRIDE; + virtual void OnEntryRemoved(const storage::FileSystemURL& url) OVERRIDE; + + // Sets a custom dispatcher for tests in order to be able to verify dispatched + // events. + void SetDispatchEventImplForTesting( + const DispatchEventImplCallback& callback); + + // Sets a custom context getter for tests in order to inject a testing + // file system context implementation. + void SetGetFileSystemContextImplForTesting( + const GetFileSystemContextImplCallback& callback); + + private: + // Holds information about an active entry watcher. + struct EntryWatcher { + EntryWatcher(); + EntryWatcher(const storage::FileSystemURL& url, + bool directory, + bool recursive); + ~EntryWatcher(); + + storage::FileSystemURL url; + bool directory; + bool recursive; + }; + + // Map from a file system url to a map from an extension id to an entry + // watcher descriptor. + typedef std::map<storage::FileSystemURL, + std::map<std::string, EntryWatcher>, + storage::FileSystemURL::Comparator> WatcherMap; + + // Called when adding a directory watcher is completed with either a success + // or an error. + void OnWatchDirectoryCompleted( + storage::WatcherManager* watcher_manager, + const std::string& extension_id, + const storage::FileSystemURL& url, + bool recursive, + const storage::WatcherManager::StatusCallback& callback, + base::File::Error result); + + // Called when removing a watcher is completed with either a success or an + // error. + void OnUnwatchEntryCompleted( + const std::string& extension_id, + const storage::FileSystemURL& url, + const storage::WatcherManager::StatusCallback& callback, + base::File::Error result); + + content::BrowserContext* context_; + WatcherMap watchers_; + DispatchEventImplCallback dispatch_event_impl_; + GetFileSystemContextImplCallback get_file_system_context_impl_; + ScopedObserver<storage::WatcherManager, storage::WatcherManager::Observer> + observing_; + base::WeakPtrFactory<EntryWatcherService> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(EntryWatcherService); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_H_ diff --git a/chrome/browser/extensions/api/file_system/entry_watcher_service_factory.cc b/chrome/browser/extensions/api/file_system/entry_watcher_service_factory.cc new file mode 100644 index 0000000..d0273e2 --- /dev/null +++ b/chrome/browser/extensions/api/file_system/entry_watcher_service_factory.cc @@ -0,0 +1,37 @@ +// 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/extensions/api/file_system/entry_watcher_service_factory.h" + +#include "chrome/browser/extensions/api/file_system/entry_watcher_service.h" +#include "chrome/browser/profiles/profile.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace extensions { + +EntryWatcherServiceFactory* EntryWatcherServiceFactory::GetInstance() { + return Singleton<EntryWatcherServiceFactory>::get(); +} + +EntryWatcherServiceFactory::EntryWatcherServiceFactory() + : BrowserContextKeyedServiceFactory( + "EntryWatcherService", + BrowserContextDependencyManager::GetInstance()) { +} + +EntryWatcherServiceFactory::~EntryWatcherServiceFactory() { +} + +KeyedService* EntryWatcherServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new EntryWatcherService(Profile::FromBrowserContext(context)); +} + +bool EntryWatcherServiceFactory::ServiceIsCreatedWithBrowserContext() const { + // Required to restore persistent watchers as soon as the profile is loaded. + return true; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/file_system/entry_watcher_service_factory.h b/chrome/browser/extensions/api/file_system/entry_watcher_service_factory.h new file mode 100644 index 0000000..36296a4 --- /dev/null +++ b/chrome/browser/extensions/api/file_system/entry_watcher_service_factory.h @@ -0,0 +1,49 @@ +// 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_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace extensions { + +class EntryWatcherService; + +// Creates instances of the EntryWatcherService class. +class EntryWatcherServiceFactory : public BrowserContextKeyedServiceFactory { + public: + // Returns a service instance singleton per |context|, after creating it + // (if necessary). + static EntryWatcherService* Get(content::BrowserContext* context); + + // Returns a service instance for the context if exists. Otherwise, returns + // NULL. + static EntryWatcherService* FindExisting(content::BrowserContext* context); + + // Gets a singleton instance of the factory. + static EntryWatcherServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<EntryWatcherServiceFactory>; + + EntryWatcherServiceFactory(); + virtual ~EntryWatcherServiceFactory(); + + // BrowserContextKeyedServiceFactory overrides: + virtual KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const OVERRIDE; + virtual bool ServiceIsCreatedWithBrowserContext() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(EntryWatcherServiceFactory); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_FACTORY_H_ diff --git a/chrome/browser/extensions/api/file_system/entry_watcher_service_unittest.cc b/chrome/browser/extensions/api/file_system/entry_watcher_service_unittest.cc new file mode 100644 index 0000000..94b4d8f --- /dev/null +++ b/chrome/browser/extensions/api/file_system/entry_watcher_service_unittest.cc @@ -0,0 +1,254 @@ +// 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/extensions/api/file_system/entry_watcher_service.h" + +#include <string> +#include <vector> + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_vector.h" +#include "base/run_loop.h" +#include "chrome/common/extensions/api/file_system.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_file_system_context.h" +#include "extensions/browser/event_router.h" +#include "webkit/browser/fileapi/file_system_url.h" +#include "webkit/common/fileapi/file_system_types.h" + +namespace extensions { +namespace { + +const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj"; + +void LogStatus(std::vector<base::File::Error>* log, base::File::Error status) { + log->push_back(status); +} + +} // namespace + +class EntryWatcherServiceTest : public testing::Test { + protected: + EntryWatcherServiceTest() {} + virtual ~EntryWatcherServiceTest() {} + + virtual void SetUp() OVERRIDE { + profile_.reset(new TestingProfile); + ASSERT_TRUE(data_dir_.CreateUniqueTempDir()); + file_system_context_ = + content::CreateFileSystemContextForTesting(NULL, data_dir_.path()); + service_.reset(new EntryWatcherService(profile_.get())); + service_->SetDispatchEventImplForTesting(base::Bind( + &EntryWatcherServiceTest::DispatchEventImpl, base::Unretained(this))); + service_->SetGetFileSystemContextImplForTesting( + base::Bind(&EntryWatcherServiceTest::GetFileSystemContextImpl, + base::Unretained(this))); + testing_url_ = file_system_context_->CreateCrackedFileSystemURL( + GURL(std::string("chrome-extension://") + kExtensionId), + storage::kFileSystemTypeTest, + base::FilePath::FromUTF8Unsafe("/x/y/z")); + } + + virtual void TearDown() OVERRIDE { + dispatch_event_log_targets_.clear(); + dispatch_event_log_events_.clear(); + } + + void DispatchEventImpl(const std::string& extension_id, + scoped_ptr<Event> event) { + dispatch_event_log_targets_.push_back(extension_id); + dispatch_event_log_events_.push_back(event.release()); + } + + storage::FileSystemContext* GetFileSystemContextImpl( + const std::string& extension_id, + content::BrowserContext* context) { + EXPECT_EQ(kExtensionId, extension_id); + EXPECT_EQ(profile_.get(), context); + return file_system_context_.get(); + } + + content::TestBrowserThreadBundle thread_bundle_; + scoped_ptr<TestingProfile> profile_; + base::ScopedTempDir data_dir_; + scoped_refptr<storage::FileSystemContext> file_system_context_; + scoped_ptr<EntryWatcherService> service_; + storage::FileSystemURL testing_url_; + std::vector<std::string> dispatch_event_log_targets_; + ScopedVector<Event> dispatch_event_log_events_; +}; + +TEST_F(EntryWatcherServiceTest, GetWatchedEntries) { + std::vector<base::File::Error> log; + + const bool recursive = false; + service_->WatchDirectory( + kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(1u, log.size()); + EXPECT_EQ(base::File::FILE_OK, log[0]); + + { + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + ASSERT_EQ(1u, watched_entries.size()); + EXPECT_EQ(testing_url_, watched_entries[0]); + } + + { + const std::string wrong_extension_id = "abcabcabcabcabcabcabcabcabcabcab"; + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(wrong_extension_id); + EXPECT_EQ(0u, watched_entries.size()); + } +} + +TEST_F(EntryWatcherServiceTest, WatchDirectory) { + std::vector<base::File::Error> log; + + const bool recursive = false; + service_->WatchDirectory( + kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(1u, log.size()); + EXPECT_EQ(base::File::FILE_OK, log[0]); + + // The testing WatcherManager implementation emits two hard-coded fake + // notifications as soon as the watcher is set properly. See: + // TestWatcherManager::WatchDirectory() for details. + ASSERT_LE(1u, dispatch_event_log_targets_.size()); + ASSERT_LE(1u, dispatch_event_log_events_.size()); + + EXPECT_EQ(kExtensionId, dispatch_event_log_targets_[0]); + EXPECT_EQ(api::file_system::OnEntryChanged::kEventName, + dispatch_event_log_events_[0]->event_name); + + ASSERT_LE(2u, dispatch_event_log_targets_.size()); + ASSERT_LE(2u, dispatch_event_log_events_.size()); + EXPECT_EQ(kExtensionId, dispatch_event_log_targets_[1]); + EXPECT_EQ(api::file_system::OnEntryRemoved::kEventName, + dispatch_event_log_events_[1]->event_name); + + // No unexpected events. + ASSERT_EQ(2u, dispatch_event_log_targets_.size()); + ASSERT_EQ(2u, dispatch_event_log_events_.size()); + + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + ASSERT_EQ(1u, watched_entries.size()); + EXPECT_EQ(testing_url_, watched_entries[0]); +} + +TEST_F(EntryWatcherServiceTest, WatchDirectory_AlreadyExists) { + std::vector<base::File::Error> log; + + const bool recursive = false; + service_->WatchDirectory( + kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(1u, log.size()); + EXPECT_EQ(base::File::FILE_OK, log[0]); + + ASSERT_EQ(2u, dispatch_event_log_targets_.size()); + ASSERT_EQ(2u, dispatch_event_log_events_.size()); + + { + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + EXPECT_EQ(1u, watched_entries.size()); + } + + service_->WatchDirectory( + kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(2u, log.size()); + EXPECT_EQ(base::File::FILE_ERROR_EXISTS, log[1]); + + // No new unexpected events. + ASSERT_EQ(2u, dispatch_event_log_targets_.size()); + ASSERT_EQ(2u, dispatch_event_log_events_.size()); + + { + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + EXPECT_EQ(1u, watched_entries.size()); + } +} + +TEST_F(EntryWatcherServiceTest, WatchDirectory_Recursive) { + std::vector<base::File::Error> log; + + const bool recursive = true; + service_->WatchDirectory( + kExtensionId, testing_url_, recursive, base::Bind(&LogStatus, &log)); + base::RunLoop().RunUntilIdle(); + + // Recursive watchers are not supported yet. + ASSERT_EQ(1u, log.size()); + EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, log[0]); + + // No unexpected events. + ASSERT_EQ(0u, dispatch_event_log_targets_.size()); + ASSERT_EQ(0u, dispatch_event_log_events_.size()); + + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + EXPECT_EQ(0u, watched_entries.size()); +} + +TEST_F(EntryWatcherServiceTest, UnwatchEntry) { + std::vector<base::File::Error> watch_log; + + const bool recursive = false; + service_->WatchDirectory(kExtensionId, + testing_url_, + recursive, + base::Bind(&LogStatus, &watch_log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(1u, watch_log.size()); + EXPECT_EQ(base::File::FILE_OK, watch_log[0]); + + ASSERT_EQ(2u, dispatch_event_log_targets_.size()); + ASSERT_EQ(2u, dispatch_event_log_events_.size()); + + { + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + EXPECT_EQ(1u, watched_entries.size()); + } + + std::vector<base::File::Error> unwatch_log; + service_->UnwatchEntry( + kExtensionId, testing_url_, base::Bind(&LogStatus, &unwatch_log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(1u, unwatch_log.size()); + EXPECT_EQ(base::File::FILE_OK, unwatch_log[0]); + + { + const std::vector<storage::FileSystemURL> watched_entries = + service_->GetWatchedEntries(kExtensionId); + EXPECT_EQ(0u, watched_entries.size()); + } +} + +TEST_F(EntryWatcherServiceTest, UnwatchEntry_NotFound) { + std::vector<base::File::Error> log; + service_->UnwatchEntry( + kExtensionId, testing_url_, base::Bind(&LogStatus, &log)); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(1u, log.size()); + EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0]); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/file_system_api.cc index 7e9fd89..84b6e00 100644 --- a/chrome/browser/extensions/api/file_system/file_system_api.cc +++ b/chrome/browser/extensions/api/file_system/file_system_api.cc @@ -4,6 +4,8 @@ #include "chrome/browser/extensions/api/file_system/file_system_api.h" +#include <set> + #include "apps/app_window.h" #include "apps/app_window_registry.h" #include "apps/saved_files_service.h" diff --git a/chrome/browser/extensions/api/file_system/file_system_api.h b/chrome/browser/extensions/api/file_system/file_system_api.h index 328f8d7..1d16abe 100644 --- a/chrome/browser/extensions/api/file_system/file_system_api.h +++ b/chrome/browser/extensions/api/file_system/file_system_api.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_FILE_SYSTEM_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_FILE_SYSTEM_API_H_ +#include <string> #include <vector> #include "base/files/file_path.h" diff --git a/chrome/browser/local_discovery/storage/privet_filesystem_backend.cc b/chrome/browser/local_discovery/storage/privet_filesystem_backend.cc index 7ec937c..9abfcaf 100644 --- a/chrome/browser/local_discovery/storage/privet_filesystem_backend.cc +++ b/chrome/browser/local_discovery/storage/privet_filesystem_backend.cc @@ -55,6 +55,11 @@ storage::AsyncFileUtil* PrivetFileSystemBackend::GetAsyncFileUtil( return async_util_.get(); } +storage::WatcherManager* PrivetFileSystemBackend::GetWatcherManager( + storage::FileSystemType type) { + return NULL; +} + storage::CopyOrMoveFileValidatorFactory* PrivetFileSystemBackend::GetCopyOrMoveFileValidatorFactory( storage::FileSystemType type, diff --git a/chrome/browser/local_discovery/storage/privet_filesystem_backend.h b/chrome/browser/local_discovery/storage/privet_filesystem_backend.h index e368a9f..bed35d7 100644 --- a/chrome/browser/local_discovery/storage/privet_filesystem_backend.h +++ b/chrome/browser/local_discovery/storage/privet_filesystem_backend.h @@ -35,6 +35,8 @@ class PrivetFileSystemBackend : public storage::FileSystemBackend { virtual storage::AsyncFileUtil* GetAsyncFileUtil( storage::FileSystemType type) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + storage::FileSystemType type) OVERRIDE; virtual storage::CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc index b160135..1417d33 100644 --- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc +++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc @@ -5,6 +5,7 @@ #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" #include <string> +#include <vector> #include "base/bind.h" #include "base/files/file_path.h" @@ -269,6 +270,11 @@ storage::AsyncFileUtil* MediaFileSystemBackend::GetAsyncFileUtil( return NULL; } +storage::WatcherManager* MediaFileSystemBackend::GetWatcherManager( + storage::FileSystemType type) { + return NULL; +} + storage::CopyOrMoveFileValidatorFactory* MediaFileSystemBackend::GetCopyOrMoveFileValidatorFactory( storage::FileSystemType type, diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.h b/chrome/browser/media_galleries/fileapi/media_file_system_backend.h index eb44eca..cbb6ed6 100644 --- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.h +++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.h @@ -61,6 +61,8 @@ class MediaFileSystemBackend : public storage::FileSystemBackend { const OpenFileSystemCallback& callback) OVERRIDE; virtual storage::AsyncFileUtil* GetAsyncFileUtil( storage::FileSystemType type) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + storage::FileSystemType type) OVERRIDE; virtual storage::CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc index b3b345f..ebf1eb3 100644 --- a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc +++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc @@ -4,6 +4,8 @@ #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" +#include <string> + #include "base/logging.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h" @@ -138,6 +140,11 @@ storage::AsyncFileUtil* SyncFileSystemBackend::GetAsyncFileUtil( return GetDelegate()->file_util(); } +storage::WatcherManager* SyncFileSystemBackend::GetWatcherManager( + storage::FileSystemType type) { + return NULL; +} + storage::CopyOrMoveFileValidatorFactory* SyncFileSystemBackend::GetCopyOrMoveFileValidatorFactory( storage::FileSystemType type, diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.h b/chrome/browser/sync_file_system/local/sync_file_system_backend.h index 473ea7e..877928a 100644 --- a/chrome/browser/sync_file_system/local/sync_file_system_backend.h +++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.h @@ -34,6 +34,8 @@ class SyncFileSystemBackend : public storage::FileSystemBackend { const OpenFileSystemCallback& callback) OVERRIDE; virtual storage::AsyncFileUtil* GetAsyncFileUtil( storage::FileSystemType type) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + storage::FileSystemType type) OVERRIDE; virtual storage::CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 16befea..5024a7f 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -283,6 +283,10 @@ 'browser/extensions/api/file_handlers/app_file_handler_util.h', 'browser/extensions/api/file_handlers/mime_util.cc', 'browser/extensions/api/file_handlers/mime_util.h', + 'browser/extensions/api/file_system/entry_watcher_service.cc', + 'browser/extensions/api/file_system/entry_watcher_service.h', + 'browser/extensions/api/file_system/entry_watcher_service_factory.cc', + 'browser/extensions/api/file_system/entry_watcher_service_factory.h', 'browser/extensions/api/file_system/file_system_api.cc', 'browser/extensions/api/file_system/file_system_api.h', 'browser/extensions/api/font_settings/font_settings_api.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index b5395db..519abb0 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -868,6 +868,7 @@ 'browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc', 'browser/extensions/api/extension_action/extension_action_prefs_unittest.cc', 'browser/extensions/api/file_handlers/mime_util_unittest.cc', + 'browser/extensions/api/file_system/entry_watcher_service_unittest.cc', 'browser/extensions/api/file_system/file_system_api_unittest.cc', 'browser/extensions/api/identity/extension_token_key_unittest.cc', 'browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc', diff --git a/content/public/test/test_file_system_backend.cc b/content/public/test/test_file_system_backend.cc index f48a034..47f48d4 100644 --- a/content/public/test/test_file_system_backend.cc +++ b/content/public/test/test_file_system_backend.cc @@ -9,7 +9,11 @@ #include <vector> #include "base/file_util.h" +#include "base/files/file.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" #include "base/sequenced_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "webkit/browser/blob/file_stream_reader.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" #include "webkit/browser/fileapi/file_observers.h" @@ -20,6 +24,7 @@ #include "webkit/browser/fileapi/native_file_util.h" #include "webkit/browser/fileapi/quota/quota_reservation.h" #include "webkit/browser/fileapi/sandbox_file_stream_writer.h" +#include "webkit/browser/fileapi/watcher_manager.h" #include "webkit/browser/quota/quota_manager.h" #include "webkit/common/fileapi/file_system_util.h" @@ -32,6 +37,7 @@ namespace content { namespace { +// Stub implementation of storage::LocalFileUtil. class TestFileUtil : public storage::LocalFileUtil { public: explicit TestFileUtil(const base::FilePath& base_path) @@ -51,6 +57,101 @@ class TestFileUtil : public storage::LocalFileUtil { base::FilePath base_path_; }; +// Stub implementation of storage::WatcherManager. Emits a fake notification +// after a directory watcher is set successfully. +class TestWatcherManager : public storage::WatcherManager { + public: + TestWatcherManager() : weak_ptr_factory_(this) {} + virtual ~TestWatcherManager() {} + + // storage::WatcherManager overrides. + virtual void AddObserver(Observer* observer) OVERRIDE { + observers_.AddObserver(observer); + } + + virtual void RemoveObserver(Observer* observer) OVERRIDE { + observers_.RemoveObserver(observer); + } + + virtual bool HasObserver(Observer* observer) const OVERRIDE { + return observers_.HasObserver(observer); + } + + virtual void WatchDirectory(const storage::FileSystemURL& url, + bool recursive, + const StatusCallback& callback) OVERRIDE { + if (recursive) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); + return; + } + + const GURL gurl = url.ToGURL(); + if (watched_urls_.find(gurl) != watched_urls_.end()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_EXISTS)); + return; + } + + watched_urls_.insert(gurl); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, base::File::FILE_OK)); + + // Send a fake changed notification. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&TestWatcherManager::SendFakeChangeNotification, + weak_ptr_factory_.GetWeakPtr(), + url)); + + // Send a fake removed notification. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&TestWatcherManager::SendFakeRemoveNotification, + weak_ptr_factory_.GetWeakPtr(), + url)); + } + + virtual void UnwatchEntry(const storage::FileSystemURL& url, + const StatusCallback& callback) OVERRIDE { + const GURL gurl = url.ToGURL(); + if (watched_urls_.find(gurl) == watched_urls_.end()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND)); + return; + } + + watched_urls_.erase(gurl); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, base::File::FILE_OK)); + } + + private: + // Sends a fake notification to each observer about a changed entry + // represented by |url|, as long as it is still being watched. + void SendFakeChangeNotification(const storage::FileSystemURL& url) { + if (watched_urls_.find(url.ToGURL()) == watched_urls_.end()) + return; + + FOR_EACH_OBSERVER(Observer, observers_, OnEntryChanged(url)); + } + + // Sends a fake notification to each observer about a removed entry + // represented by |url|, as long as it is still being watched. + void SendFakeRemoveNotification(const storage::FileSystemURL& url) { + if (watched_urls_.find(url.ToGURL()) == watched_urls_.end()) + return; + + FOR_EACH_OBSERVER(Observer, observers_, OnEntryRemoved(url)); + } + + ObserverList<Observer> observers_; + std::set<GURL> watched_urls_; + + base::WeakPtrFactory<TestWatcherManager> weak_ptr_factory_; +}; + } // namespace // This only supports single origin. @@ -162,6 +263,7 @@ TestFileSystemBackend::TestFileSystemBackend( : base_path_(base_path), file_util_( new storage::AsyncFileUtilAdapter(new TestFileUtil(base_path))), + watcher_manager_(new TestWatcherManager()), quota_util_(new QuotaUtil(task_runner)), require_copy_or_move_validator_(false) { } @@ -189,6 +291,11 @@ storage::AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil( return file_util_.get(); } +storage::WatcherManager* TestFileSystemBackend::GetWatcherManager( + storage::FileSystemType type) { + return watcher_manager_.get(); +} + storage::CopyOrMoveFileValidatorFactory* TestFileSystemBackend::GetCopyOrMoveFileValidatorFactory( storage::FileSystemType type, diff --git a/content/public/test/test_file_system_backend.h b/content/public/test/test_file_system_backend.h index 2c80e62..bcea543 100644 --- a/content/public/test/test_file_system_backend.h +++ b/content/public/test/test_file_system_backend.h @@ -41,6 +41,8 @@ class TestFileSystemBackend : public storage::FileSystemBackend { const OpenFileSystemCallback& callback) OVERRIDE; virtual storage::AsyncFileUtil* GetAsyncFileUtil( storage::FileSystemType type) OVERRIDE; + virtual storage::WatcherManager* GetWatcherManager( + storage::FileSystemType type) OVERRIDE; virtual storage::CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type, base::File::Error* error_code) OVERRIDE; @@ -85,6 +87,7 @@ class TestFileSystemBackend : public storage::FileSystemBackend { base::FilePath base_path_; scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_ptr<storage::AsyncFileUtilAdapter> file_util_; + scoped_ptr<storage::WatcherManager> watcher_manager_; scoped_ptr<QuotaUtil> quota_util_; bool require_copy_or_move_validator_; diff --git a/webkit/browser/fileapi/file_system_backend.h b/webkit/browser/fileapi/file_system_backend.h index 9853745..7f0220d 100644 --- a/webkit/browser/fileapi/file_system_backend.h +++ b/webkit/browser/fileapi/file_system_backend.h @@ -33,6 +33,7 @@ class FileSystemContext; class FileSystemFileUtil; class FileSystemOperation; class FileSystemQuotaUtil; +class WatcherManager; // An interface for defining a file system backend. // @@ -70,6 +71,9 @@ class STORAGE_EXPORT FileSystemBackend { // Returns the specialized AsyncFileUtil for this backend. virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) = 0; + // Returns the specialized WatcherManager for this backend. + virtual WatcherManager* GetWatcherManager(FileSystemType type) = 0; + // Returns the specialized CopyOrMoveFileValidatorFactory for this backend // and |type|. If |error_code| is File::FILE_OK and the result is NULL, // then no validator is required. @@ -105,6 +109,8 @@ class STORAGE_EXPORT FileSystemBackend { // ERR_UPLOAD_FILE_CHANGED error. // This method itself does *not* check if the given path exists and is a // regular file. + // The |length| argument says how many bytes are going to be read using the + // instance of the file stream reader. If unknown, then equal to -1. virtual scoped_ptr<storage::FileStreamReader> CreateFileStreamReader( const FileSystemURL& url, int64 offset, diff --git a/webkit/browser/fileapi/file_system_context.cc b/webkit/browser/fileapi/file_system_context.cc index 5d44499..cb62556 100644 --- a/webkit/browser/fileapi/file_system_context.cc +++ b/webkit/browser/fileapi/file_system_context.cc @@ -277,6 +277,14 @@ FileSystemBackend* FileSystemContext::GetFileSystemBackend( return NULL; } +WatcherManager* FileSystemContext::GetWatcherManager( + FileSystemType type) const { + FileSystemBackend* backend = GetFileSystemBackend(type); + if (!backend) + return NULL; + return backend->GetWatcherManager(type); +} + bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const { FileSystemBackendMap::const_iterator found = backend_map_.find(type); return found != backend_map_.end() && found->second->GetQuotaUtil(); diff --git a/webkit/browser/fileapi/file_system_context.h b/webkit/browser/fileapi/file_system_context.h index f8b9b52..d9c1c45 100644 --- a/webkit/browser/fileapi/file_system_context.h +++ b/webkit/browser/fileapi/file_system_context.h @@ -65,6 +65,7 @@ class IsolatedFileSystemBackend; class MountPoints; class QuotaReservation; class SandboxFileSystemBackend; +class WatchManager; struct DefaultContextDeleter; struct FileSystemInfo; @@ -163,6 +164,10 @@ class STORAGE_EXPORT FileSystemContext FileSystemBackend* GetFileSystemBackend( FileSystemType type) const; + // Returns the watcher manager for the given |type|. + // This may return NULL if the type does not support watching. + WatcherManager* GetWatcherManager(FileSystemType type) const; + // Returns true for sandboxed filesystems. Currently this does // the same as GetQuotaUtil(type) != NULL. (In an assumption that // all sandboxed filesystems must cooperate with QuotaManager so that diff --git a/webkit/browser/fileapi/isolated_file_system_backend.cc b/webkit/browser/fileapi/isolated_file_system_backend.cc index b2aee2b..1b5f346 100644 --- a/webkit/browser/fileapi/isolated_file_system_backend.cc +++ b/webkit/browser/fileapi/isolated_file_system_backend.cc @@ -23,6 +23,7 @@ #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/native_file_util.h" #include "webkit/browser/fileapi/transient_file_util.h" +#include "webkit/browser/fileapi/watcher_manager.h" #include "webkit/common/fileapi/file_system_types.h" #include "webkit/common/fileapi/file_system_util.h" @@ -84,6 +85,11 @@ AsyncFileUtil* IsolatedFileSystemBackend::GetAsyncFileUtil( return NULL; } +WatcherManager* IsolatedFileSystemBackend::GetWatcherManager( + FileSystemType type) { + return NULL; +} + CopyOrMoveFileValidatorFactory* IsolatedFileSystemBackend::GetCopyOrMoveFileValidatorFactory( FileSystemType type, base::File::Error* error_code) { diff --git a/webkit/browser/fileapi/isolated_file_system_backend.h b/webkit/browser/fileapi/isolated_file_system_backend.h index a64b0b7..b252845 100644 --- a/webkit/browser/fileapi/isolated_file_system_backend.h +++ b/webkit/browser/fileapi/isolated_file_system_backend.h @@ -24,6 +24,7 @@ class IsolatedFileSystemBackend : public FileSystemBackend { OpenFileSystemMode mode, const OpenFileSystemCallback& callback) OVERRIDE; virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE; + virtual WatcherManager* GetWatcherManager(FileSystemType type) OVERRIDE; virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/webkit/browser/fileapi/plugin_private_file_system_backend.cc b/webkit/browser/fileapi/plugin_private_file_system_backend.cc index 7beb1b7..0a3cbf9 100644 --- a/webkit/browser/fileapi/plugin_private_file_system_backend.cc +++ b/webkit/browser/fileapi/plugin_private_file_system_backend.cc @@ -160,6 +160,11 @@ PluginPrivateFileSystemBackend::GetAsyncFileUtil(FileSystemType type) { return file_util_.get(); } +WatcherManager* PluginPrivateFileSystemBackend::GetWatcherManager( + FileSystemType type) { + return NULL; +} + CopyOrMoveFileValidatorFactory* PluginPrivateFileSystemBackend::GetCopyOrMoveFileValidatorFactory( FileSystemType type, diff --git a/webkit/browser/fileapi/plugin_private_file_system_backend.h b/webkit/browser/fileapi/plugin_private_file_system_backend.h index 9ebc471..bd95bc9 100644 --- a/webkit/browser/fileapi/plugin_private_file_system_backend.h +++ b/webkit/browser/fileapi/plugin_private_file_system_backend.h @@ -29,6 +29,7 @@ class SpecialStoragePolicy; namespace storage { class ObfuscatedFileUtil; +class WatcherManager; class STORAGE_EXPORT PluginPrivateFileSystemBackend : public FileSystemBackend, @@ -65,6 +66,7 @@ class STORAGE_EXPORT PluginPrivateFileSystemBackend OpenFileSystemMode mode, const OpenFileSystemCallback& callback) OVERRIDE; virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE; + virtual WatcherManager* GetWatcherManager(FileSystemType type) OVERRIDE; virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/webkit/browser/fileapi/sandbox_file_system_backend.cc b/webkit/browser/fileapi/sandbox_file_system_backend.cc index a0ab15d..4aa9595 100644 --- a/webkit/browser/fileapi/sandbox_file_system_backend.cc +++ b/webkit/browser/fileapi/sandbox_file_system_backend.cc @@ -85,6 +85,11 @@ AsyncFileUtil* SandboxFileSystemBackend::GetAsyncFileUtil( return delegate_->file_util(); } +WatcherManager* SandboxFileSystemBackend::GetWatcherManager( + FileSystemType type) { + return NULL; +} + CopyOrMoveFileValidatorFactory* SandboxFileSystemBackend::GetCopyOrMoveFileValidatorFactory( FileSystemType type, diff --git a/webkit/browser/fileapi/sandbox_file_system_backend.h b/webkit/browser/fileapi/sandbox_file_system_backend.h index 98743d2..7a60d4f 100644 --- a/webkit/browser/fileapi/sandbox_file_system_backend.h +++ b/webkit/browser/fileapi/sandbox_file_system_backend.h @@ -38,6 +38,7 @@ class STORAGE_EXPORT SandboxFileSystemBackend OpenFileSystemMode mode, const OpenFileSystemCallback& callback) OVERRIDE; virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE; + virtual WatcherManager* GetWatcherManager(FileSystemType type) OVERRIDE; virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory( FileSystemType type, base::File::Error* error_code) OVERRIDE; diff --git a/webkit/browser/fileapi/watcher_manager.h b/webkit/browser/fileapi/watcher_manager.h new file mode 100644 index 0000000..83a64f6 --- /dev/null +++ b/webkit/browser/fileapi/watcher_manager.h @@ -0,0 +1,67 @@ +// 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 WEBKIT_BROWSER_FILEAPI_WATCHER_MANAGER_H_ +#define WEBKIT_BROWSER_FILEAPI_WATCHER_MANAGER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/files/file.h" + +namespace base { +class Time; +} + +namespace storage { + +class FileSystemOperationContext; +class FileSystemURL; + +// An interface for providing entry observing capability for file system +// backends. +// +// It is NOT valid to give null callback to this class, and implementors +// can assume that they don't get any null callbacks. +class WatcherManager { + public: + typedef base::Callback<void(base::File::Error result)> StatusCallback; + + // Observes watched entries. + class Observer { + public: + Observer() {} + virtual ~Observer() {} + + // Notifies about an entry represented by |url| being changed. + virtual void OnEntryChanged(const FileSystemURL& url) = 0; + + // Notifies about an entry represented by |url| being removed. + virtual void OnEntryRemoved(const FileSystemURL& url) = 0; + }; + + virtual ~WatcherManager() {} + + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + virtual bool HasObserver(Observer* observer) const = 0; + + // Observes a directory entry. If the |recursive| mode is not supported then + // FILE_ERROR_INVALID_OPERATION must be returned as an error. If the |url| is + // already watched, or setting up the watcher fails, then |callback| + // must be called with a specific error code. Otherwise |callback| must be + // called with the FILE_OK error code. + virtual void WatchDirectory(const FileSystemURL& url, + bool recursive, + const StatusCallback& callback) = 0; + + // Stops observing an entry represented by |url|. + virtual void UnwatchEntry(const FileSystemURL& url, + const StatusCallback& callback) = 0; +}; + +} // namespace storage + +#endif // WEBKIT_BROWSER_FILEAPI_WATCHER_MANAGER_H_ |
