Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6670)

Unified Diff: chrome/browser/file_system/entry_watcher_service.cc

Issue 452043003: [ew] Add basic classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments. Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..3d350d0eac38a923db22d96ea4ec22ef583cbcac
--- /dev/null
+++ b/chrome/browser/file_system/entry_watcher_service.cc
@@ -0,0 +1,301 @@
+// 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 extensions {
+namespace {
+
+// Default implementation for dispatching an event. Can be replaced for unit
+// tests by EntryWatcherService::SetDispatchEventImplForTesting().
+bool DispatchEventImpl(extensions::EventRouter* event_router,
+ const std::string& extension_id,
+ scoped_ptr<extensions::Event> event) {
+ if (!event_router->ExtensionHasEventListener(extension_id, event->event_name))
benwells 2014/08/13 05:50:47 Is it necessary to check this and return a bool?
mtomasz 2014/08/13 07:02:08 Right. Removed.
+ return false;
+
+ event_router->DispatchEventToExtension(extension_id, event.Pass());
+ return true;
+}
+
+// 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(
benwells 2014/08/13 05:50:47 Why do you post this and not just run it immediate
mtomasz 2014/08/13 07:02:09 Several times during reviews I was asked to post c
benwells 2014/08/14 05:16:32 OK, makes sense. Could you add a comment explainin
mtomasz 2014/08/15 05:35:57 Done.
+ FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_SECURITY));
benwells 2014/08/13 05:50:47 Why is this a security error?
mtomasz 2014/08/13 07:02:09 Changed to base::File::FILE_ERROR_INVALID_OPERATIO
+ 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_SECURITY));
+ 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;
+ 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 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;
+ 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(
+ 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;
+ }
+
+ DCHECK_EQ(base::File::FILE_OK, result);
benwells 2014/08/13 05:50:47 Is this DCHECK necessary?
mtomasz 2014/08/13 07:02:09 Removed.
+
+ 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_SECURITY);
benwells 2014/08/13 05:50:47 Given that you should have already successfully go
mtomasz 2014/08/13 07:02:09 GetWatcherManager() may return NULL at any point.
benwells 2014/08/14 05:16:32 That seems odd, are you thinking about the cases w
mtomasz 2014/08/15 05:35:56 I don't think it's weird. I can imagine a backend
mtomasz 2014/08/15 05:35:57 I reorganized the code. How about now?
benwells 2014/08/18 22:58:32 I think that is better, but the lifetime issues ar
mtomasz 2014/08/19 06:50:36 Done.
+ 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);
+
+ 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);
+
+ fileapi::FileSystemContext* const context =
+ get_file_system_context_impl_.Run(extension_id, context_);
+ DCHECK(context);
+
+ fileapi::WatcherManager* const watcher_manager =
benwells 2014/08/13 05:50:47 Why do you get the watcher manager here?
mtomasz 2014/08/13 07:02:09 Not needed. Removed.
+ context->GetWatcherManager(url.type());
+ if (!watcher_manager) {
+ callback.Run(base::File::FILE_ERROR_SECURITY);
+ return;
+ }
+
+ // 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() {
+}
+
+// static
+EntryWatcherService* EntryWatcherServiceFactory::Get(
+ content::BrowserContext* context) {
+ return static_cast<EntryWatcherService*>(
+ GetInstance()->GetServiceForBrowserContext(context, true));
+}
+
+// static
+EntryWatcherService* EntryWatcherServiceFactory::FindExisting(
+ content::BrowserContext* context) {
+ return static_cast<EntryWatcherService*>(
+ GetInstance()->GetServiceForBrowserContext(context, false));
+}
+
+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 {
+ return true;
benwells 2014/08/13 05:50:47 Could you add a comment about why this is needed?
mtomasz 2014/08/13 07:02:09 Done.
+}
+
+} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698