Index: chrome/browser/file_system/entry_watcher_service.cc |
diff --git a/chrome/browser/file_system/entry_watcher_service.cc b/chrome/browser/file_system/entry_watcher_service.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c2e4aaa763043068df815f6e57741bf692e8deef |
--- /dev/null |
+++ b/chrome/browser/file_system/entry_watcher_service.cc |
@@ -0,0 +1,247 @@ |
+// 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/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/storage_partition.h" |
+#include "extensions/browser/event_router.h" |
+#include "webkit/browser/fileapi/file_system_context.h" |
+ |
+namespace { |
+ |
+// Default implementation for dispatching an event. Can be replaced for unit |
+// tests by EntryWatcherService::SetDispatchEventImplForTesting(). |
+void DispatchEventImpl(extensions::EventRouter* event_router, |
+ const std::string& extension_id, |
+ scoped_ptr<extensions::Event> event) { |
+ event_router->DispatchEventToExtension(extension_id, event.Pass()); |
+} |
+ |
+// Default implementation for acquiring a file system context for a specific |
+// |extension_id| and |context|. |
+fileapi::FileSystemContext* GetFileSystemContextImpl( |
+ const std::string& extension_id, |
+ content::BrowserContext* context) { |
+ const GURL site = |
+ extensions::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, |
+ extensions::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 fileapi::FileSystemURL& url, |
+ bool recursive, |
+ const fileapi::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; |
+ } |
+ |
+ fileapi::FileSystemContext* const context = |
+ get_file_system_context_impl_.Run(extension_id, context_); |
+ DCHECK(context); |
+ |
+ fileapi::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; |
+ } |
+ |
+ watcher_manager->WatchDirectory( |
+ url, |
+ recursive, |
+ base::Bind(&EntryWatcherService::OnWatchDirectoryCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ extension_id, |
+ url, |
+ recursive, |
+ callback)); |
+} |
+ |
+void EntryWatcherService::UnwatchEntry( |
+ const std::string& extension_id, |
+ const fileapi::FileSystemURL& url, |
+ const fileapi::WatcherManager::StatusCallback& callback) { |
+ fileapi::FileSystemContext* const context = |
+ get_file_system_context_impl_.Run(extension_id, context_); |
+ DCHECK(context); |
+ |
+ fileapi::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; |
+ } |
+ |
+ watcher_manager->UnwatchEntry( |
+ url, |
+ base::Bind(&EntryWatcherService::OnUnwatchEntryCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ extension_id, |
+ url, |
+ callback)); |
+} |
+ |
+std::vector<fileapi::FileSystemURL> EntryWatcherService::GetWatchedEntries( |
+ const std::string& extension_id) { |
+ std::vector<fileapi::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 fileapi::FileSystemURL& url) { |
+ const WatcherMap::const_iterator it = watchers_.find(url.ToGURL()); |
+ 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; |
+ extensions::api::file_system::EntryChangedEvent event; |
+ dispatch_event_impl_.Run( |
+ extension_id, |
+ make_scoped_ptr(new extensions::Event( |
+ extensions::api::file_system::OnEntryChanged::kEventName, |
+ extensions::api::file_system::OnEntryChanged::Create(event)))); |
+ } |
+} |
+ |
+void EntryWatcherService::OnEntryRemoved(const fileapi::FileSystemURL& url) { |
+ WatcherMap::const_iterator it = watchers_.find(url.ToGURL()); |
+ 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; |
+ extensions::api::file_system::EntryRemovedEvent event; |
+ dispatch_event_impl_.Run( |
+ extension_id, |
+ make_scoped_ptr(new extensions::Event( |
+ extensions::api::file_system::OnEntryRemoved::kEventName, |
+ extensions::api::file_system::OnEntryRemoved::Create(event)))); |
+ } |
+} |
+ |
+void EntryWatcherService::OnWatchDirectoryCompleted( |
+ const std::string& extension_id, |
+ const fileapi::FileSystemURL& url, |
+ bool recursive, |
+ const fileapi::WatcherManager::StatusCallback& callback, |
+ base::File::Error result) { |
+ if (result != base::File::FILE_OK) { |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ fileapi::FileSystemContext* const context = |
+ get_file_system_context_impl_.Run(extension_id, context_); |
+ DCHECK(context); |
+ |
+ fileapi::WatcherManager* const watcher_manager = |
+ context->GetWatcherManager(url.type()); |
+ if (!watcher_manager) { |
+ callback.Run(base::File::FILE_ERROR_INVALID_OPERATION); |
+ return; |
+ } |
+ |
+ // Observe the manager if not observed yet. |
+ if (!observing_.IsObserving(watcher_manager)) |
+ observing_.Add(watcher_manager); |
+ |
+ const GURL gurl = url.ToGURL(); |
+ watchers_[gurl][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 fileapi::FileSystemURL& url, |
+ const fileapi::WatcherManager::StatusCallback& callback, |
+ base::File::Error result) { |
+ if (result != base::File::FILE_OK) { |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ DCHECK_EQ(base::File::FILE_OK, result); |
benwells
2014/08/14 05:16:32
This DCHECK can be removed as well.
mtomasz
2014/08/15 05:35:57
Done.
|
+ |
+ const GURL gurl = url.ToGURL(); |
+ if (watchers_[gurl].erase(extension_id) == 0) { |
+ callback.Run(base::File::FILE_ERROR_NOT_FOUND); |
+ return; |
+ } |
+ |
+ if (watchers_[gurl].empty()) |
+ watchers_.erase(gurl); |
+ |
+ // TODO(mtomasz): Save in preferences. |
+ |
+ callback.Run(base::File::FILE_OK); |
+} |
+ |
+EntryWatcherService::EntryWatcher::EntryWatcher() |
+ : directory(false), recursive(false) { |
+} |
+ |
+EntryWatcherService::EntryWatcher::EntryWatcher( |
+ const fileapi::FileSystemURL& url, |
+ bool directory, |
+ bool recursive) |
+ : url(url), directory(directory), recursive(recursive) { |
+} |
+ |
+EntryWatcherService::EntryWatcher::~EntryWatcher() { |
+} |