summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.cc6
-rw-r--r--chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h7
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.cc6
-rw-r--r--chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h8
-rw-r--r--chrome/browser/chromeos/fileapi/file_system_backend.cc5
-rw-r--r--chrome/browser/chromeos/fileapi/file_system_backend.h3
-rw-r--r--chrome/browser/chromeos/fileapi/file_system_backend_delegate.h9
-rw-r--r--chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc6
-rw-r--r--chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h11
-rw-r--r--chrome/browser/extensions/api/file_system/OWNERS1
-rw-r--r--chrome/browser/extensions/api/file_system/entry_watcher_service.cc253
-rw-r--r--chrome/browser/extensions/api/file_system/entry_watcher_service.h128
-rw-r--r--chrome/browser/extensions/api/file_system/entry_watcher_service_factory.cc37
-rw-r--r--chrome/browser/extensions/api/file_system/entry_watcher_service_factory.h49
-rw-r--r--chrome/browser/extensions/api/file_system/entry_watcher_service_unittest.cc254
-rw-r--r--chrome/browser/extensions/api/file_system/file_system_api.cc2
-rw-r--r--chrome/browser/extensions/api/file_system/file_system_api.h1
-rw-r--r--chrome/browser/local_discovery/storage/privet_filesystem_backend.cc5
-rw-r--r--chrome/browser/local_discovery/storage/privet_filesystem_backend.h2
-rw-r--r--chrome/browser/media_galleries/fileapi/media_file_system_backend.cc6
-rw-r--r--chrome/browser/media_galleries/fileapi/media_file_system_backend.h2
-rw-r--r--chrome/browser/sync_file_system/local/sync_file_system_backend.cc7
-rw-r--r--chrome/browser/sync_file_system/local/sync_file_system_backend.h2
-rw-r--r--chrome/chrome_browser_extensions.gypi4
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--content/public/test/test_file_system_backend.cc107
-rw-r--r--content/public/test/test_file_system_backend.h3
-rw-r--r--webkit/browser/fileapi/file_system_backend.h6
-rw-r--r--webkit/browser/fileapi/file_system_context.cc8
-rw-r--r--webkit/browser/fileapi/file_system_context.h5
-rw-r--r--webkit/browser/fileapi/isolated_file_system_backend.cc6
-rw-r--r--webkit/browser/fileapi/isolated_file_system_backend.h1
-rw-r--r--webkit/browser/fileapi/plugin_private_file_system_backend.cc5
-rw-r--r--webkit/browser/fileapi/plugin_private_file_system_backend.h2
-rw-r--r--webkit/browser/fileapi/sandbox_file_system_backend.cc5
-rw-r--r--webkit/browser/fileapi/sandbox_file_system_backend.h1
-rw-r--r--webkit/browser/fileapi/watcher_manager.h67
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_