diff options
author | cmumford <cmumford@chromium.org> | 2016-03-24 13:35:27 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-24 20:36:29 +0000 |
commit | 6ae8d465f961bb3c6fa9ed285d877710fb69ae12 (patch) | |
tree | 2ef3a31a1254b3d2bc9fa84f942f5a4be023ab4a /extensions/browser/value_store | |
parent | 7fad54c907e98f5c574cbf12420ab072eecf81ad (diff) | |
download | chromium_src-6ae8d465f961bb3c6fa9ed285d877710fb69ae12.zip chromium_src-6ae8d465f961bb3c6fa9ed285d877710fb69ae12.tar.gz chromium_src-6ae8d465f961bb3c6fa9ed285d877710fb69ae12.tar.bz2 |
Extensions: Using common ValueStoreFactory for all value stores.
1. Introduce new ValueStoreFactory interface used for the creation of
ValueStore's in all namespaces (local, sync, and managed), and for all
types (extension and application).
2. Delete SettingsStorageFactory/LeveldbSettingsStorageFactory,
and switched to ValueStoreFactory.
3. Created a new TestValueStoreFactory (for testing). This combines
settings_sync_unittest.cc:TestingValueStoreFactory and
ScopedSettingsStorageFactory.
4. ValueStoreFrontend::Backend always lazilily initializes using the
ValueStoreFactory. This makes unnecessary StateStore's deferred
initialization mechanism - which will be removed in an upcoming CL.
5. A new ValueStoreFactoryImpl to mint new ValueStore's for Chrome.
This currently delegates to a new LegacyValueStoreFactory which
creates new LeveldbValueStore. An upcoming CL will add a second
delegated factory (currently called ProfileValueStoreFactory) to
support a unified (per-profile) extensions database.
6. Removed memcheck suppression for SettingsStorageFactory as this
class is now deleted (crbug.com/163922).
BUG=453946,163922
Review URL: https://codereview.chromium.org/1803193002
Cr-Commit-Position: refs/heads/master@{#383137}
Diffstat (limited to 'extensions/browser/value_store')
10 files changed, 887 insertions, 54 deletions
diff --git a/extensions/browser/value_store/legacy_value_store_factory.cc b/extensions/browser/value_store/legacy_value_store_factory.cc new file mode 100644 index 0000000..bafb93e --- /dev/null +++ b/extensions/browser/value_store/legacy_value_store_factory.cc @@ -0,0 +1,264 @@ +// Copyright 2016 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 "extensions/browser/value_store/legacy_value_store_factory.h" + +#include <string> + +#include "base/files/file_enumerator.h" +#include "base/files/file_util.h" +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/value_store/leveldb_value_store.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" + +using base::AutoLock; +using content::BrowserThread; + +namespace { + +// Statistics are logged to UMA with these strings as part of histogram name. +// They can all be found under Extensions.Database.Open.<client>. Changing this +// needs to synchronize with histograms.xml, AND will also become incompatible +// with older browsers still reporting the previous values. +const char kSettingsDatabaseUMAClientName[] = "Settings"; +const char kRulesDatabaseUMAClientName[] = "Rules"; +const char kStateDatabaseUMAClientName[] = "State"; + +bool ValidDBExists(const base::FilePath& path) { + // TODO(cmumford): Enhance to detect if dir contains valid database. + return base::DirectoryExists(path); +} + +} // namespace + +namespace extensions { + +// +// ModelSettings +// +LegacyValueStoreFactory::ModelSettings::ModelSettings( + const base::FilePath& data_path) + : data_path_(data_path) {} + +base::FilePath LegacyValueStoreFactory::ModelSettings::GetDBPath( + const ExtensionId& extension_id) const { + return data_path_.AppendASCII(extension_id); +} + +bool LegacyValueStoreFactory::ModelSettings::DeleteData( + const ExtensionId& extension_id) { + return base::DeleteFile(GetDBPath(extension_id), true /* recursive */); +} + +bool LegacyValueStoreFactory::ModelSettings::DataExists( + const ExtensionId& extension_id) const { + return ValidDBExists(GetDBPath(extension_id)); +} + +std::set<ExtensionId> +LegacyValueStoreFactory::ModelSettings::GetKnownExtensionIDs() const { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + std::set<ExtensionId> result; + + // Leveldb databases are directories inside |base_path_|. + base::FileEnumerator extension_dirs(data_path_, false, + base::FileEnumerator::DIRECTORIES); + while (!extension_dirs.Next().empty()) { + base::FilePath extension_dir = extension_dirs.GetInfo().GetName(); + DCHECK(!extension_dir.IsAbsolute()); + // Extension ID's are 'a'..'p', so any directory within this folder will + // either be ASCII, or created by some other application and safe to ignore. + std::string maybe_as_ascii(extension_dir.MaybeAsASCII()); + if (!maybe_as_ascii.empty()) { + result.insert(maybe_as_ascii); + } + } + + return result; +} + +// +// SettingsRoot +// + +LegacyValueStoreFactory::SettingsRoot::SettingsRoot( + const base::FilePath& base_path, + const std::string& extension_dirname, + const std::string& app_dirname) { + if (!extension_dirname.empty()) + extensions_.reset( + new ModelSettings(base_path.AppendASCII(extension_dirname))); + + if (!app_dirname.empty()) + apps_.reset(new ModelSettings(base_path.AppendASCII(app_dirname))); +} + +LegacyValueStoreFactory::SettingsRoot::~SettingsRoot() = default; + +const LegacyValueStoreFactory::ModelSettings* +LegacyValueStoreFactory::SettingsRoot::GetModel(ModelType model_type) const { + switch (model_type) { + case ValueStoreFactory::ModelType::APP: + DCHECK(apps_ != nullptr); + return apps_.get(); + case ValueStoreFactory::ModelType::EXTENSION: + DCHECK(extensions_ != nullptr); + return extensions_.get(); + } + NOTREACHED(); + return nullptr; +} + +LegacyValueStoreFactory::ModelSettings* +LegacyValueStoreFactory::SettingsRoot::GetModel(ModelType model_type) { + switch (model_type) { + case ValueStoreFactory::ModelType::APP: + DCHECK(apps_ != nullptr); + return apps_.get(); + case ValueStoreFactory::ModelType::EXTENSION: + DCHECK(extensions_ != nullptr); + return extensions_.get(); + } + NOTREACHED(); + return nullptr; +} + +std::set<ExtensionId> +LegacyValueStoreFactory::SettingsRoot::GetKnownExtensionIDs( + ModelType model_type) const { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + switch (model_type) { + case ValueStoreFactory::ModelType::APP: + DCHECK(apps_ != nullptr); + return apps_->GetKnownExtensionIDs(); + case ValueStoreFactory::ModelType::EXTENSION: + DCHECK(extensions_ != nullptr); + return extensions_->GetKnownExtensionIDs(); + } + NOTREACHED(); + return std::set<ExtensionId>(); +} + +// +// LegacyValueStoreFactory +// + +LegacyValueStoreFactory::LegacyValueStoreFactory( + const base::FilePath& profile_path) + : profile_path_(profile_path), + local_settings_(profile_path, + kLocalExtensionSettingsDirectoryName, + kLocalAppSettingsDirectoryName), + sync_settings_(profile_path, + kSyncExtensionSettingsDirectoryName, + kSyncAppSettingsDirectoryName), + // Currently no such thing as a managed app - only an extension. + managed_settings_(profile_path, kManagedSettingsDirectoryName, "") {} + +LegacyValueStoreFactory::~LegacyValueStoreFactory() = default; + +bool LegacyValueStoreFactory::RulesDBExists() const { + return ValidDBExists(GetRulesDBPath()); +} + +bool LegacyValueStoreFactory::StateDBExists() const { + return ValidDBExists(GetStateDBPath()); +} + +scoped_ptr<ValueStore> LegacyValueStoreFactory::CreateRulesStore() { + return make_scoped_ptr( + new LeveldbValueStore(kRulesDatabaseUMAClientName, GetRulesDBPath())); +} + +scoped_ptr<ValueStore> LegacyValueStoreFactory::CreateStateStore() { + return make_scoped_ptr( + new LeveldbValueStore(kStateDatabaseUMAClientName, GetStateDBPath())); +} + +scoped_ptr<ValueStore> LegacyValueStoreFactory::CreateSettingsStore( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + const ModelSettings* settings_root = + GetSettingsRoot(settings_namespace).GetModel(model_type); + DCHECK(settings_root != nullptr); + return make_scoped_ptr(new LeveldbValueStore( + kSettingsDatabaseUMAClientName, settings_root->GetDBPath(extension_id))); +} + +void LegacyValueStoreFactory::DeleteSettings( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + // TODO(cmumford): Verify that we always need to be called on FILE thread. + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + ModelSettings* model_settings = + GetSettingsRoot(settings_namespace).GetModel(model_type); + if (model_settings == nullptr) { + NOTREACHED(); + return; + } + model_settings->DeleteData(extension_id); +} + +bool LegacyValueStoreFactory::HasSettings( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + const ModelSettings* model_settings = + GetSettingsRoot(settings_namespace).GetModel(model_type); + if (model_settings == nullptr) + return false; + return model_settings->DataExists(extension_id); +} + +std::set<ExtensionId> LegacyValueStoreFactory::GetKnownExtensionIDs( + settings_namespace::Namespace settings_type, + ModelType model_type) const { + return GetSettingsRoot(settings_type).GetKnownExtensionIDs(model_type); +} + +const LegacyValueStoreFactory::SettingsRoot& +LegacyValueStoreFactory::GetSettingsRoot( + settings_namespace::Namespace settings_namespace) const { + switch (settings_namespace) { + case settings_namespace::LOCAL: + return local_settings_; + case settings_namespace::SYNC: + return sync_settings_; + case settings_namespace::MANAGED: + return managed_settings_; + case settings_namespace::INVALID: + break; + } + NOTREACHED(); + return local_settings_; +} + +LegacyValueStoreFactory::SettingsRoot& LegacyValueStoreFactory::GetSettingsRoot( + settings_namespace::Namespace settings_namespace) { + switch (settings_namespace) { + case settings_namespace::LOCAL: + return local_settings_; + case settings_namespace::SYNC: + return sync_settings_; + case settings_namespace::MANAGED: + return managed_settings_; + case settings_namespace::INVALID: + break; + } + NOTREACHED(); + return local_settings_; +} + +base::FilePath LegacyValueStoreFactory::GetRulesDBPath() const { + return profile_path_.AppendASCII(kRulesStoreName); +} + +base::FilePath LegacyValueStoreFactory::GetStateDBPath() const { + return profile_path_.AppendASCII(kStateStoreName); +} + +} // namespace extensions diff --git a/extensions/browser/value_store/legacy_value_store_factory.h b/extensions/browser/value_store/legacy_value_store_factory.h new file mode 100644 index 0000000..65db027 --- /dev/null +++ b/extensions/browser/value_store/legacy_value_store_factory.h @@ -0,0 +1,111 @@ +// Copyright 2016 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 EXTENSIONS_BROWSER_VALUE_STORE_LEGACY_VALUE_STORE_FACTORY_H_ +#define EXTENSIONS_BROWSER_VALUE_STORE_LEGACY_VALUE_STORE_FACTORY_H_ + +#include <set> +#include <string> + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/value_store/value_store.h" +#include "extensions/browser/value_store/value_store_factory.h" +#include "extensions/common/extension.h" + +namespace extensions { + +// A factory to create legacy ValueStore instances for storing extension +// state/rules/settings. "legacy" refers to the initial storage implementation +// which created a settings database per extension. +class LegacyValueStoreFactory : public ValueStoreFactory { + public: + explicit LegacyValueStoreFactory(const base::FilePath& profile_path); + + bool RulesDBExists() const; + bool StateDBExists() const; + + // ValueStoreFactory: + scoped_ptr<ValueStore> CreateRulesStore() override; + scoped_ptr<ValueStore> CreateStateStore() override; + scoped_ptr<ValueStore> CreateSettingsStore( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + + void DeleteSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + bool HasSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + std::set<ExtensionId> GetKnownExtensionIDs( + settings_namespace::Namespace settings_namespace, + ModelType model_type) const override; + + private: + friend class base::RefCounted<LegacyValueStoreFactory>; + + // Manages a collection of legacy settings databases all within a common + // directory. + class ModelSettings { + public: + explicit ModelSettings(const base::FilePath& data_path); + + base::FilePath GetDBPath(const ExtensionId& extension_id) const; + bool DeleteData(const ExtensionId& extension_id); + bool DataExists(const ExtensionId& extension_id) const; + std::set<ExtensionId> GetKnownExtensionIDs() const; + + private: + // The path containing all settings databases under this root. + const base::FilePath data_path_; + + DISALLOW_COPY_AND_ASSIGN(ModelSettings); + }; + + // Manages two collections of legacy settings databases (apps & extensions) + // within a common base directory. + class SettingsRoot { + public: + // If either |extension_dirname| or |app_dirname| are empty then that + // ModelSetting will *not* be created. + SettingsRoot(const base::FilePath& base_path, + const std::string& extension_dirname, + const std::string& app_dirname); + ~SettingsRoot(); + + std::set<ExtensionId> GetKnownExtensionIDs(ModelType model_type) const; + const ModelSettings* GetModel(ModelType model_type) const; + ModelSettings* GetModel(ModelType model_type); + + private: + scoped_ptr<ModelSettings> extensions_; + scoped_ptr<ModelSettings> apps_; + + DISALLOW_COPY_AND_ASSIGN(SettingsRoot); + }; + + ~LegacyValueStoreFactory() override; + + const SettingsRoot& GetSettingsRoot( + settings_namespace::Namespace settings_namespace) const; + SettingsRoot& GetSettingsRoot( + settings_namespace::Namespace settings_namespace); + + base::FilePath GetRulesDBPath() const; + base::FilePath GetStateDBPath() const; + + const base::FilePath profile_path_; + SettingsRoot local_settings_; + SettingsRoot sync_settings_; + SettingsRoot managed_settings_; + + DISALLOW_COPY_AND_ASSIGN(LegacyValueStoreFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_VALUE_STORE_LEGACY_VALUE_STORE_FACTORY_H_ diff --git a/extensions/browser/value_store/test_value_store_factory.cc b/extensions/browser/value_store/test_value_store_factory.cc new file mode 100644 index 0000000..f5135df --- /dev/null +++ b/extensions/browser/value_store/test_value_store_factory.cc @@ -0,0 +1,190 @@ +// Copyright 2016 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 "extensions/browser/value_store/test_value_store_factory.h" + +#include "extensions/browser/value_store/leveldb_value_store.h" +#include "extensions/browser/value_store/testing_value_store.h" + +namespace { + +const char kUMAClientName[] = "Test"; + +} // namespace + +namespace extensions { + +using SettingsNamespace = settings_namespace::Namespace; + +TestValueStoreFactory::StorageHelper::StorageHelper() = default; + +TestValueStoreFactory::StorageHelper::~StorageHelper() = default; + +std::set<ExtensionId> +TestValueStoreFactory::StorageHelper::GetKnownExtensionIDs( + ModelType model_type) const { + std::set<ExtensionId> ids; + switch (model_type) { + case ValueStoreFactory::ModelType::APP: + for (const auto& key : app_stores_) + ids.insert(key.first); + break; + case ValueStoreFactory::ModelType::EXTENSION: + for (const auto& key : extension_stores_) + ids.insert(key.first); + break; + } + return ids; +} + +void TestValueStoreFactory::StorageHelper::Reset() { + app_stores_.clear(); + extension_stores_.clear(); +} + +ValueStore* TestValueStoreFactory::StorageHelper::AddValueStore( + const ExtensionId& extension_id, + ValueStore* value_store, + ModelType model_type) { + if (model_type == ValueStoreFactory::ModelType::APP) { + DCHECK(app_stores_.find(extension_id) == app_stores_.end()); + app_stores_[extension_id] = value_store; + } else { + DCHECK(extension_stores_.find(extension_id) == extension_stores_.end()); + extension_stores_[extension_id] = value_store; + } + return value_store; +} + +void TestValueStoreFactory::StorageHelper::DeleteSettings( + const ExtensionId& extension_id, + ModelType model_type) { + switch (model_type) { + case ValueStoreFactory::ModelType::APP: + app_stores_.erase(extension_id); + break; + case ValueStoreFactory::ModelType::EXTENSION: + extension_stores_.erase(extension_id); + break; + } +} + +bool TestValueStoreFactory::StorageHelper::HasSettings( + const ExtensionId& extension_id, + ModelType model_type) const { + switch (model_type) { + case ValueStoreFactory::ModelType::APP: + return app_stores_.find(extension_id) != app_stores_.end(); + case ValueStoreFactory::ModelType::EXTENSION: + return extension_stores_.find(extension_id) != extension_stores_.end(); + } + NOTREACHED(); + return false; +} + +ValueStore* TestValueStoreFactory::StorageHelper::GetExisting( + const ExtensionId& extension_id) const { + auto it = app_stores_.find(extension_id); + if (it != app_stores_.end()) + return it->second; + it = extension_stores_.find(extension_id); + if (it != extension_stores_.end()) + return it->second; + return nullptr; +} + +TestValueStoreFactory::TestValueStoreFactory() = default; + +TestValueStoreFactory::TestValueStoreFactory(const base::FilePath& db_path) + : db_path_(db_path) {} + +TestValueStoreFactory::~TestValueStoreFactory() {} + +scoped_ptr<ValueStore> TestValueStoreFactory::CreateRulesStore() { + if (db_path_.empty()) + last_created_store_ = new TestingValueStore(); + else + last_created_store_ = new LeveldbValueStore(kUMAClientName, db_path_); + return make_scoped_ptr(last_created_store_); +} + +scoped_ptr<ValueStore> TestValueStoreFactory::CreateStateStore() { + return CreateRulesStore(); +} + +TestValueStoreFactory::StorageHelper& TestValueStoreFactory::GetStorageHelper( + SettingsNamespace settings_namespace) { + switch (settings_namespace) { + case settings_namespace::LOCAL: + return local_helper_; + case settings_namespace::SYNC: + return sync_helper_; + case settings_namespace::MANAGED: + return managed_helper_; + case settings_namespace::INVALID: + break; + } + NOTREACHED(); + return local_helper_; +} + +scoped_ptr<ValueStore> TestValueStoreFactory::CreateSettingsStore( + SettingsNamespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + scoped_ptr<ValueStore> settings_store(CreateRulesStore()); + // Note: This factory is purposely keeping the raw pointers to each ValueStore + // created. Tests using TestValueStoreFactory must be careful to keep + // those ValueStore's alive for the duration of their test. + GetStorageHelper(settings_namespace) + .AddValueStore(extension_id, settings_store.get(), model_type); + return settings_store; +} + +ValueStore* TestValueStoreFactory::LastCreatedStore() const { + return last_created_store_; +} + +void TestValueStoreFactory::DeleteSettings(SettingsNamespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + GetStorageHelper(settings_namespace).DeleteSettings(extension_id, model_type); +} + +bool TestValueStoreFactory::HasSettings(SettingsNamespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + return GetStorageHelper(settings_namespace) + .HasSettings(extension_id, model_type); +} + +std::set<ExtensionId> TestValueStoreFactory::GetKnownExtensionIDs( + SettingsNamespace settings_namespace, + ModelType model_type) const { + return const_cast<TestValueStoreFactory*>(this) + ->GetStorageHelper(settings_namespace) + .GetKnownExtensionIDs(model_type); +} + +ValueStore* TestValueStoreFactory::GetExisting( + const ExtensionId& extension_id) const { + ValueStore* existing_store = local_helper_.GetExisting(extension_id); + if (existing_store) + return existing_store; + existing_store = sync_helper_.GetExisting(extension_id); + if (existing_store) + return existing_store; + existing_store = managed_helper_.GetExisting(extension_id); + DCHECK(existing_store != nullptr); + return existing_store; +} + +void TestValueStoreFactory::Reset() { + last_created_store_ = nullptr; + local_helper_.Reset(); + sync_helper_.Reset(); + managed_helper_.Reset(); +} + +} // namespace extensions diff --git a/extensions/browser/value_store/test_value_store_factory.h b/extensions/browser/value_store/test_value_store_factory.h new file mode 100644 index 0000000..86fbee77 --- /dev/null +++ b/extensions/browser/value_store/test_value_store_factory.h @@ -0,0 +1,95 @@ +// Copyright 2016 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 EXTENSIONS_BROWSER_VALUE_STORE_TEST_VALUE_STORE_FACTORY_H_ +#define EXTENSIONS_BROWSER_VALUE_STORE_TEST_VALUE_STORE_FACTORY_H_ + +#include <map> +#include <set> + +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/value_store/value_store_factory.h" +#include "extensions/common/extension.h" + +class ValueStore; + +namespace extensions { + +// Used for tests when a new test ValueStore is required. Will either open a +// database on disk (if path provided) returning a |LeveldbValueStore|. +// Otherwise a new |TestingValueStore| instance will be returned. +class TestValueStoreFactory : public ValueStoreFactory { + public: + TestValueStoreFactory(); + explicit TestValueStoreFactory(const base::FilePath& db_path); + + // ValueStoreFactory + scoped_ptr<ValueStore> CreateRulesStore() override; + scoped_ptr<ValueStore> CreateStateStore() override; + scoped_ptr<ValueStore> CreateSettingsStore( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + void DeleteSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + bool HasSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + std::set<ExtensionId> GetKnownExtensionIDs( + settings_namespace::Namespace settings_namespace, + ModelType model_type) const override; + + // Return the last created |ValueStore|. Use with caution as this may return + // a dangling pointer since the creator now owns the ValueStore which can be + // deleted at any time. + ValueStore* LastCreatedStore() const; + // Return a previously created |ValueStore| for an extension. + ValueStore* GetExisting(const ExtensionId& extension_id) const; + // Reset this class (as if just created). + void Reset(); + + private: + // Manages a collection of |ValueStore|'s created for an app/extension. + // One of these exists for each setting type. + class StorageHelper { + public: + StorageHelper(); + ~StorageHelper(); + std::set<ExtensionId> GetKnownExtensionIDs(ModelType model_type) const; + ValueStore* AddValueStore(const ExtensionId& extension_id, + ValueStore* value_store, + ModelType model_type); + void DeleteSettings(const ExtensionId& extension_id, ModelType model_type); + bool HasSettings(const ExtensionId& extension_id, + ModelType model_type) const; + void Reset(); + ValueStore* GetExisting(const ExtensionId& extension_id) const; + + private: + std::map<ExtensionId, ValueStore*> app_stores_; + std::map<ExtensionId, ValueStore*> extension_stores_; + + DISALLOW_COPY_AND_ASSIGN(StorageHelper); + }; + + StorageHelper& GetStorageHelper( + settings_namespace::Namespace settings_namespace); + + ~TestValueStoreFactory() override; + base::FilePath db_path_; + ValueStore* last_created_store_ = nullptr; + + // None of these value stores are owned by this factory, so care must be + // taken when calling GetExisting. + StorageHelper local_helper_; + StorageHelper sync_helper_; + StorageHelper managed_helper_; + + DISALLOW_COPY_AND_ASSIGN(TestValueStoreFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_VALUE_STORE_TEST_VALUE_STORE_FACTORY_H_ diff --git a/extensions/browser/value_store/value_store_factory.h b/extensions/browser/value_store/value_store_factory.h new file mode 100644 index 0000000..ec43547 --- /dev/null +++ b/extensions/browser/value_store/value_store_factory.h @@ -0,0 +1,67 @@ +// Copyright 2016 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 EXTENSIONS_BROWSER_VALUE_STORE_VALUE_STORE_FACTORY_H_ +#define EXTENSIONS_BROWSER_VALUE_STORE_VALUE_STORE_FACTORY_H_ + +#include <set> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/api/storage/settings_namespace.h" +#include "extensions/common/extension.h" + +class ValueStore; + +namespace extensions { + +// Create new value stores for rules, state, or settings. For settings will +// also create stores for the specified namespace and model type. +// +// Note: This factory creates the lower level stores that directly read/write to +// disk. Sync/Managed stores are created directly, but delegate their +// calls to a |ValueStore| created by this interface. +class ValueStoreFactory : public base::RefCountedThreadSafe<ValueStoreFactory> { + public: + enum class ModelType { APP, EXTENSION }; + + // Create a |ValueStore| to contain rules data. + virtual scoped_ptr<ValueStore> CreateRulesStore() = 0; + + // Create a |ValueStore| to contain state data. + virtual scoped_ptr<ValueStore> CreateStateStore() = 0; + + // Create a |ValueStore| to contain settings data for a specific extension + // namespace and model type. + virtual scoped_ptr<ValueStore> CreateSettingsStore( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) = 0; + + // Delete all settings for specified given extension in the specified + // namespace/model_type. + virtual void DeleteSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) = 0; + + // Are there any settings stored in the specified namespace/model_type for + // the given extension? + virtual bool HasSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) = 0; + + // Return all extension ID's with settings stored in the given + // namespace/model_type. + virtual std::set<ExtensionId> GetKnownExtensionIDs( + settings_namespace::Namespace settings_namespace, + ModelType model_type) const = 0; + + protected: + friend class base::RefCountedThreadSafe<ValueStoreFactory>; + virtual ~ValueStoreFactory() {} +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_VALUE_STORE_VALUE_STORE_FACTORY_H_ diff --git a/extensions/browser/value_store/value_store_factory_impl.cc b/extensions/browser/value_store/value_store_factory_impl.cc new file mode 100644 index 0000000..27324fd --- /dev/null +++ b/extensions/browser/value_store/value_store_factory_impl.cc @@ -0,0 +1,53 @@ +// Copyright 2016 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 "extensions/browser/value_store/value_store_factory_impl.h" + +#include "extensions/browser/value_store/legacy_value_store_factory.h" + +namespace extensions { + +using SettingsNamespace = settings_namespace::Namespace; + +ValueStoreFactoryImpl::ValueStoreFactoryImpl(const base::FilePath& profile_path) + : legacy_factory_(new LegacyValueStoreFactory(profile_path)) {} + +ValueStoreFactoryImpl::~ValueStoreFactoryImpl() = default; + +scoped_ptr<ValueStore> ValueStoreFactoryImpl::CreateRulesStore() { + return legacy_factory_->CreateRulesStore(); +} + +scoped_ptr<ValueStore> ValueStoreFactoryImpl::CreateStateStore() { + return legacy_factory_->CreateStateStore(); +} + +scoped_ptr<ValueStore> ValueStoreFactoryImpl::CreateSettingsStore( + SettingsNamespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + return legacy_factory_->CreateSettingsStore(settings_namespace, model_type, + extension_id); +} + +void ValueStoreFactoryImpl::DeleteSettings(SettingsNamespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + legacy_factory_->DeleteSettings(settings_namespace, model_type, extension_id); +} + +bool ValueStoreFactoryImpl::HasSettings(SettingsNamespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) { + return legacy_factory_->HasSettings(settings_namespace, model_type, + extension_id); +} + +std::set<ExtensionId> ValueStoreFactoryImpl::GetKnownExtensionIDs( + SettingsNamespace settings_namespace, + ModelType model_type) const { + return legacy_factory_->GetKnownExtensionIDs(settings_namespace, model_type); +} + +} // namespace extensions diff --git a/extensions/browser/value_store/value_store_factory_impl.h b/extensions/browser/value_store/value_store_factory_impl.h new file mode 100644 index 0000000..d0a0677 --- /dev/null +++ b/extensions/browser/value_store/value_store_factory_impl.h @@ -0,0 +1,56 @@ +// Copyright 2016 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 EXTENSIONS_BROWSER_VALUE_STORE_VALUE_STORE_FACTORY_IMPL_H_ +#define EXTENSIONS_BROWSER_VALUE_STORE_VALUE_STORE_FACTORY_IMPL_H_ + +#include <set> +#include <string> + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/value_store/value_store.h" +#include "extensions/browser/value_store/value_store_factory.h" +#include "extensions/common/extension.h" + +namespace extensions { + +class LegacyValueStoreFactory; + +// Mint new |ValueStore| instances for use by the extensions system. These are +// used for extension rules, state, and settings. +class ValueStoreFactoryImpl : public ValueStoreFactory { + public: + explicit ValueStoreFactoryImpl(const base::FilePath& profile_path); + + // ValueStoreFactory + scoped_ptr<ValueStore> CreateRulesStore() override; + scoped_ptr<ValueStore> CreateStateStore() override; + scoped_ptr<ValueStore> CreateSettingsStore( + settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + void DeleteSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + bool HasSettings(settings_namespace::Namespace settings_namespace, + ModelType model_type, + const ExtensionId& extension_id) override; + std::set<ExtensionId> GetKnownExtensionIDs( + settings_namespace::Namespace settings_namespace, + ModelType model_type) const override; + + private: + // ValueStoreFactory is refcounted. + ~ValueStoreFactoryImpl() override; + + scoped_refptr<LegacyValueStoreFactory> legacy_factory_; + + DISALLOW_COPY_AND_ASSIGN(ValueStoreFactoryImpl); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_VALUE_STORE_VALUE_STORE_FACTORY_IMPL_H_ diff --git a/extensions/browser/value_store/value_store_frontend.cc b/extensions/browser/value_store/value_store_frontend.cc index 6f37bbc..b3912d6 100644 --- a/extensions/browser/value_store/value_store_frontend.cc +++ b/extensions/browser/value_store/value_store_frontend.cc @@ -13,31 +13,21 @@ #include "base/trace_event/trace_event.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/value_store/leveldb_value_store.h" +#include "extensions/browser/value_store/value_store_factory.h" using content::BrowserThread; +using extensions::ValueStoreFactory; class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> { public: - Backend() : storage_(NULL) {} - - void Init(const std::string& uma_client_name, const base::FilePath& db_path) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - DCHECK(!storage_); - TRACE_EVENT0("ValueStoreFrontend::Backend", "Init"); - db_path_ = db_path; - storage_ = new LeveldbValueStore(uma_client_name, db_path); - } - - // This variant is useful for testing (using a mock ValueStore). - void InitWithStore(scoped_ptr<ValueStore> storage) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - DCHECK(!storage_); - storage_ = storage.release(); - } + Backend(const scoped_refptr<ValueStoreFactory>& store_factory, + BackendType backend_type) + : store_factory_(store_factory), backend_type_(backend_type) {} void Get(const std::string& key, const ValueStoreFrontend::ReadCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); + LazyInit(); ValueStore::ReadResult result = storage_->Get(key); // Extract the value from the ReadResult and pass ownership of it to the @@ -57,6 +47,7 @@ class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> { void Set(const std::string& key, scoped_ptr<base::Value> value) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); + LazyInit(); // We don't need the old value, so skip generating changes. ValueStore::WriteResult result = storage_->Set( ValueStore::IGNORE_QUOTA | ValueStore::NO_GENERATE_CHANGES, @@ -68,6 +59,7 @@ class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> { void Remove(const std::string& key) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); + LazyInit(); storage_->Remove(key); } @@ -75,10 +67,23 @@ class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> { friend class base::RefCountedThreadSafe<Backend>; virtual ~Backend() { - if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - delete storage_; - } else { - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, storage_); + if (storage_ && !BrowserThread::CurrentlyOn(BrowserThread::FILE)) + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, + storage_.release()); + } + + void LazyInit() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + if (storage_) + return; + TRACE_EVENT0("ValueStoreFrontend::Backend", "LazyInit"); + switch (backend_type_) { + case BackendType::RULES: + storage_ = store_factory_->CreateRulesStore(); + break; + case BackendType::STATE: + storage_ = store_factory_->CreateStateStore(); + break; } } @@ -88,43 +93,29 @@ class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> { callback.Run(std::move(value)); } + // The factory which will be used to lazily create the ValueStore when needed. + // Used exclusively on the FILE thread. + scoped_refptr<ValueStoreFactory> store_factory_; + BackendType backend_type_; + // The actual ValueStore that handles persisting the data to disk. Used // exclusively on the FILE thread. - ValueStore* storage_; + scoped_ptr<ValueStore> storage_; base::FilePath db_path_; DISALLOW_COPY_AND_ASSIGN(Backend); }; -ValueStoreFrontend::ValueStoreFrontend() - : backend_(new Backend()) { -} - -ValueStoreFrontend::ValueStoreFrontend(const std::string& uma_client_name, - const base::FilePath& db_path) - : backend_(new Backend()) { - Init(uma_client_name, db_path); -} - -ValueStoreFrontend::ValueStoreFrontend(scoped_ptr<ValueStore> value_store) - : backend_(new Backend()) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&ValueStoreFrontend::Backend::InitWithStore, - backend_, base::Passed(&value_store))); -} +ValueStoreFrontend::ValueStoreFrontend( + const scoped_refptr<ValueStoreFactory>& store_factory, + BackendType backend_type) + : backend_(new Backend(store_factory, backend_type)) {} ValueStoreFrontend::~ValueStoreFrontend() { DCHECK(CalledOnValidThread()); } -void ValueStoreFrontend::Init(const std::string& uma_client_name, - const base::FilePath& db_path) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&ValueStoreFrontend::Backend::Init, - backend_, uma_client_name, db_path)); -} - void ValueStoreFrontend::Get(const std::string& key, const ReadCallback& callback) { DCHECK(CalledOnValidThread()); diff --git a/extensions/browser/value_store/value_store_frontend.h b/extensions/browser/value_store/value_store_frontend.h index 972efad..55253fb 100644 --- a/extensions/browser/value_store/value_store_frontend.h +++ b/extensions/browser/value_store/value_store_frontend.h @@ -20,22 +20,25 @@ namespace base { class FilePath; } +namespace extensions { +class ValueStoreFactory; +} // namespace extensions + // A frontend for a LeveldbValueStore, for use on the UI thread. class ValueStoreFrontend : public base::SupportsWeakPtr<ValueStoreFrontend>, public base::NonThreadSafe { public: + // The kind of extensions data stored in a backend. + enum class BackendType { RULES, STATE }; + typedef base::Callback<void(scoped_ptr<base::Value>)> ReadCallback; - ValueStoreFrontend(); - ValueStoreFrontend(const std::string& uma_client_name, - const base::FilePath& db_path); - // This variant is useful for testing (using a mock ValueStore). - explicit ValueStoreFrontend(scoped_ptr<ValueStore> value_store); + ValueStoreFrontend( + const scoped_refptr<extensions::ValueStoreFactory>& store_factory, + BackendType backend_type); ~ValueStoreFrontend(); - void Init(const std::string& uma_client_name, const base::FilePath& db_path); - // Retrieves a value from the database asynchronously, passing a copy to // |callback| when ready. NULL is passed if no matching entry is found. void Get(const std::string& key, const ReadCallback& callback); diff --git a/extensions/browser/value_store/value_store_frontend_unittest.cc b/extensions/browser/value_store/value_store_frontend_unittest.cc index 0ee4c5c..21f95b6 100644 --- a/extensions/browser/value_store/value_store_frontend_unittest.cc +++ b/extensions/browser/value_store/value_store_frontend_unittest.cc @@ -12,13 +12,12 @@ #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "content/public/test/test_browser_thread.h" +#include "extensions/browser/value_store/test_value_store_factory.h" #include "extensions/common/extension_paths.h" #include "testing/gtest/include/gtest/gtest.h" using content::BrowserThread; -const char kDatabaseUMAClientName[] = "Test"; - class ValueStoreFrontendTest : public testing::Test { public: ValueStoreFrontendTest() @@ -35,6 +34,8 @@ class ValueStoreFrontendTest : public testing::Test { db_path_ = temp_dir_.path().AppendASCII("temp_db"); base::CopyDirectory(src_db, db_path_, true); + factory_ = new extensions::TestValueStoreFactory(db_path_); + ResetStorage(); } @@ -45,7 +46,8 @@ class ValueStoreFrontendTest : public testing::Test { // Reset the value store, reloading the DB from disk. void ResetStorage() { - storage_.reset(new ValueStoreFrontend(kDatabaseUMAClientName, db_path_)); + storage_.reset(new ValueStoreFrontend( + factory_, ValueStoreFrontend::BackendType::RULES)); } bool Get(const std::string& key, scoped_ptr<base::Value>* output) { @@ -62,6 +64,7 @@ class ValueStoreFrontendTest : public testing::Test { base::MessageLoop::current()->QuitWhenIdle(); } + scoped_refptr<extensions::TestValueStoreFactory> factory_; scoped_ptr<ValueStoreFrontend> storage_; base::ScopedTempDir temp_dir_; base::FilePath db_path_; |