Commit b524db20 authored by mtomasz's avatar mtomasz Committed by Commit bot

[ew] Simplify the entry watcher logic.

The responsibility of EntryWatcherService has been moved to WatcherManager
implementations.

Note, that the original code was unnecesarily complicated. After replacing
the observer interface with a simple callback, there is no need for an extra
service managing watchers anymore.

This was possible because with the new design entry watchers will no longer
survive reboots.

TEST=Compiles.
BUG=261491

Review URL: https://codereview.chromium.org/642343004

Cr-Commit-Position: refs/heads/master@{#301319}
parent fba4353d
// 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 "storage/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
// 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 "storage/browser/fileapi/file_system_url.h"
#include "storage/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);
~EntryWatcherService() override;
// 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.
void OnEntryChanged(const storage::FileSystemURL& url) override;
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_
// 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
// 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();
~EntryWatcherServiceFactory() override;
// BrowserContextKeyedServiceFactory overrides:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* profile) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
DISALLOW_COPY_AND_ASSIGN(EntryWatcherServiceFactory);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_FILE_SYSTEM_ENTRY_WATCHER_SERVICE_FACTORY_H_
// 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 "storage/browser/fileapi/file_system_url.h"
#include "storage/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
...@@ -231,10 +231,6 @@ ...@@ -231,10 +231,6 @@
'browser/extensions/api/file_handlers/app_file_handler_util.h', '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.cc',
'browser/extensions/api/file_handlers/mime_util.h', '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.cc',
'browser/extensions/api/file_system/file_system_api.h', 'browser/extensions/api/file_system/file_system_api.h',
'browser/extensions/api/font_settings/font_settings_api.cc', 'browser/extensions/api/font_settings/font_settings_api.cc',
......
...@@ -329,7 +329,6 @@ ...@@ -329,7 +329,6 @@
'browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc', '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/extension_action/extension_action_prefs_unittest.cc',
'browser/extensions/api/file_handlers/mime_util_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/file_system/file_system_api_unittest.cc',
'browser/extensions/api/identity/extension_token_key_unittest.cc', 'browser/extensions/api/identity/extension_token_key_unittest.cc',
'browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc', 'browser/extensions/api/identity/gaia_web_auth_flow_unittest.cc',
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include "storage/browser/fileapi/native_file_util.h" #include "storage/browser/fileapi/native_file_util.h"
#include "storage/browser/fileapi/quota/quota_reservation.h" #include "storage/browser/fileapi/quota/quota_reservation.h"
#include "storage/browser/fileapi/sandbox_file_stream_writer.h" #include "storage/browser/fileapi/sandbox_file_stream_writer.h"
#include "storage/browser/fileapi/watcher_manager.h"
#include "storage/browser/quota/quota_manager.h" #include "storage/browser/quota/quota_manager.h"
#include "storage/common/fileapi/file_system_util.h" #include "storage/common/fileapi/file_system_util.h"
...@@ -56,101 +55,6 @@ class TestFileUtil : public storage::LocalFileUtil { ...@@ -56,101 +55,6 @@ class TestFileUtil : public storage::LocalFileUtil {
base::FilePath base_path_; 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) {}
~TestWatcherManager() override {}
// storage::WatcherManager overrides.
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
bool HasObserver(Observer* observer) const override {
return observers_.HasObserver(observer);
}
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));
}
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 } // namespace
// This only supports single origin. // This only supports single origin.
...@@ -214,7 +118,6 @@ TestFileSystemBackend::TestFileSystemBackend( ...@@ -214,7 +118,6 @@ TestFileSystemBackend::TestFileSystemBackend(
task_runner_(task_runner), task_runner_(task_runner),
file_util_( file_util_(
new storage::AsyncFileUtilAdapter(new TestFileUtil(base_path))), new storage::AsyncFileUtilAdapter(new TestFileUtil(base_path))),
watcher_manager_(new TestWatcherManager()),
quota_util_(new QuotaUtil), quota_util_(new QuotaUtil),
require_copy_or_move_validator_(false) { require_copy_or_move_validator_(false) {
update_observers_ = update_observers_ =
...@@ -246,7 +149,7 @@ storage::AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil( ...@@ -246,7 +149,7 @@ storage::AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil(
storage::WatcherManager* TestFileSystemBackend::GetWatcherManager( storage::WatcherManager* TestFileSystemBackend::GetWatcherManager(
storage::FileSystemType type) { storage::FileSystemType type) {
return watcher_manager_.get(); return nullptr;
} }
storage::CopyOrMoveFileValidatorFactory* storage::CopyOrMoveFileValidatorFactory*
...@@ -260,7 +163,7 @@ TestFileSystemBackend::GetCopyOrMoveFileValidatorFactory( ...@@ -260,7 +163,7 @@ TestFileSystemBackend::GetCopyOrMoveFileValidatorFactory(
*error_code = base::File::FILE_ERROR_SECURITY; *error_code = base::File::FILE_ERROR_SECURITY;
return copy_or_move_file_validator_factory_.get(); return copy_or_move_file_validator_factory_.get();
} }
return NULL; return nullptr;
} }
void TestFileSystemBackend::InitializeCopyOrMoveFileValidatorFactory( void TestFileSystemBackend::InitializeCopyOrMoveFileValidatorFactory(
...@@ -328,7 +231,7 @@ const storage::ChangeObserverList* TestFileSystemBackend::GetChangeObservers( ...@@ -328,7 +231,7 @@ const storage::ChangeObserverList* TestFileSystemBackend::GetChangeObservers(
const storage::AccessObserverList* TestFileSystemBackend::GetAccessObservers( const storage::AccessObserverList* TestFileSystemBackend::GetAccessObservers(
storage::FileSystemType type) const { storage::FileSystemType type) const {
return NULL; return nullptr;
} }
void TestFileSystemBackend::AddFileChangeObserver( void TestFileSystemBackend::AddFileChangeObserver(
......
...@@ -91,7 +91,6 @@ class TestFileSystemBackend : public storage::FileSystemBackend { ...@@ -91,7 +91,6 @@ class TestFileSystemBackend : public storage::FileSystemBackend {
base::FilePath base_path_; base::FilePath base_path_;
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
scoped_ptr<storage::AsyncFileUtilAdapter> file_util_; scoped_ptr<storage::AsyncFileUtilAdapter> file_util_;
scoped_ptr<storage::WatcherManager> watcher_manager_;
scoped_ptr<QuotaUtil> quota_util_; scoped_ptr<QuotaUtil> quota_util_;
storage::UpdateObserverList update_observers_; storage::UpdateObserverList update_observers_;
storage::ChangeObserverList change_observers_; storage::ChangeObserverList change_observers_;
......
...@@ -27,35 +27,24 @@ class FileSystemURL; ...@@ -27,35 +27,24 @@ class FileSystemURL;
// can assume that they don't get any null callbacks. // can assume that they don't get any null callbacks.
class WatcherManager { class WatcherManager {
public: public:
typedef base::Callback<void(base::File::Error result)> StatusCallback; enum Action { CHANGED, REMOVED };
// 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. typedef base::Callback<void(base::File::Error result)> StatusCallback;
virtual void OnEntryRemoved(const FileSystemURL& url) = 0; typedef base::Callback<void(Action action)> NotificationCallback;
};
virtual ~WatcherManager() {} 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 // 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 // FILE_ERROR_INVALID_OPERATION must be returned as an error. If the |url| is
// already watched, or setting up the watcher fails, then |callback| // already watched, or setting up the watcher fails, then |callback|
// must be called with a specific error code. Otherwise |callback| must be // must be called with a specific error code. Otherwise |callback| must be
// called with the FILE_OK error code. // called with the FILE_OK error code. |notification_callback| is called for
virtual void WatchDirectory(const FileSystemURL& url, // every change related to the watched directory.
bool recursive, virtual void WatchDirectory(
const StatusCallback& callback) = 0; const FileSystemURL& url,
bool recursive,
const StatusCallback& callback,
const NotificationCallback& notification_callback) = 0;
// Stops observing an entry represented by |url|. // Stops observing an entry represented by |url|.
virtual void UnwatchEntry(const FileSystemURL& url, virtual void UnwatchEntry(const FileSystemURL& url,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment