diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-09-18 14:34:04 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-10-04 11:15:27 +0000 |
commit | e6430e577f105ad8813c92e75c54660c4985026e (patch) | |
tree | 88115e5d1fb471fea807111924dcccbeadbf9e4f /chromium/services/preferences | |
parent | 53d399fe6415a96ea6986ec0d402a9c07da72453 (diff) | |
download | qtwebengine-chromium-e6430e577f105ad8813c92e75c54660c4985026e.tar.gz |
BASELINE: Update Chromium to 61.0.3163.99
Change-Id: I8452f34574d88ca2b27af9bd56fc9ff3f16b1367
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/services/preferences')
66 files changed, 3126 insertions, 1075 deletions
diff --git a/chromium/services/preferences/BUILD.gn b/chromium/services/preferences/BUILD.gn index d60d44d0684..3e7a94d09a1 100644 --- a/chromium/services/preferences/BUILD.gn +++ b/chromium/services/preferences/BUILD.gn @@ -11,6 +11,11 @@ service_manifest("manifest") { source = "manifest.json" } +service_manifest("local_state_manifest") { + name = "local_state" + source = "local_state_manifest.json" +} + source_set("preferences") { visibility = [ ":*", @@ -25,14 +30,16 @@ source_set("preferences") { "//services/service_manager/public/cpp", ] sources = [ - "persistent_pref_store_factory.cc", - "persistent_pref_store_factory.h", "persistent_pref_store_impl.cc", "persistent_pref_store_impl.h", + "pref_store_impl.cc", + "pref_store_impl.h", "pref_store_manager_impl.cc", "pref_store_manager_impl.h", "scoped_pref_connection_builder.cc", "scoped_pref_connection_builder.h", + "shared_pref_registry.cc", + "shared_pref_registry.h", ] } @@ -55,6 +62,8 @@ source_set("tests") { ] sources = [ "persistent_pref_store_impl_unittest.cc", + "pref_store_consistency_unittest.cc", + "pref_store_impl_unittest.cc", ] if (!is_ios) { sources += [ "pref_service_factory_unittest.cc" ] @@ -65,7 +74,15 @@ source_set("tests") { service_manifest("unittest_manifest") { name = "prefs_unittests" source = "unittest_manifest.json" - packaged_services = [ ":manifest" ] + packaged_services = [ + ":manifest", + ":unittest_helper_manifest", + ] +} + +service_manifest("unittest_helper_manifest") { + name = "prefs_unittest_helper" + source = "unittest_helper_manifest.json" } catalog("tests_catalog") { diff --git a/chromium/services/preferences/README.md b/chromium/services/preferences/README.md new file mode 100644 index 00000000000..a74dd9315a1 --- /dev/null +++ b/chromium/services/preferences/README.md @@ -0,0 +1,82 @@ +# Preference Service User Guide + +[TOC] + +## What is the Preference Service? + +Preferences, also known as "prefs", are key-value pairs stored by +Chrome. Examples include the settings in chrome://settings, all per-extension +metadata, the list of plugins and so on. Individual prefs are keyed by a string +and have a type. E.g., the "browser.enable_spellchecking" pref stores a boolean +indicating whether spell-checking is enabled. + +The pref service persists prefs to disk and communicates updates to prefs +between services, including Chrome itself. There is a pref service instance per +profile (prefs are persisted on a per-profile basis). + +## Using the service + +The service is used through a client library that offers clients fast and +synchronous access to prefs. To connect to the service and start reading and +writing prefs simply use the `ConnectToPrefService` factory function: + +``` cpp +#include "services/preferences/public/cpp/pref_service_factory.h" + +class MyService : public service_manager::Service { + void OnStart() { + auto* connector = context()->connector(); + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + // Register any preferences you intend to use in |pref_registry|. + prefs::ConnectToPrefService( + connector, std::move(pref_registry), {}, + base::Bind(&MyService::OnPrefServiceConnected, base::Unretained(this))); + } + + void OnPrefServiceConnected(std::unique_ptr<::PrefService> pref_service) { + // Use |pref_service|. + } +}; +``` + +The returned `PrefService` class predates the Pref Service and its behavior +hasn't changed (i.e. all existing documentation still applies). + +## Semantics + +Updates made on the `PrefService` object are reflected immediately in the +originating service and eventually in all other services. In other words, +updates are eventually consistent. + +## Registering your preferences + +Every pref should be owned by one service. The owning service provides the type +and default value for that pref. Owned prefs can be registered as public, +meaning other services can read and/or write them, or private (the default). Services +that want to access a pref not owned by them must still register those prefs as +"foreign" prefs. Registration happens through the `PrefRegistry` passed to +`ConnectToPrefService`. For example: + +**`//services/my_service/my_service.cc`** +``` cpp +void MyService::OnStart() { + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry->RegisterIntegerPref(kKey, kInitialValue, PrefRegistry::PUBLIC); + prefs::ConnectToPrefService(...); +} +``` + +**`//services/other_service/other_service.cc`** +``` cpp +void OtherService::OnStart() { + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry->RegisterForeignPref(kKey); + prefs::ConnectToPrefService(...); +} +``` + +## Design + +The design doc is here: + +https://docs.google.com/document/d/1JU8QUWxMEXWMqgkvFUumKSxr7Z-nfq0YvreSJTkMVmU/edit?usp=sharing diff --git a/chromium/services/preferences/local_state_manifest.json b/chromium/services/preferences/local_state_manifest.json new file mode 100644 index 00000000000..f3c24684e71 --- /dev/null +++ b/chromium/services/preferences/local_state_manifest.json @@ -0,0 +1,15 @@ +{ + "name": "local_state", + "display_name": "Local state preferences", + "interface_provider_specs": { + "service_manager:connector": { + "provides": { + "pref_client": [ + "prefs::mojom::PrefStoreConnector" + ] + }, + "requires": { + } + } + } +} diff --git a/chromium/services/preferences/manifest.json b/chromium/services/preferences/manifest.json index 40473465595..047409d2667 100644 --- a/chromium/services/preferences/manifest.json +++ b/chromium/services/preferences/manifest.json @@ -5,11 +5,7 @@ "service_manager:connector": { "provides": { "pref_client": [ - "prefs::mojom::PrefStoreConnector", - "prefs::mojom::PrefStoreRegistry" - ], - "pref_control": [ - "prefs::mojom::PrefServiceControl" + "prefs::mojom::PrefStoreConnector" ] }, "requires": { diff --git a/chromium/services/preferences/persistent_pref_store_factory.cc b/chromium/services/preferences/persistent_pref_store_factory.cc deleted file mode 100644 index 587ac96d182..00000000000 --- a/chromium/services/preferences/persistent_pref_store_factory.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 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 "services/preferences/persistent_pref_store_factory.h" - -#include <memory> -#include <utility> - -#include "components/prefs/json_pref_store.h" -#include "components/prefs/pref_filter.h" -#include "services/preferences/persistent_pref_store_impl.h" -#include "services/preferences/tracked/tracked_persistent_pref_store_factory.h" - -namespace prefs { -namespace { - -std::unique_ptr<PersistentPrefStoreImpl> CreateSimplePersistentPrefStore( - mojom::SimplePersistentPrefStoreConfigurationPtr config, - base::SequencedWorkerPool* worker_pool, - base::Closure on_initialized) { - return base::MakeUnique<PersistentPrefStoreImpl>( - new JsonPrefStore(config->pref_filename, - JsonPrefStore::GetTaskRunnerForFile( - config->pref_filename.DirName(), worker_pool), - nullptr), - std::move(on_initialized)); -} - -} // namespace - -std::unique_ptr<PersistentPrefStoreImpl> CreatePersistentPrefStore( - mojom::PersistentPrefStoreConfigurationPtr configuration, - base::SequencedWorkerPool* worker_pool, - base::Closure on_initialized) { - if (configuration->is_simple_configuration()) { - return CreateSimplePersistentPrefStore( - std::move(configuration->get_simple_configuration()), worker_pool, - std::move(on_initialized)); - } - if (configuration->is_tracked_configuration()) { - return base::MakeUnique<PersistentPrefStoreImpl>( - CreateTrackedPersistentPrefStore( - std::move(configuration->get_tracked_configuration()), worker_pool), - std::move(on_initialized)); - } - NOTREACHED(); - return nullptr; -} - -} // namespace prefs diff --git a/chromium/services/preferences/persistent_pref_store_factory.h b/chromium/services/preferences/persistent_pref_store_factory.h deleted file mode 100644 index 06cf8f701b0..00000000000 --- a/chromium/services/preferences/persistent_pref_store_factory.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 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 SERVICES_PREFERENCES_PERSISTENT_PREF_STORE_FACTORY_H_ -#define SERVICES_PREFERENCES_PERSISTENT_PREF_STORE_FACTORY_H_ - -#include <memory> - -#include "services/preferences/public/interfaces/preferences.mojom.h" - -namespace base { -class SequencedWorkerPool; -} - -namespace prefs { -class PersistentPrefStoreImpl; - -// Create a new mojom::PersistentPrefStore impl that runs on |worker_pool| -// configured by |configuration|. -std::unique_ptr<PersistentPrefStoreImpl> CreatePersistentPrefStore( - mojom::PersistentPrefStoreConfigurationPtr configuration, - base::SequencedWorkerPool* worker_pool, - base::Closure on_initialized); - -} // namespace prefs - -#endif // SERVICES_PREFERENCES_PERSISTENT_PREF_STORE_FACTORY_H_ diff --git a/chromium/services/preferences/persistent_pref_store_impl.cc b/chromium/services/preferences/persistent_pref_store_impl.cc index f5b4ae14d65..09357e3cc6d 100644 --- a/chromium/services/preferences/persistent_pref_store_impl.cc +++ b/chromium/services/preferences/persistent_pref_store_impl.cc @@ -16,6 +16,65 @@ #include "services/preferences/public/cpp/lib/util.h" namespace prefs { +namespace { + +// Creates a PrefUpdateValuePtr representing |value| at |path|. +mojom::PrefUpdateValuePtr CreatePrefUpdate(const std::vector<std::string>& path, + const base::Value* value) { + if (path.empty()) { + return mojom::PrefUpdateValue::NewAtomicUpdate( + value ? value->CreateDeepCopy() : nullptr); + } + std::vector<mojom::SubPrefUpdatePtr> pref_updates; + pref_updates.emplace_back(base::in_place, path, + value ? value->CreateDeepCopy() : nullptr); + return mojom::PrefUpdateValue::NewSplitUpdates(std::move(pref_updates)); +} + +// Returns a mojom::PrefUpdateValuePtr for |path| relative to |value|. If the +// full path does not exist, a PrefUpdateValue containing the closest value is +// returned. +// +// For example, for a |path| of {"foo", "bar"}: +// - with a |value| of +// { +// "foo": 1 +// } +// returns a path {"foo"} and value 1. +// +// - with a |value| of +// {} +// returns a path {"foo"} and null value. +// +// - with a |value| of +// { +// "foo": {} +// } +// returns a path {"foo", "bar"} and null value. +// +// - with a |value| of +// { +// "foo": { +// "bar": "baz" +// } +// } +// returns a path {"foo", "bar"} and value "baz". +mojom::PrefUpdateValuePtr LookupPrefUpdate(const std::vector<std::string>& path, + const base::Value* value) { + if (!value) + return CreatePrefUpdate(std::vector<std::string>(), value); + + for (size_t i = 0; i < path.size(); ++i) { + const base::DictionaryValue* dictionary_value = nullptr; + if (!value->GetAsDictionary(&dictionary_value) || + !dictionary_value->Get(path[i], &value)) { + return CreatePrefUpdate({path.begin(), path.begin() + i}, value); + } + } + return CreatePrefUpdate(path, value); +} + +} // namespace class PersistentPrefStoreImpl::Connection : public mojom::PersistentPrefStore { public: @@ -55,9 +114,24 @@ class PersistentPrefStoreImpl::Connection : public mojom::PersistentPrefStore { void SetValues(std::vector<mojom::PrefUpdatePtr> updates) override { base::AutoReset<bool> scoped_call_in_progress(&write_in_progress_, true); pref_store_->SetValues(std::move(updates)); + observer_->OnPrefChangeAck(); + } + + void RequestValue(const std::string& key, + const std::vector<std::string>& path) override { + if (!base::ContainsKey(observed_keys_, key)) + return; + + const base::Value* value = nullptr; + pref_store_->GetValue(key, &value); + std::vector<mojom::PrefUpdatePtr> updates; + updates.emplace_back(base::in_place, key, LookupPrefUpdate(path, value), 0); + observer_->OnPrefsChanged(std::move(updates)); } - void CommitPendingWrite() override { pref_store_->CommitPendingWrite(); } + void CommitPendingWrite(CommitPendingWriteCallback done_callback) override { + pref_store_->CommitPendingWrite(std::move(done_callback)); + } void SchedulePendingLossyWrites() override { pref_store_->SchedulePendingLossyWrites(); } @@ -83,15 +157,16 @@ PersistentPrefStoreImpl::PersistentPrefStoreImpl( scoped_refptr<PersistentPrefStore> backing_pref_store, base::OnceClosure on_initialized) : backing_pref_store_(backing_pref_store) { + backing_pref_store_->AddObserver(this); if (!backing_pref_store_->IsInitializationComplete()) { - backing_pref_store_->AddObserver(this); on_initialized_ = std::move(on_initialized); initializing_ = true; - backing_pref_store_->ReadPrefsAsync(nullptr); } } -PersistentPrefStoreImpl::~PersistentPrefStoreImpl() = default; +PersistentPrefStoreImpl::~PersistentPrefStoreImpl() { + backing_pref_store_->RemoveObserver(this); +} mojom::PersistentPrefStoreConnectionPtr PersistentPrefStoreImpl::CreateConnection(ObservedPrefs observed_prefs) { @@ -106,6 +181,7 @@ PersistentPrefStoreImpl::CreateConnection(ObservedPrefs observed_prefs) { mojom::PrefStoreObserverPtr observer; mojom::PrefStoreObserverRequest observer_request = mojo::MakeRequest(&observer); + auto values = FilterPrefs(backing_pref_store_->GetValues(), observed_prefs); auto connection = base::MakeUnique<Connection>( this, mojo::MakeRequest(&pref_store_ptr), std::move(observer), std::move(observed_prefs)); @@ -113,22 +189,38 @@ PersistentPrefStoreImpl::CreateConnection(ObservedPrefs observed_prefs) { connections_.insert(std::make_pair(connection_ptr, std::move(connection))); return mojom::PersistentPrefStoreConnection::New( mojom::PrefStoreConnection::New(std::move(observer_request), - backing_pref_store_->GetValues(), true), + std::move(values), true), std::move(pref_store_ptr), backing_pref_store_->GetReadError(), backing_pref_store_->ReadOnly()); } -void PersistentPrefStoreImpl::OnPrefValueChanged(const std::string& key) {} +void PersistentPrefStoreImpl::OnPrefValueChanged(const std::string& key) { + if (write_in_progress_) + return; + + const base::Value* value = nullptr; + for (auto& entry : connections_) { + auto update_value = mojom::PrefUpdateValue::New(); + if (GetValue(key, &value)) { + update_value->set_atomic_update(value->CreateDeepCopy()); + } else { + update_value->set_atomic_update(nullptr); + } + std::vector<mojom::PrefUpdatePtr> updates; + updates.emplace_back(base::in_place, key, std::move(update_value), 0); + entry.first->OnPrefValuesChanged(updates); + } +} void PersistentPrefStoreImpl::OnInitializationCompleted(bool succeeded) { DCHECK(initializing_); - backing_pref_store_->RemoveObserver(this); initializing_ = false; std::move(on_initialized_).Run(); } void PersistentPrefStoreImpl::SetValues( std::vector<mojom::PrefUpdatePtr> updates) { + base::AutoReset<bool> scoped_call_in_progress(&write_in_progress_, true); for (auto& entry : connections_) entry.first->OnPrefValuesChanged(updates); @@ -171,8 +263,14 @@ void PersistentPrefStoreImpl::SetValues( } } -void PersistentPrefStoreImpl::CommitPendingWrite() { - backing_pref_store_->CommitPendingWrite(); +bool PersistentPrefStoreImpl::GetValue(const std::string& key, + const base::Value** value) const { + return backing_pref_store_->GetValue(key, value); +} + +void PersistentPrefStoreImpl::CommitPendingWrite( + base::OnceClosure done_callback) { + backing_pref_store_->CommitPendingWrite(std::move(done_callback)); } void PersistentPrefStoreImpl::SchedulePendingLossyWrites() { diff --git a/chromium/services/preferences/persistent_pref_store_impl.h b/chromium/services/preferences/persistent_pref_store_impl.h index 5111ed7f4e2..9d888936b05 100644 --- a/chromium/services/preferences/persistent_pref_store_impl.h +++ b/chromium/services/preferences/persistent_pref_store_impl.h @@ -6,10 +6,12 @@ #define SERVICES_PREFERENCES_PERSISTENT_PREF_STORE_IMPL_H_ #include <memory> +#include <set> #include <string> #include <unordered_map> #include <vector> +#include "base/callback.h" #include "base/macros.h" #include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom.h" @@ -37,8 +39,9 @@ class PersistentPrefStoreImpl : public PrefStore::Observer { class Connection; void SetValues(std::vector<mojom::PrefUpdatePtr> updates); + bool GetValue(const std::string& key, const base::Value** value) const; - void CommitPendingWrite(); + void CommitPendingWrite(base::OnceClosure done_callback); void SchedulePendingLossyWrites(); void ClearMutableValues(); @@ -56,6 +59,10 @@ class PersistentPrefStoreImpl : public PrefStore::Observer { base::OnceClosure on_initialized_; + // If true then a write is in progress and any update notifications should be + // ignored, as those updates would originate from ourselves. + bool write_in_progress_ = false; + DISALLOW_COPY_AND_ASSIGN(PersistentPrefStoreImpl); }; diff --git a/chromium/services/preferences/persistent_pref_store_impl_unittest.cc b/chromium/services/preferences/persistent_pref_store_impl_unittest.cc index 7a326276c0a..ad364efb1bd 100644 --- a/chromium/services/preferences/persistent_pref_store_impl_unittest.cc +++ b/chromium/services/preferences/persistent_pref_store_impl_unittest.cc @@ -32,7 +32,12 @@ class PrefStoreObserverMock : public PrefStore::Observer { class PersistentPrefStoreMock : public InMemoryPrefStore { public: - MOCK_METHOD0(CommitPendingWrite, void()); + void CommitPendingWrite(base::OnceClosure callback) override { + CommitPendingWriteMock(); + InMemoryPrefStore::CommitPendingWrite(std::move(callback)); + } + + MOCK_METHOD0(CommitPendingWriteMock, void()); MOCK_METHOD0(SchedulePendingLossyWrites, void()); MOCK_METHOD0(ClearMutableValues, void()); @@ -50,56 +55,6 @@ void ExpectPrefChange(PrefStore* pref_store, base::StringPiece key) { pref_store->RemoveObserver(&observer); } -class InitializationMockPersistentPrefStore : public InMemoryPrefStore { - public: - InitializationMockPersistentPrefStore( - bool success, - PersistentPrefStore::PrefReadError error, - bool read_only) - : success_(success), read_error_(error), read_only_(read_only) {} - - bool IsInitializationComplete() const override { - return initialized_ && success_; - } - - void AddObserver(PrefStore::Observer* observer) override { - observers_.AddObserver(observer); - } - - void RemoveObserver(PrefStore::Observer* observer) override { - observers_.RemoveObserver(observer); - } - - void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override { - DCHECK(!error_delegate); - DCHECK(!initialized_); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&InitializationMockPersistentPrefStore::CompleteRead, this)); - } - - void CompleteRead() { - initialized_ = true; - for (auto& observer : observers_) { - observer.OnInitializationCompleted(success_); - } - } - - PersistentPrefStore::PrefReadError GetReadError() const override { - return read_error_; - } - bool ReadOnly() const override { return read_only_; } - - private: - ~InitializationMockPersistentPrefStore() override = default; - - bool initialized_ = false; - bool success_; - PersistentPrefStore::PrefReadError read_error_; - bool read_only_; - base::ObserverList<PrefStore::Observer, true> observers_; -}; - constexpr char kKey[] = "path.to.key"; constexpr char kOtherKey[] = "path.to.other_key"; @@ -146,37 +101,27 @@ class PersistentPrefStoreImplTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(PersistentPrefStoreImplTest); }; -TEST_F(PersistentPrefStoreImplTest, InitializationSuccess) { - auto backing_pref_store = - make_scoped_refptr(new InitializationMockPersistentPrefStore( - true, PersistentPrefStore::PREF_READ_ERROR_NONE, false)); - CreateImpl(backing_pref_store); - EXPECT_TRUE(pref_store()->IsInitializationComplete()); - EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, - pref_store()->GetReadError()); - EXPECT_FALSE(pref_store()->ReadOnly()); -} - -TEST_F(PersistentPrefStoreImplTest, InitializationFailure) { - auto backing_pref_store = - make_scoped_refptr(new InitializationMockPersistentPrefStore( - false, PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, true)); - CreateImpl(backing_pref_store); - EXPECT_FALSE(pref_store()->IsInitializationComplete()); - EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, - pref_store()->GetReadError()); - EXPECT_TRUE(pref_store()->ReadOnly()); -} - TEST_F(PersistentPrefStoreImplTest, InitialValue) { + constexpr char kUnregisteredKey[] = "path.to.unregistered_key"; + constexpr char kUnregisteredTopLevelKey[] = "unregistered_key"; + constexpr char kUnregisteredPrefixKey[] = "p"; auto backing_pref_store = make_scoped_refptr(new InMemoryPrefStore()); const base::Value value("value"); backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0); + backing_pref_store->SetValue(kUnregisteredKey, value.CreateDeepCopy(), 0); + backing_pref_store->SetValue(kUnregisteredPrefixKey, value.CreateDeepCopy(), + 0); + backing_pref_store->SetValue(kUnregisteredTopLevelKey, value.CreateDeepCopy(), + 0); CreateImpl(backing_pref_store); EXPECT_TRUE(pref_store()->IsInitializationComplete()); const base::Value* output = nullptr; ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); EXPECT_TRUE(value.Equals(output)); + + EXPECT_FALSE(pref_store()->GetValue(kUnregisteredKey, nullptr)); + EXPECT_FALSE(pref_store()->GetValue(kUnregisteredTopLevelKey, nullptr)); + EXPECT_FALSE(pref_store()->GetValue(kUnregisteredPrefixKey, nullptr)); } TEST_F(PersistentPrefStoreImplTest, InitialValueWithoutPathExpansion) { @@ -200,7 +145,7 @@ TEST_F(PersistentPrefStoreImplTest, WriteObservedByOtherClient) { EXPECT_TRUE(other_pref_store->IsInitializationComplete()); const base::Value value("value"); - pref_store()->SetValueSilently(kKey, value.CreateDeepCopy(), 0); + pref_store()->SetValue(kKey, value.CreateDeepCopy(), 0); ExpectPrefChange(other_pref_store.get(), kKey); const base::Value* output = nullptr; @@ -303,10 +248,8 @@ TEST_F(PersistentPrefStoreImplTest, CommitPendingWrite) { auto backing_store = make_scoped_refptr(new PersistentPrefStoreMock); CreateImpl(backing_store); base::RunLoop run_loop; - EXPECT_CALL(*backing_store, CommitPendingWrite()) - .Times(2) - .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); }))); - pref_store()->CommitPendingWrite(); + EXPECT_CALL(*backing_store, CommitPendingWriteMock()).Times(2); + pref_store()->CommitPendingWrite(run_loop.QuitClosure()); run_loop.Run(); } @@ -316,7 +259,7 @@ TEST_F(PersistentPrefStoreImplTest, SchedulePendingLossyWrites) { base::RunLoop run_loop; EXPECT_CALL(*backing_store, SchedulePendingLossyWrites()) .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); }))); - EXPECT_CALL(*backing_store, CommitPendingWrite()).Times(1); + EXPECT_CALL(*backing_store, CommitPendingWriteMock()).Times(1); pref_store()->SchedulePendingLossyWrites(); run_loop.Run(); } @@ -327,7 +270,7 @@ TEST_F(PersistentPrefStoreImplTest, ClearMutableValues) { base::RunLoop run_loop; EXPECT_CALL(*backing_store, ClearMutableValues()) .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); }))); - EXPECT_CALL(*backing_store, CommitPendingWrite()).Times(1); + EXPECT_CALL(*backing_store, CommitPendingWriteMock()).Times(1); pref_store()->ClearMutableValues(); run_loop.Run(); } diff --git a/chromium/services/preferences/pref_service_factory_unittest.cc b/chromium/services/preferences/pref_service_factory_unittest.cc index fad18de480b..9166e96a0d1 100644 --- a/chromium/services/preferences/pref_service_factory_unittest.cc +++ b/chromium/services/preferences/pref_service_factory_unittest.cc @@ -9,15 +9,17 @@ #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/sequenced_worker_pool_owner.h" +#include "components/prefs/in_memory_pref_store.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" +#include "components/prefs/pref_service_factory.h" #include "components/prefs/value_map_pref_store.h" #include "components/prefs/writeable_pref_store.h" #include "mojo/public/cpp/bindings/binding_set.h" +#include "services/preferences/pref_store_impl.h" #include "services/preferences/public/cpp/dictionary_value_update.h" #include "services/preferences/public/cpp/pref_service_main.h" -#include "services/preferences/public/cpp/pref_store_impl.h" #include "services/preferences/public/cpp/scoped_pref_update.h" #include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/service_manager/public/cpp/binder_registry.h" @@ -31,10 +33,19 @@ namespace { class ServiceTestClient : public service_manager::test::ServiceTestClient, public service_manager::mojom::ServiceFactory { public: - ServiceTestClient(service_manager::test::ServiceTest* test, - scoped_refptr<base::SequencedWorkerPool> worker_pool) + ServiceTestClient( + service_manager::test::ServiceTest* test, + scoped_refptr<WriteablePrefStore> above_user_prefs_pref_store, + scoped_refptr<WriteablePrefStore> below_user_prefs_pref_store, + scoped_refptr<PersistentPrefStore> user_prefs, + scoped_refptr<PrefRegistry> pref_registry, + base::OnceCallback<void(service_manager::Connector*)> connector_callback) : service_manager::test::ServiceTestClient(test), - worker_pool_(std::move(worker_pool)) { + above_user_prefs_pref_store_(std::move(above_user_prefs_pref_store)), + below_user_prefs_pref_store_(std::move(below_user_prefs_pref_store)), + user_prefs_(std::move(user_prefs)), + pref_registry_(std::move(pref_registry)), + connector_callback_(std::move(connector_callback)) { registry_.AddInterface<service_manager::mojom::ServiceFactory>( base::Bind(&ServiceTestClient::Create, base::Unretained(this))); } @@ -43,8 +54,7 @@ class ServiceTestClient : public service_manager::test::ServiceTestClient, void OnBindInterface(const service_manager::BindSourceInfo& source_info, const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) override { - registry_.BindInterface(source_info, interface_name, - std::move(interface_pipe)); + registry_.BindInterface(interface_name, std::move(interface_pipe)); } void CreateService(service_manager::mojom::ServiceRequest request, @@ -52,25 +62,35 @@ class ServiceTestClient : public service_manager::test::ServiceTestClient, if (name == prefs::mojom::kServiceName) { pref_service_context_.reset(new service_manager::ServiceContext( CreatePrefService( - {PrefValueStore::COMMAND_LINE_STORE, - PrefValueStore::RECOMMENDED_STORE, PrefValueStore::USER_STORE, - PrefValueStore::DEFAULT_STORE}, - worker_pool_), + nullptr, nullptr, nullptr, above_user_prefs_pref_store_.get(), + user_prefs_.get(), nullptr, below_user_prefs_pref_store_.get(), + pref_registry_.get()) + .first, std::move(request))); + } else if (name == "prefs_unittest_helper") { + test_helper_service_context_ = + base::MakeUnique<service_manager::ServiceContext>( + base::MakeUnique<service_manager::Service>(), std::move(request)); + std::move(connector_callback_) + .Run(test_helper_service_context_->connector()); } } - void Create(const service_manager::BindSourceInfo& source_info, - service_manager::mojom::ServiceFactoryRequest request) { + void Create(service_manager::mojom::ServiceFactoryRequest request) { service_factory_bindings_.AddBinding(this, std::move(request)); } private: - scoped_refptr<base::SequencedWorkerPool> worker_pool_; service_manager::BinderRegistry registry_; mojo::BindingSet<service_manager::mojom::ServiceFactory> service_factory_bindings_; + scoped_refptr<WriteablePrefStore> above_user_prefs_pref_store_; + scoped_refptr<WriteablePrefStore> below_user_prefs_pref_store_; + scoped_refptr<PersistentPrefStore> user_prefs_; + scoped_refptr<PrefRegistry> pref_registry_; std::unique_ptr<service_manager::ServiceContext> pref_service_context_; + std::unique_ptr<service_manager::ServiceContext> test_helper_service_context_; + base::OnceCallback<void(service_manager::Connector*)> connector_callback_; }; constexpr int kInitialValue = 1; @@ -78,69 +98,107 @@ constexpr int kUpdatedValue = 2; constexpr char kKey[] = "some_key"; constexpr char kOtherKey[] = "some_other_key"; constexpr char kDictionaryKey[] = "a.dictionary.pref"; +constexpr char kInitialKey[] = "initial_key"; +constexpr char kOtherInitialKey[] = "other_initial_key"; class PrefServiceFactoryTest : public service_manager::test::ServiceTest { public: - PrefServiceFactoryTest() - : ServiceTest("prefs_unittests", false), - worker_pool_owner_(2, "PrefServiceFactoryTest") {} + PrefServiceFactoryTest() : ServiceTest("prefs_unittests", false) {} protected: void SetUp() override { - ServiceTest::SetUp(); - ASSERT_TRUE(profile_dir_.CreateUniqueTempDir()); - - // Init the pref service (in production Chrome startup would do this.) - mojom::PrefServiceControlPtr control; - connector()->BindInterface(mojom::kServiceName, &control); - auto config = mojom::PersistentPrefStoreConfiguration::New(); - config->set_simple_configuration( - mojom::SimplePersistentPrefStoreConfiguration::New( - profile_dir_.GetPath().AppendASCII("Preferences"))); - control->Init(std::move(config)); above_user_prefs_pref_store_ = new ValueMapPrefStore(); below_user_prefs_pref_store_ = new ValueMapPrefStore(); - connector()->BindInterface(mojom::kServiceName, &pref_store_registry_); - CreateLayeredPrefStores(); + user_prefs_ = new InMemoryPrefStore(); + PrefServiceFactory factory; + factory.set_user_prefs(user_prefs_); + factory.set_recommended_prefs(below_user_prefs_pref_store_); + factory.set_command_line_prefs(above_user_prefs_pref_store_); + pref_service_ = factory.Create(GetInitialPrefRegistry().get()); + + base::RunLoop run_loop; + connector_callback_ = + base::BindOnce(&PrefServiceFactoryTest::SetOtherClientConnector, + base::Unretained(this), run_loop.QuitClosure()); + ServiceTest::SetUp(); + ASSERT_TRUE(profile_dir_.CreateUniqueTempDir()); + connector()->StartService("prefs_unittest_helper"); + run_loop.Run(); } - virtual void CreateLayeredPrefStores() { - above_user_prefs_impl_ = PrefStoreImpl::Create( - pref_store_registry(), above_user_prefs_pref_store_, - PrefValueStore::COMMAND_LINE_STORE); - below_user_prefs_impl_ = PrefStoreImpl::Create( - pref_store_registry(), below_user_prefs_pref_store_, - PrefValueStore::RECOMMENDED_STORE); + service_manager::Connector* other_client_connector() { + return other_client_connector_; } // service_manager::test::ServiceTest: std::unique_ptr<service_manager::Service> CreateService() override { - return base::MakeUnique<ServiceTestClient>(this, worker_pool_owner_.pool()); + return base::MakeUnique<ServiceTestClient>( + this, above_user_prefs_pref_store_, below_user_prefs_pref_store_, + user_prefs_, GetInitialPrefRegistry().get(), + std::move(connector_callback_)); } // Create a fully initialized PrefService synchronously. std::unique_ptr<PrefService> Create() { + return CreateImpl(CreateDefaultPrefRegistry(), connector()); + } + + std::unique_ptr<PrefService> CreateForeign() { + return CreateImpl(CreateDefaultForeignPrefRegistry(), + other_client_connector()); + } + + std::unique_ptr<PrefService> CreateImpl( + scoped_refptr<PrefRegistry> pref_registry, + service_manager::Connector* connector) { std::unique_ptr<PrefService> pref_service; base::RunLoop run_loop; - CreateAsync(std::vector<PrefValueStore::PrefStoreType>(), - run_loop.QuitClosure(), &pref_service); + CreateAsync(std::move(pref_registry), connector, run_loop.QuitClosure(), + &pref_service); run_loop.Run(); return pref_service; } - void CreateAsync( - const std::vector<PrefValueStore::PrefStoreType>& already_connected_types, - base::Closure callback, - std::unique_ptr<PrefService>* out) { - auto pref_registry = make_scoped_refptr(new PrefRegistrySimple()); - pref_registry->RegisterIntegerPref(kKey, kInitialValue); - pref_registry->RegisterIntegerPref(kOtherKey, kInitialValue); - pref_registry->RegisterDictionaryPref(kDictionaryKey); + void CreateAsync(scoped_refptr<PrefRegistry> pref_registry, + service_manager::Connector* connector, + base::Closure callback, + std::unique_ptr<PrefService>* out) { ConnectToPrefService( - connector(), pref_registry, already_connected_types, + connector, std::move(pref_registry), base::Bind(&PrefServiceFactoryTest::OnCreate, callback, out)); } + scoped_refptr<PrefRegistrySimple> GetInitialPrefRegistry() { + if (!pref_registry_) { + pref_registry_ = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry_->RegisterIntegerPref(kInitialKey, kInitialValue, + PrefRegistry::PUBLIC); + pref_registry_->RegisterIntegerPref(kOtherInitialKey, kInitialValue, + PrefRegistry::PUBLIC); + } + return pref_registry_; + } + + scoped_refptr<PrefRegistrySimple> CreateDefaultPrefRegistry() { + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry->RegisterIntegerPref(kKey, kInitialValue, + PrefRegistry::PUBLIC); + pref_registry->RegisterIntegerPref(kOtherKey, kInitialValue, + PrefRegistry::PUBLIC); + pref_registry->RegisterDictionaryPref(kDictionaryKey, PrefRegistry::PUBLIC); + pref_registry->RegisterForeignPref(kInitialKey); + pref_registry->RegisterForeignPref(kOtherInitialKey); + return pref_registry; + } + + scoped_refptr<PrefRegistrySimple> CreateDefaultForeignPrefRegistry() { + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry->RegisterForeignPref(kKey); + pref_registry->RegisterForeignPref(kOtherKey); + pref_registry->RegisterForeignPref(kDictionaryKey); + return pref_registry; + } + // Wait until first update of the pref |key| in |pref_service| synchronously. void WaitForPrefChange(PrefService* pref_service, const std::string& key) { PrefChangeRegistrar registrar; @@ -156,14 +214,13 @@ class PrefServiceFactoryTest : public service_manager::test::ServiceTest { WriteablePrefStore* below_user_prefs_pref_store() { return below_user_prefs_pref_store_.get(); } - mojom::PrefStoreRegistry* pref_store_registry() { - return pref_store_registry_.get(); - } + PrefService* pref_service() { return pref_service_.get(); } private: - // Called when the PrefService has been initialized. - static void OnInit(const base::Closure& quit_closure, bool success) { - quit_closure.Run(); + void SetOtherClientConnector(base::OnceClosure done, + service_manager::Connector* connector) { + other_client_connector_ = connector; + std::move(done).Run(); } // Called when the PrefService has been created. @@ -172,12 +229,6 @@ class PrefServiceFactoryTest : public service_manager::test::ServiceTest { std::unique_ptr<PrefService> pref_service) { DCHECK(pref_service); *out = std::move(pref_service); - if ((*out)->GetInitializationStatus() == - PrefService::INITIALIZATION_STATUS_WAITING) { - (*out)->AddPrefInitObserver( - base::Bind(PrefServiceFactoryTest::OnInit, quit_closure)); - return; - } quit_closure.Run(); } @@ -189,12 +240,13 @@ class PrefServiceFactoryTest : public service_manager::test::ServiceTest { } base::ScopedTempDir profile_dir_; - base::SequencedWorkerPoolOwner worker_pool_owner_; scoped_refptr<WriteablePrefStore> above_user_prefs_pref_store_; - std::unique_ptr<PrefStoreImpl> above_user_prefs_impl_; scoped_refptr<WriteablePrefStore> below_user_prefs_pref_store_; - std::unique_ptr<PrefStoreImpl> below_user_prefs_impl_; - mojom::PrefStoreRegistryPtr pref_store_registry_; + scoped_refptr<PersistentPrefStore> user_prefs_; + scoped_refptr<PrefRegistrySimple> pref_registry_; + std::unique_ptr<PrefService> pref_service_; + service_manager::Connector* other_client_connector_ = nullptr; + base::OnceCallback<void(service_manager::Connector*)> connector_callback_; DISALLOW_COPY_AND_ASSIGN(PrefServiceFactoryTest); }; @@ -211,7 +263,7 @@ TEST_F(PrefServiceFactoryTest, Basic) { // Check that updates in one client eventually propagates to the other. TEST_F(PrefServiceFactoryTest, MultipleClients) { auto pref_service = Create(); - auto pref_service2 = Create(); + auto pref_service2 = CreateForeign(); EXPECT_EQ(kInitialValue, pref_service->GetInteger(kKey)); EXPECT_EQ(kInitialValue, pref_service2->GetInteger(kKey)); @@ -220,6 +272,59 @@ TEST_F(PrefServiceFactoryTest, MultipleClients) { EXPECT_EQ(kUpdatedValue, pref_service2->GetInteger(kKey)); } +// Check that updates in one client eventually propagates to the other. +TEST_F(PrefServiceFactoryTest, InternalAndExternalClients) { + auto pref_service2 = Create(); + + EXPECT_EQ(kInitialValue, pref_service()->GetInteger(kInitialKey)); + EXPECT_EQ(kInitialValue, pref_service2->GetInteger(kInitialKey)); + EXPECT_EQ(kInitialValue, pref_service()->GetInteger(kOtherInitialKey)); + EXPECT_EQ(kInitialValue, pref_service2->GetInteger(kOtherInitialKey)); + pref_service()->SetInteger(kInitialKey, kUpdatedValue); + WaitForPrefChange(pref_service2.get(), kInitialKey); + EXPECT_EQ(kUpdatedValue, pref_service2->GetInteger(kInitialKey)); + + pref_service2->SetInteger(kOtherInitialKey, kUpdatedValue); + WaitForPrefChange(pref_service(), kOtherInitialKey); + EXPECT_EQ(kUpdatedValue, pref_service()->GetInteger(kOtherInitialKey)); +} + +TEST_F(PrefServiceFactoryTest, MultipleConnectionsFromSingleClient) { + Create(); + CreateForeign(); + Create(); + CreateForeign(); +} + +// Check that defaults set by one client are correctly shared to the other +// client. +TEST_F(PrefServiceFactoryTest, MultipleClients_Defaults) { + std::unique_ptr<PrefService> pref_service, pref_service2; + { + base::RunLoop run_loop; + auto done_closure = base::BarrierClosure(2, run_loop.QuitClosure()); + + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry->RegisterIntegerPref(kKey, kInitialValue, + PrefRegistry::PUBLIC); + pref_registry->RegisterForeignPref(kOtherKey); + auto pref_registry2 = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry2->RegisterForeignPref(kKey); + pref_registry2->RegisterIntegerPref(kOtherKey, kInitialValue, + PrefRegistry::PUBLIC); + CreateAsync(std::move(pref_registry), connector(), done_closure, + &pref_service); + CreateAsync(std::move(pref_registry2), other_client_connector(), + done_closure, &pref_service2); + run_loop.Run(); + } + + EXPECT_EQ(kInitialValue, pref_service->GetInteger(kKey)); + EXPECT_EQ(kInitialValue, pref_service2->GetInteger(kKey)); + EXPECT_EQ(kInitialValue, pref_service->GetInteger(kOtherKey)); + EXPECT_EQ(kInitialValue, pref_service2->GetInteger(kOtherKey)); +} + // Check that read-only pref store changes are observed. TEST_F(PrefServiceFactoryTest, ReadOnlyPrefStore) { auto pref_service = Create(); @@ -278,7 +383,7 @@ void Fail(PrefService* pref_service) { TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_Basic) { auto pref_service = Create(); - auto pref_service2 = Create(); + auto pref_service2 = CreateForeign(); void (*updates[])(ScopedDictionaryPrefUpdate*) = { [](ScopedDictionaryPrefUpdate* update) { @@ -436,7 +541,7 @@ TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_Basic) { TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_Erase) { auto pref_service = Create(); - auto pref_service2 = Create(); + auto pref_service2 = CreateForeign(); { ScopedDictionaryPrefUpdate update(pref_service.get(), kDictionaryKey); update->SetInteger("path.to.integer", 1); @@ -454,7 +559,7 @@ TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_Erase) { TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_ClearDictionary) { auto pref_service = Create(); - auto pref_service2 = Create(); + auto pref_service2 = CreateForeign(); { ScopedDictionaryPrefUpdate update(pref_service.get(), kDictionaryKey); @@ -474,11 +579,16 @@ TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_ClearDictionary) { TEST_F(PrefServiceFactoryTest, MultipleClients_SubPrefUpdates_ClearEmptyDictionary) { auto pref_service = Create(); - auto pref_service2 = Create(); + auto pref_service2 = CreateForeign(); { ScopedDictionaryPrefUpdate update(pref_service.get(), kDictionaryKey); - update.Get(); + update->SetInteger(kKey, kInitialValue); + } + WaitForPrefChange(pref_service2.get(), kDictionaryKey); + { + ScopedDictionaryPrefUpdate update(pref_service.get(), kDictionaryKey); + update->Remove(kKey, nullptr); } WaitForPrefChange(pref_service2.get(), kDictionaryKey); EXPECT_TRUE(pref_service2->GetDictionary(kDictionaryKey)->empty()); @@ -494,89 +604,5 @@ TEST_F(PrefServiceFactoryTest, WaitForPrefChange(pref_service2.get(), kKey); } -class PrefServiceFactoryManualPrefStoreRegistrationTest - : public PrefServiceFactoryTest { - protected: - void CreateLayeredPrefStores() override {} -}; - -class ConnectionReportingPrefStoreImpl : public mojom::PrefStore { - public: - ConnectionReportingPrefStoreImpl(base::OnceClosure on_add_observer, - mojom::PrefStoreRequest request) - : on_add_observer_(std::move(on_add_observer)), - binding_(this, std::move(request)) {} - - void AddObserver(const std::vector<std::string>& prefs_to_observe, - AddObserverCallback callback) override { - observer_added_ = true; - if (on_add_observer_) - std::move(on_add_observer_).Run(); - - mojom::PrefStoreObserverPtr observer; - auto values = base::MakeUnique<base::DictionaryValue>(); - values->Set(kKey, base::MakeUnique<base::Value>(kUpdatedValue)); - std::move(callback).Run(mojom::PrefStoreConnection::New( - mojo::MakeRequest(&observer), std::move(values), true)); - } - - bool observer_added() { return observer_added_; } - - private: - bool observer_added_ = false; - base::OnceClosure on_add_observer_; - mojo::Binding<mojom::PrefStore> binding_; -}; - -// Check that connect calls received before all read-only prefs stores have -// been registered are correctly handled. -TEST_F(PrefServiceFactoryManualPrefStoreRegistrationTest, - RegistrationBeforeAndAfterConnect) { - base::RunLoop add_observer_run_loop; - - mojom::PrefStorePtr below_ptr; - ConnectionReportingPrefStoreImpl below_user_prefs( - add_observer_run_loop.QuitClosure(), mojo::MakeRequest(&below_ptr)); - pref_store_registry()->Register(PrefValueStore::RECOMMENDED_STORE, - std::move(below_ptr)); - - std::unique_ptr<PrefService> pref_service; - base::RunLoop run_loop; - auto barrier = base::BarrierClosure(2, run_loop.QuitClosure()); - CreateAsync(std::vector<PrefValueStore::PrefStoreType>(), barrier, - &pref_service); - - add_observer_run_loop.Run(); - ASSERT_TRUE(below_user_prefs.observer_added()); - - EXPECT_FALSE(pref_service); - - mojom::PrefStorePtr above_ptr; - ConnectionReportingPrefStoreImpl above_user_prefs( - barrier, mojo::MakeRequest(&above_ptr)); - pref_store_registry()->Register(PrefValueStore::COMMAND_LINE_STORE, - std::move(above_ptr)); - - run_loop.Run(); - EXPECT_TRUE(above_user_prefs.observer_added()); - EXPECT_EQ(kUpdatedValue, pref_service->GetInteger(kKey)); - pref_service->SetInteger(kKey, kInitialValue); - EXPECT_EQ(kUpdatedValue, pref_service->GetInteger(kKey)); -} - -// Check that connect calls claiming to have all read-only pref stores locally -// do not wait for those stores to be registered with the pref service. -TEST_F(PrefServiceFactoryManualPrefStoreRegistrationTest, - LocalButNotRegisteredReadOnlyStores) { - std::unique_ptr<PrefService> pref_service; - base::RunLoop run_loop; - CreateAsync( - {PrefValueStore::RECOMMENDED_STORE, PrefValueStore::COMMAND_LINE_STORE}, - run_loop.QuitClosure(), &pref_service); - - run_loop.Run(); - EXPECT_TRUE(pref_service); -} - } // namespace } // namespace prefs diff --git a/chromium/services/preferences/pref_store_consistency_unittest.cc b/chromium/services/preferences/pref_store_consistency_unittest.cc new file mode 100644 index 00000000000..ade786d80c3 --- /dev/null +++ b/chromium/services/preferences/pref_store_consistency_unittest.cc @@ -0,0 +1,916 @@ +// Copyright 2017 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 <utility> + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/values.h" +#include "components/prefs/in_memory_pref_store.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/preferences/persistent_pref_store_impl.h" +#include "services/preferences/public/cpp/dictionary_value_update.h" +#include "services/preferences/public/cpp/persistent_pref_store_client.h" +#include "services/preferences/public/cpp/scoped_pref_update.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace prefs { +namespace { + +constexpr int kInitialValue = 1; +constexpr char kKey[] = "key"; +constexpr char kNestedKey[] = "key.child"; +constexpr char kChildKey[] = "child"; +constexpr char kOtherKey[] = "other_key"; +constexpr char kNestedOtherKey[] = "key.other_key"; +constexpr char kDictionaryKey[] = "a.dictionary.pref"; + +void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {} + +struct UpdateOrAck { + std::vector<mojom::PrefUpdatePtr> updates; + bool is_ack; +}; + +struct Request { + std::string key; + std::vector<std::string> path; +}; + +struct UpdateOrRequest { + std::vector<mojom::PrefUpdatePtr> updates; + Request request; +}; + +// A client connection to a between a PersistentPrefStoreImpl and a +// PersistentPrefStoreClient that provides control over when messages between +// the two are delivered. +class PrefServiceConnection : public mojom::PrefStoreObserver, + public mojom::PersistentPrefStore { + public: + explicit PrefServiceConnection(PersistentPrefStoreImpl* pref_store) + : observer_binding_(this), pref_store_binding_(this) { + auto connection = pref_store->CreateConnection({ + kKey, kOtherKey, kDictionaryKey, + }); + observer_binding_.Bind( + std::move(connection->pref_store_connection->observer)); + connection->pref_store_connection->observer = mojo::MakeRequest(&observer_); + + pref_store_ = std::move(connection->pref_store); + pref_store_binding_.Bind(mojo::MakeRequest(&connection->pref_store)); + + pref_store_client_ = + base::MakeRefCounted<PersistentPrefStoreClient>(std::move(connection)); + PrefNotifierImpl* pref_notifier = new PrefNotifierImpl(); + auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>(); + pref_registry->RegisterIntegerPref(kKey, kInitialValue); + pref_registry->RegisterIntegerPref(kOtherKey, kInitialValue); + pref_registry->RegisterDictionaryPref(kDictionaryKey); + auto* pref_value_store = new PrefValueStore( + nullptr, nullptr, nullptr, nullptr, pref_store_client_.get(), nullptr, + pref_registry->defaults().get(), pref_notifier); + pref_service_ = base::MakeUnique<::PrefService>( + pref_notifier, pref_value_store, pref_store_client_.get(), + pref_registry.get(), base::Bind(&DoNothingHandleReadError), true); + } + + ~PrefServiceConnection() override { + observer_binding_.FlushForTesting(); + pref_store_binding_.FlushForTesting(); + EXPECT_TRUE(writes_.empty()); + EXPECT_TRUE(updates_.empty()); + } + + PrefService& pref_service() { return *pref_service_; } + + void WaitForWrites(size_t num_writes) { + if (writes_.size() >= num_writes) + return; + + expected_writes_ = num_writes; + base::RunLoop run_loop; + stop_ = run_loop.QuitClosure(); + run_loop.Run(); + } + void ForwardWrites(size_t num_writes) { + WaitForWrites(num_writes); + for (size_t i = 0; i < num_writes; ++i) { + auto& write = writes_.front(); + if (write.updates.empty()) { + pref_store_->RequestValue(write.request.key, write.request.path); + } else { + pref_store_->SetValues(std::move(write.updates)); + } + writes_.pop_front(); + } + pref_store_.FlushForTesting(); + } + void WaitForUpdates(size_t num_updates) { + if (updates_.size() >= num_updates) + return; + + expected_updates_ = num_updates; + base::RunLoop run_loop; + stop_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + void ForwardUpdates(size_t num_updates) { + WaitForUpdates(num_updates); + for (size_t i = 0; i < num_updates; ++i) { + auto& update = updates_.front(); + if (update.is_ack) { + observer_->OnPrefChangeAck(); + } else { + observer_->OnPrefsChanged(std::move(update.updates)); + } + updates_.pop_front(); + } + observer_.FlushForTesting(); + } + + // mojom::PersistentPrefStore + void SetValues(std::vector<mojom::PrefUpdatePtr> updates) override { + writes_.push_back({std::move(updates)}); + if (writes_.size() == expected_writes_) { + expected_writes_ = 0; + std::move(stop_).Run(); + } + } + void RequestValue(const std::string& key, + const std::vector<std::string>& path) override { + writes_.push_back({std::vector<mojom::PrefUpdatePtr>(), {key, path}}); + if (writes_.size() == expected_writes_) { + expected_writes_ = 0; + std::move(stop_).Run(); + } + } + void CommitPendingWrite(base::OnceClosure) override {} + void SchedulePendingLossyWrites() override {} + void ClearMutableValues() override {} + + // mojom::PrefStoreObserver + void OnPrefsChanged(std::vector<mojom::PrefUpdatePtr> updates) override { + updates_.push_back({std::move(updates), false}); + if (updates_.size() == expected_updates_) { + expected_updates_ = 0; + std::move(stop_).Run(); + } + } + void OnInitializationCompleted(bool succeeded) override { NOTREACHED(); } + void OnPrefChangeAck() override { + updates_.push_back({std::vector<mojom::PrefUpdatePtr>(), true}); + acks_.push_back(updates_.size()); + } + + private: + scoped_refptr<PersistentPrefStoreClient> pref_store_client_; + std::unique_ptr<PrefService> pref_service_; + mojom::PrefStoreObserverPtr observer_; + mojom::PersistentPrefStorePtr pref_store_; + mojo::Binding<mojom::PrefStoreObserver> observer_binding_; + mojo::Binding<mojom::PersistentPrefStore> pref_store_binding_; + + base::OnceClosure stop_; + size_t expected_writes_ = 0; + size_t expected_updates_ = 0; + + std::deque<UpdateOrRequest> writes_; + std::deque<UpdateOrAck> updates_; + std::deque<size_t> acks_; +}; + +class PersistentPrefStoreConsistencyTest : public testing::Test { + public: + void SetUp() override { + pref_store_ = base::MakeRefCounted<InMemoryPrefStore>(); + pref_store_impl_ = base::MakeUnique<PersistentPrefStoreImpl>( + pref_store_, base::BindOnce(&base::DoNothing)); + } + + PersistentPrefStore* pref_store() { return pref_store_.get(); } + + std::unique_ptr<PrefServiceConnection> CreateConnection() { + return base::MakeUnique<PrefServiceConnection>(pref_store_impl_.get()); + } + + private: + scoped_refptr<PersistentPrefStore> pref_store_; + std::unique_ptr<PersistentPrefStoreImpl> pref_store_impl_; + base::MessageLoop message_loop_; +}; + +TEST_F(PersistentPrefStoreConsistencyTest, TwoPrefs) { + pref_store()->SetValue(kKey, base::MakeUnique<base::Value>(kInitialValue), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + + EXPECT_EQ(kInitialValue, pref_service.GetInteger(kKey)); + EXPECT_EQ(kInitialValue, pref_service2.GetInteger(kKey)); + + pref_service.SetInteger(kKey, 2); + pref_service2.SetInteger(kKey, 3); + + // The writes arrive in the order: 2, 3. + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + connection->ForwardUpdates(1); + EXPECT_EQ(2, pref_service.GetInteger(kKey)); + + // This connection already has a later value. It should not move backwards in + // time. + connection2->ForwardUpdates(1); + EXPECT_EQ(3, pref_service2.GetInteger(kKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(3, pref_service.GetInteger(kKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(3, pref_service2.GetInteger(kKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, TwoSubPrefs) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 2); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + } + + // The writes arrive in the order: 2, 3. + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue two_dict; + two_dict.SetInteger(kKey, 2); + base::DictionaryValue three_dict; + three_dict.SetInteger(kKey, 3); + + connection->ForwardUpdates(1); + EXPECT_EQ(two_dict, *pref_service.GetDictionary(kDictionaryKey)); + + // This connection already has a later value. It should not move backwards in + // time. + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, DifferentSubPrefs) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 2); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kOtherKey, 3); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue two_dict; + two_dict.SetInteger(kKey, 2); + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 2); + expected_dict.SetInteger(kOtherKey, 3); + + connection->ForwardUpdates(1); + EXPECT_EQ(two_dict, *pref_service.GetDictionary(kDictionaryKey)); + + // This connection already has a later value. It should not move backwards in + // time. + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, WriteParentThenChild) { + auto initial_value = base::MakeUnique<base::DictionaryValue>(); + initial_value->SetDictionary(kDictionaryKey, + base::MakeUnique<base::DictionaryValue>()); + pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 4); + update->Clear(); + update->SetInteger(kKey, 2); + update->SetInteger(kOtherKey, 4); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue two_and_four_dict; + two_and_four_dict.SetInteger(kKey, 2); + two_and_four_dict.SetInteger(kOtherKey, 4); + base::DictionaryValue three_dict; + three_dict.SetInteger(kKey, 3); + three_dict.SetDictionary(kDictionaryKey, + base::MakeUnique<base::DictionaryValue>()); + base::DictionaryValue five_dict = three_dict; + five_dict.SetInteger(kKey, 5); + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 3); + expected_dict.SetInteger(kOtherKey, 4); + + connection->ForwardUpdates(1); + EXPECT_EQ(two_and_four_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + // Applying the other client's write would lose this client's write so is + // ignored. Instead, this client requests an updated value from the service. + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + connection2->ForwardWrites(1); + + // Perform another update while waiting for the updated value from the + // service. + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 5); + } + connection2->ForwardWrites(1); + + expected_dict.SetInteger(kKey, 5); + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + // Ack for the original write. + connection2->ForwardUpdates(1); + EXPECT_EQ(five_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + // New value that is ignored due to the second in-flight write. + connection2->ForwardUpdates(1); + // Sending another request for the value from the service. + connection2->ForwardWrites(1); + EXPECT_EQ(five_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + // Ack for the second write. + connection2->ForwardUpdates(1); + EXPECT_EQ(five_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, WriteChildThenParent) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 4); + update->Clear(); + update->SetInteger(kKey, 2); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + } + + connection2->ForwardWrites(1); + connection->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 2); + base::DictionaryValue three_dict; + three_dict.SetInteger(kKey, 3); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, WriteChildThenDeleteParent) { + pref_store()->SetValue(kDictionaryKey, + base::MakeUnique<base::DictionaryValue>(), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + pref_service.ClearPref(kDictionaryKey); + EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey)); + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + } + + connection2->ForwardWrites(1); + connection->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 2); + base::DictionaryValue three_dict; + three_dict.SetInteger(kKey, 3); + + connection->ForwardUpdates(1); + EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_FALSE(pref_service2.HasPrefPath(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, DeleteParentThenWriteChild) { + auto initial_value = base::MakeUnique<base::DictionaryValue>(); + initial_value->SetInteger(kOtherKey, 5); + pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + pref_service.ClearPref(kDictionaryKey); + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue intermediate_dict; + intermediate_dict.SetInteger(kKey, 3); + intermediate_dict.SetInteger(kOtherKey, 5); + + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 3); + + connection->ForwardUpdates(1); + EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardWrites(1); + connection2->ForwardUpdates(1); + EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, WriteParentThenDeleteChild) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 1); + update->Clear(); + update->SetInteger(kKey, 2); + update->SetInteger(kOtherKey, 4); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + update->RemovePath(kKey, nullptr); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue two_and_four_dict; + two_and_four_dict.SetInteger(kKey, 2); + two_and_four_dict.SetInteger(kOtherKey, 4); + base::DictionaryValue empty_dict; + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kOtherKey, 4); + + connection->ForwardUpdates(1); + EXPECT_EQ(two_and_four_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + // Applying the other client's write would lose this client's write so is + // ignored. Instead, this client requests an updated value from the service. + EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey)); + connection2->ForwardWrites(1); + + // Ack for the original write. + connection2->ForwardUpdates(1); + EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, DeleteChildThenWriteParent) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 4); + update->Clear(); + update->SetInteger(kKey, 2); + update->SetInteger(kOtherKey, 4); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + update->RemovePath(kKey, nullptr); + } + + connection2->ForwardWrites(1); + connection->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue empty_dict; + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 2); + expected_dict.SetInteger(kOtherKey, 4); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, ReplaceParentThenWriteChild) { + auto initial_value = base::MakeUnique<base::DictionaryValue>(); + initial_value->SetInteger(kNestedOtherKey, 5); + pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 1); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kNestedKey, 2); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue simple_dict; + simple_dict.SetInteger(kKey, 1); + base::DictionaryValue nested_dict; + nested_dict.SetInteger(kNestedKey, 2); + nested_dict.SetInteger(kNestedOtherKey, 5); + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kNestedKey, 2); + + connection->ForwardUpdates(1); + EXPECT_EQ(simple_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(nested_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardWrites(1); + connection2->ForwardUpdates(1); + EXPECT_EQ(nested_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, WriteChildThenReplaceParent) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->SetInteger(kKey, 1); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kNestedKey, 2); + } + + connection2->ForwardWrites(1); + connection->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue simple_dict; + simple_dict.SetInteger(kKey, 1); + base::DictionaryValue nested_dict; + nested_dict.SetInteger(kNestedKey, 2); + + connection->ForwardUpdates(1); + EXPECT_EQ(simple_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection->ForwardUpdates(1); + EXPECT_EQ(simple_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(nested_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(simple_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, NestedWriteParentThenChild) { + pref_store()->SetValue(kKey, base::MakeUnique<base::DictionaryValue>(), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + auto nested_dict = + update->SetDictionary(kKey, base::MakeUnique<base::DictionaryValue>()); + nested_dict->SetInteger(kChildKey, 2); + nested_dict->SetInteger(kOtherKey, 4); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kNestedKey, 3); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue two_and_four_dict; + two_and_four_dict.SetInteger(kNestedKey, 2); + two_and_four_dict.SetInteger(kNestedOtherKey, 4); + base::DictionaryValue three_dict; + three_dict.SetInteger(kNestedKey, 3); + base::DictionaryValue expected_dict = two_and_four_dict; + expected_dict.SetInteger(kNestedKey, 3); + + connection->ForwardUpdates(1); + EXPECT_EQ(two_and_four_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + // Applying the other client's write would lose this client's write so is + // ignored. Instead, this client requests an updated value from the service. + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + connection2->ForwardWrites(1); + + // Ack for the original write. + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + // Updated value from the service. + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, NestedWriteChildThenParent) { + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + auto nested_dict = + update->SetDictionary(kKey, base::MakeUnique<base::DictionaryValue>()); + nested_dict->SetInteger(kChildKey, 2); + nested_dict->SetInteger(kOtherKey, 4); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kNestedKey, 3); + } + + connection2->ForwardWrites(1); + connection->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kNestedKey, 2); + expected_dict.SetInteger(kNestedOtherKey, 4); + base::DictionaryValue three_dict; + three_dict.SetInteger(kNestedKey, 3); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + connection2->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, + DeleteParentThenWriteChildThenDeleteParent) { + auto initial_value = base::MakeUnique<base::DictionaryValue>(); + initial_value->SetInteger(kOtherKey, 5); + pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + pref_service.ClearPref(kDictionaryKey); + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kKey, 3); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue intermediate_dict; + intermediate_dict.SetInteger(kKey, 3); + intermediate_dict.SetInteger(kOtherKey, 5); + + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kKey, 3); + + connection->ForwardUpdates(1); + EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + pref_service.ClearPref(kDictionaryKey); + connection->ForwardWrites(1); + connection->ForwardUpdates(1); + EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey)); + + connection2->ForwardWrites(1); + connection2->ForwardUpdates(1); + EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_FALSE(pref_service2.HasPrefPath(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_FALSE(pref_service2.HasPrefPath(kDictionaryKey)); +} + +TEST_F(PersistentPrefStoreConsistencyTest, + NestedDeleteParentThenWriteChildThenDeleteChild) { + auto initial_value = base::MakeUnique<base::DictionaryValue>(); + initial_value->SetInteger(kNestedOtherKey, 5); + pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0); + auto connection = CreateConnection(); + auto connection2 = CreateConnection(); + auto& pref_service = connection->pref_service(); + auto& pref_service2 = connection2->pref_service(); + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->RemovePath(kKey, nullptr); + } + { + ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey); + update->SetInteger(kNestedKey, 3); + } + + connection->ForwardWrites(1); + connection2->ForwardWrites(1); + + connection->WaitForUpdates(2); + connection2->WaitForUpdates(2); + + base::DictionaryValue intermediate_dict; + intermediate_dict.SetInteger(kNestedKey, 3); + intermediate_dict.SetInteger(kNestedOtherKey, 5); + + base::DictionaryValue expected_dict; + expected_dict.SetInteger(kNestedKey, 3); + + base::DictionaryValue empty_dict; + + connection->ForwardUpdates(1); + EXPECT_EQ(empty_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection->ForwardUpdates(1); + EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey)); + + { + ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey); + update->RemovePath(kKey, nullptr); + } + connection->ForwardWrites(1); + connection->ForwardUpdates(1); + EXPECT_TRUE(pref_service.HasPrefPath(kDictionaryKey)); + EXPECT_EQ(empty_dict, *pref_service.GetDictionary(kDictionaryKey)); + + connection2->ForwardWrites(1); + connection2->ForwardUpdates(1); + EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey)); + + connection2->ForwardUpdates(1); + EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey)); +} + +} // namespace +} // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_store_impl.cc b/chromium/services/preferences/pref_store_impl.cc index efcf8f3f6ed..de0575db8ea 100644 --- a/chromium/services/preferences/public/cpp/pref_store_impl.cc +++ b/chromium/services/preferences/pref_store_impl.cc @@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/preferences/public/cpp/pref_store_impl.h" +#include "services/preferences/pref_store_impl.h" #include <memory> -#include <unordered_set> +#include <set> #include <utility> #include "base/stl_util.h" #include "base/values.h" +#include "services/preferences/public/cpp/lib/util.h" namespace prefs { class PrefStoreImpl::Observer { public: - Observer(mojom::PrefStoreObserverPtr observer, - std::unordered_set<std::string> prefs) + Observer(mojom::PrefStoreObserverPtr observer, std::set<std::string> prefs) : observer_(std::move(observer)), prefs_(std::move(prefs)) {} void OnInitializationCompleted(bool succeeded) { @@ -46,16 +46,14 @@ class PrefStoreImpl::Observer { private: mojom::PrefStoreObserverPtr observer_; - const std::unordered_set<std::string> prefs_; + const std::set<std::string> prefs_; DISALLOW_COPY_AND_ASSIGN(Observer); }; -PrefStoreImpl::PrefStoreImpl(scoped_refptr<::PrefStore> pref_store, - mojom::PrefStoreRequest request) +PrefStoreImpl::PrefStoreImpl(scoped_refptr<::PrefStore> pref_store) : backing_pref_store_(std::move(pref_store)), - backing_pref_store_initialized_(false), - binding_(this, std::move(request)) { + backing_pref_store_initialized_(false) { DCHECK(backing_pref_store_); if (backing_pref_store_->IsInitializationComplete()) OnInitializationCompleted(true); @@ -66,18 +64,6 @@ PrefStoreImpl::~PrefStoreImpl() { backing_pref_store_->RemoveObserver(this); } -// static -std::unique_ptr<PrefStoreImpl> PrefStoreImpl::Create( - mojom::PrefStoreRegistry* registry_ptr, - scoped_refptr<::PrefStore> pref_store, - PrefValueStore::PrefStoreType type) { - mojom::PrefStorePtr ptr; - auto impl = base::MakeUnique<PrefStoreImpl>(std::move(pref_store), - mojo::MakeRequest(&ptr)); - registry_ptr->Register(type, std::move(ptr)); - return impl; -} - void PrefStoreImpl::OnPrefValueChanged(const std::string& key) { auto dictionary = base::MakeUnique<base::DictionaryValue>(); const base::Value* value = nullptr; @@ -100,18 +86,19 @@ void PrefStoreImpl::OnInitializationCompleted(bool succeeded) { observer->OnInitializationCompleted(succeeded); } -void PrefStoreImpl::AddObserver( - const std::vector<std::string>& prefs_to_observe, - AddObserverCallback callback) { +mojom::PrefStoreConnectionPtr PrefStoreImpl::AddObserver( + const std::vector<std::string>& prefs_to_observe) { mojom::PrefStoreObserverPtr observer_ptr; auto request = mojo::MakeRequest(&observer_ptr); - observers_.push_back(base::MakeUnique<Observer>( - std::move(observer_ptr), - std::unordered_set<std::string>(prefs_to_observe.begin(), - prefs_to_observe.end()))); - std::move(callback).Run(mojom::PrefStoreConnection::New( - std::move(request), backing_pref_store_->GetValues(), - backing_pref_store_->IsInitializationComplete())); + std::set<std::string> observed_prefs(prefs_to_observe.begin(), + prefs_to_observe.end()); + auto result = mojom::PrefStoreConnection::New( + std::move(request), + FilterPrefs(backing_pref_store_->GetValues(), observed_prefs), + backing_pref_store_->IsInitializationComplete()); + observers_.push_back(base::MakeUnique<Observer>(std::move(observer_ptr), + std::move(observed_prefs))); + return result; } } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_store_impl.h b/chromium/services/preferences/pref_store_impl.h index 6846611c6eb..8cb970247e7 100644 --- a/chromium/services/preferences/public/cpp/pref_store_impl.h +++ b/chromium/services/preferences/pref_store_impl.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_IMPL_H_ -#define SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_IMPL_H_ +#ifndef SERVICES_PREFERENCES_PREF_STORE_IMPL_H_ +#define SERVICES_PREFERENCES_PREF_STORE_IMPL_H_ #include <string> #include <vector> @@ -19,18 +19,13 @@ namespace prefs { // Wraps an actual PrefStore implementation and exposes it as a // mojom::PrefStore interface. -class PrefStoreImpl : public ::PrefStore::Observer, public mojom::PrefStore { +class PrefStoreImpl : public ::PrefStore::Observer { public: - PrefStoreImpl(scoped_refptr<::PrefStore> pref_store, - mojom::PrefStoreRequest request); + explicit PrefStoreImpl(scoped_refptr<::PrefStore> pref_store); ~PrefStoreImpl() override; - // The created instance is registered in and owned by the - // |mojom::PrefStoreRegistry|. - static std::unique_ptr<PrefStoreImpl> Create( - mojom::PrefStoreRegistry* registry_ptr, - scoped_refptr<::PrefStore> pref_store, - PrefValueStore::PrefStoreType type); + mojom::PrefStoreConnectionPtr AddObserver( + const std::vector<std::string>& prefs_to_observe); private: class Observer; @@ -39,10 +34,6 @@ class PrefStoreImpl : public ::PrefStore::Observer, public mojom::PrefStore { void OnPrefValueChanged(const std::string& key) override; void OnInitializationCompleted(bool succeeded) override; - // prefs::mojom::PrefStore: - void AddObserver(const std::vector<std::string>& prefs_to_observe, - AddObserverCallback callback) override; - // The backing store we observer for changes. scoped_refptr<::PrefStore> backing_pref_store_; @@ -54,11 +45,9 @@ class PrefStoreImpl : public ::PrefStore::Observer, public mojom::PrefStore { // OnInitializationCompleted was called. bool backing_pref_store_initialized_; - mojo::Binding<mojom::PrefStore> binding_; - DISALLOW_COPY_AND_ASSIGN(PrefStoreImpl); }; } // namespace prefs -#endif // SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_IMPL_H_ +#endif // SERVICES_PREFERENCES_PREF_STORE_IMPL_H_ diff --git a/chromium/services/preferences/pref_store_impl_unittest.cc b/chromium/services/preferences/pref_store_impl_unittest.cc new file mode 100644 index 00000000000..6480d48b27e --- /dev/null +++ b/chromium/services/preferences/pref_store_impl_unittest.cc @@ -0,0 +1,305 @@ +// Copyright 2017 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 "services/preferences/pref_store_impl.h" + +#include <utility> + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/values.h" +#include "components/prefs/value_map_pref_store.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/preferences/public/cpp/pref_store_client.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Invoke; +using testing::WithoutArgs; + +namespace prefs { +namespace { + +class PrefStoreObserverMock : public PrefStore::Observer { + public: + MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); + MOCK_METHOD1(OnInitializationCompleted, void(bool)); +}; + +class MockPrefStore : public ValueMapPrefStore { + public: + bool IsInitializationComplete() const override { + return initialized_ && success_; + } + + void AddObserver(PrefStore::Observer* observer) override { + observers_.AddObserver(observer); + } + + void RemoveObserver(PrefStore::Observer* observer) override { + observers_.RemoveObserver(observer); + } + + void CompleteInitialization(bool success) { + initialized_ = true; + success_ = success; + for (auto& observer : observers_) { + // Some pref stores report completing initialization more than once. Test + // that additional calls are ignored. + observer.OnInitializationCompleted(success); + observer.OnInitializationCompleted(success); + } + } + + void SetValue(const std::string& key, + std::unique_ptr<base::Value> value, + uint32_t flags) override { + ValueMapPrefStore::SetValue(key, std::move(value), flags); + for (auto& observer : observers_) { + observer.OnPrefValueChanged(key); + } + } + + private: + ~MockPrefStore() override = default; + + bool initialized_ = false; + bool success_ = false; + base::ObserverList<PrefStore::Observer, true> observers_; +}; + +constexpr char kKey[] = "path.to.key"; +constexpr char kOtherKey[] = "path.to.other_key"; + +void ExpectInitializationComplete(PrefStore* pref_store, bool success) { + PrefStoreObserverMock observer; + pref_store->AddObserver(&observer); + base::RunLoop run_loop; + EXPECT_CALL(observer, OnPrefValueChanged("")).Times(0); + EXPECT_CALL(observer, OnInitializationCompleted(success)) + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); }))); + run_loop.Run(); + pref_store->RemoveObserver(&observer); +} + +void ExpectPrefChange(PrefStore* pref_store, base::StringPiece key) { + PrefStoreObserverMock observer; + pref_store->AddObserver(&observer); + base::RunLoop run_loop; + EXPECT_CALL(observer, OnPrefValueChanged(key.as_string())) + .WillOnce(WithoutArgs(Invoke([&run_loop]() { run_loop.Quit(); }))); + run_loop.Run(); + pref_store->RemoveObserver(&observer); +} + +class PrefStoreImplTest : public testing::Test { + public: + PrefStoreImplTest() = default; + + // testing::Test: + void TearDown() override { + pref_store_ = nullptr; + base::RunLoop().RunUntilIdle(); + impl_.reset(); + base::RunLoop().RunUntilIdle(); + } + + void CreateImpl( + scoped_refptr<PrefStore> backing_pref_store, + std::vector<std::string> observed_prefs = std::vector<std::string>()) { + impl_ = base::MakeUnique<PrefStoreImpl>(std::move(backing_pref_store)); + + if (observed_prefs.empty()) + observed_prefs.insert(observed_prefs.end(), {kKey, kOtherKey}); + pref_store_ = make_scoped_refptr( + new PrefStoreClient(impl_->AddObserver(observed_prefs))); + } + + PrefStore* pref_store() { return pref_store_.get(); } + + private: + base::MessageLoop message_loop_; + + std::unique_ptr<PrefStoreImpl> impl_; + + scoped_refptr<PrefStore> pref_store_; + + DISALLOW_COPY_AND_ASSIGN(PrefStoreImplTest); +}; + +TEST_F(PrefStoreImplTest, InitializationSuccess) { + auto backing_pref_store = make_scoped_refptr(new MockPrefStore()); + backing_pref_store->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0); + CreateImpl(backing_pref_store); + EXPECT_FALSE(pref_store()->IsInitializationComplete()); + + backing_pref_store->CompleteInitialization(true); + ExpectInitializationComplete(pref_store(), true); + EXPECT_TRUE(pref_store()->IsInitializationComplete()); +} + +TEST_F(PrefStoreImplTest, InitializationFailure) { + auto backing_pref_store = make_scoped_refptr(new MockPrefStore()); + backing_pref_store->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0); + CreateImpl(backing_pref_store); + EXPECT_FALSE(pref_store()->IsInitializationComplete()); + + backing_pref_store->CompleteInitialization(false); + ExpectInitializationComplete(pref_store(), false); + + // TODO(sammc): Should IsInitializationComplete() return false? + EXPECT_TRUE(pref_store()->IsInitializationComplete()); +} + +TEST_F(PrefStoreImplTest, ValueChangesBeforeInitializationCompletes) { + auto backing_pref_store = make_scoped_refptr(new MockPrefStore()); + CreateImpl(backing_pref_store); + EXPECT_FALSE(pref_store()->IsInitializationComplete()); + + const base::Value value("value"); + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0); + backing_pref_store->CompleteInitialization(true); + + // The update occurs before initialization has completed, so should not + // trigger notifications to client observers, but the value should be + // observable once initialization completes. + ExpectInitializationComplete(pref_store(), true); + EXPECT_TRUE(pref_store()->IsInitializationComplete()); + + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(value.Equals(output)); +} + +TEST_F(PrefStoreImplTest, InitialValue) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + const base::Value value("value"); + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0); + CreateImpl(backing_pref_store); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(value.Equals(output)); +} + +TEST_F(PrefStoreImplTest, InitialValueWithoutPathExpansion) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + base::DictionaryValue dict; + dict.SetStringWithoutPathExpansion(kKey, "value"); + backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0); + CreateImpl(backing_pref_store); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(dict.Equals(output)); +} + +TEST_F(PrefStoreImplTest, WriteObservedByClient) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + CreateImpl(backing_pref_store); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + + const base::Value value("value"); + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0); + + ExpectPrefChange(pref_store(), kKey); + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(value.Equals(output)); +} + +TEST_F(PrefStoreImplTest, WriteToUnregisteredPrefNotObservedByClient) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + CreateImpl(backing_pref_store, {kKey}); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + + backing_pref_store->SetValue(kOtherKey, base::MakeUnique<base::Value>(123), + 0); + backing_pref_store->SetValue(kKey, base::MakeUnique<base::Value>("value"), 0); + + ExpectPrefChange(pref_store(), kKey); + EXPECT_FALSE(pref_store()->GetValue(kOtherKey, nullptr)); +} + +TEST_F(PrefStoreImplTest, WriteWithoutPathExpansionObservedByClient) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + CreateImpl(backing_pref_store); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + + base::DictionaryValue dict; + dict.SetStringWithoutPathExpansion(kKey, "value"); + backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0); + + ExpectPrefChange(pref_store(), kKey); + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(dict.Equals(output)); +} + +TEST_F(PrefStoreImplTest, RemoveObservedByClient) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + const base::Value value("value"); + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0); + CreateImpl(backing_pref_store); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(value.Equals(output)); + backing_pref_store->RemoveValue(kKey, 0); + + // This should be a no-op and shouldn't trigger a notification for the other + // client. + backing_pref_store->RemoveValue(kKey, 0); + + ExpectPrefChange(pref_store(), kKey); + EXPECT_FALSE(pref_store()->GetValue(kKey, &output)); +} + +TEST_F(PrefStoreImplTest, RemoveOfUnregisteredPrefNotObservedByClient) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + const base::Value value("value"); + backing_pref_store->SetValue(kKey, value.CreateDeepCopy(), 0); + backing_pref_store->SetValue(kOtherKey, value.CreateDeepCopy(), 0); + CreateImpl(backing_pref_store, {kKey}); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + + backing_pref_store->RemoveValue(kOtherKey, 0); + backing_pref_store->RemoveValue(kKey, 0); + + ExpectPrefChange(pref_store(), kKey); +} + +TEST_F(PrefStoreImplTest, RemoveWithoutPathExpansionObservedByOtherClient) { + auto backing_pref_store = make_scoped_refptr(new ValueMapPrefStore()); + base::DictionaryValue dict; + dict.SetStringWithoutPathExpansion(kKey, "value"); + backing_pref_store->SetValue(kKey, dict.CreateDeepCopy(), 0); + CreateImpl(backing_pref_store); + ASSERT_TRUE(pref_store()->IsInitializationComplete()); + + const base::Value* output = nullptr; + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + EXPECT_TRUE(dict.Equals(output)); + + base::Value* mutable_value = nullptr; + dict.SetStringWithoutPathExpansion(kKey, "value"); + ASSERT_TRUE(backing_pref_store->GetMutableValue(kKey, &mutable_value)); + base::DictionaryValue* mutable_dict = nullptr; + ASSERT_TRUE(mutable_value->GetAsDictionary(&mutable_dict)); + mutable_dict->RemoveWithoutPathExpansion(kKey, nullptr); + backing_pref_store->ReportValueChanged(kKey, 0); + + ExpectPrefChange(pref_store(), kKey); + ASSERT_TRUE(pref_store()->GetValue(kKey, &output)); + const base::DictionaryValue* dict_value = nullptr; + ASSERT_TRUE(output->GetAsDictionary(&dict_value)); + EXPECT_TRUE(dict_value->empty()); +} + +} // namespace +} // namespace prefs diff --git a/chromium/services/preferences/pref_store_manager_impl.cc b/chromium/services/preferences/pref_store_manager_impl.cc index 288e6496e4a..1e24c372e52 100644 --- a/chromium/services/preferences/pref_store_manager_impl.cc +++ b/chromium/services/preferences/pref_store_manager_impl.cc @@ -10,133 +10,102 @@ #include "base/memory/ref_counted.h" #include "base/stl_util.h" #include "base/threading/sequenced_worker_pool.h" -#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_registry.h" #include "components/prefs/pref_value_store.h" #include "mojo/public/cpp/bindings/interface_request.h" -#include "services/preferences/persistent_pref_store_factory.h" #include "services/preferences/persistent_pref_store_impl.h" -#include "services/preferences/public/cpp/pref_store_impl.h" +#include "services/preferences/pref_store_impl.h" #include "services/preferences/scoped_pref_connection_builder.h" +#include "services/preferences/shared_pref_registry.h" #include "services/service_manager/public/cpp/bind_source_info.h" +#include "services/service_manager/public/cpp/service_context.h" namespace prefs { +class PrefStoreManagerImpl::ConnectorConnection + : public mojom::PrefStoreConnector { + public: + ConnectorConnection(PrefStoreManagerImpl* owner, + const service_manager::BindSourceInfo& source_info) + : owner_(owner), source_info_(source_info) {} + + void Connect( + mojom::PrefRegistryPtr pref_registry, + ConnectCallback callback) override { + auto connection = owner_->shared_pref_registry_->CreateConnectionBuilder( + std::move(pref_registry), source_info_.identity, std::move(callback)); + if (owner_->persistent_pref_store_ && + owner_->persistent_pref_store_->initialized()) { + connection->ProvidePersistentPrefStore( + owner_->persistent_pref_store_.get()); + } else { + owner_->pending_persistent_connections_.push_back(connection); + } + if (owner_->incognito_persistent_pref_store_underlay_) { + if (owner_->incognito_persistent_pref_store_underlay_->initialized()) { + connection->ProvideIncognitoPersistentPrefStoreUnderlay( + owner_->incognito_persistent_pref_store_underlay_.get()); + } else { + owner_->pending_persistent_incognito_connections_.push_back(connection); + } + } + connection->ProvidePrefStoreConnections(owner_->read_only_pref_stores_); + } + + private: + PrefStoreManagerImpl* const owner_; + const service_manager::BindSourceInfo source_info_; +}; + PrefStoreManagerImpl::PrefStoreManagerImpl( - std::set<PrefValueStore::PrefStoreType> expected_pref_stores, - scoped_refptr<base::SequencedWorkerPool> worker_pool) - : expected_pref_stores_(std::move(expected_pref_stores)), - init_binding_(this), - defaults_(new DefaultPrefStore), - defaults_wrapper_(base::MakeUnique<PrefStoreImpl>( - defaults_, - mojo::MakeRequest(&pref_store_ptrs_[PrefValueStore::DEFAULT_STORE]))), - worker_pool_(std::move(worker_pool)) { - DCHECK( - base::ContainsValue(expected_pref_stores_, PrefValueStore::USER_STORE) && - base::ContainsValue(expected_pref_stores_, PrefValueStore::DEFAULT_STORE)) - << "expected_pref_stores must always include PrefValueStore::USER_STORE " - "and PrefValueStore::DEFAULT_STORE."; - // The user store is not actually connected to in the implementation, but - // accessed directly. - expected_pref_stores_.erase(PrefValueStore::USER_STORE); + PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PersistentPrefStore* user_prefs, + PersistentPrefStore* incognito_user_prefs_underlay, + PrefStore* recommended_prefs, + PrefRegistry* pref_registry) + : shared_pref_registry_(base::MakeUnique<SharedPrefRegistry>( + make_scoped_refptr(pref_registry))), + weak_factory_(this) { + // This store is done in-process so it's already "registered": registry_.AddInterface<prefs::mojom::PrefStoreConnector>( base::Bind(&PrefStoreManagerImpl::BindPrefStoreConnectorRequest, base::Unretained(this))); - registry_.AddInterface<prefs::mojom::PrefStoreRegistry>( - base::Bind(&PrefStoreManagerImpl::BindPrefStoreRegistryRequest, - base::Unretained(this))); - registry_.AddInterface<prefs::mojom::PrefServiceControl>( - base::Bind(&PrefStoreManagerImpl::BindPrefServiceControlRequest, - base::Unretained(this))); + persistent_pref_store_ = base::MakeUnique<PersistentPrefStoreImpl>( + make_scoped_refptr(user_prefs), + base::BindOnce(&PrefStoreManagerImpl::OnPersistentPrefStoreReady, + base::Unretained(this))); + if (incognito_user_prefs_underlay) { + incognito_persistent_pref_store_underlay_ = + base::MakeUnique<PersistentPrefStoreImpl>( + make_scoped_refptr(user_prefs), + base::BindOnce( + &PrefStoreManagerImpl::OnIncognitoPersistentPrefStoreReady, + base::Unretained(this))); + } + RegisterPrefStore(PrefValueStore::MANAGED_STORE, managed_prefs); + RegisterPrefStore(PrefValueStore::SUPERVISED_USER_STORE, + supervised_user_prefs); + RegisterPrefStore(PrefValueStore::EXTENSION_STORE, extension_prefs); + RegisterPrefStore(PrefValueStore::COMMAND_LINE_STORE, command_line_prefs); + RegisterPrefStore(PrefValueStore::RECOMMENDED_STORE, recommended_prefs); } PrefStoreManagerImpl::~PrefStoreManagerImpl() = default; -void PrefStoreManagerImpl::Register(PrefValueStore::PrefStoreType type, - mojom::PrefStorePtr pref_store_ptr) { - if (expected_pref_stores_.count(type) == 0) { - LOG(WARNING) << "Not registering unexpected pref store: " << type; - return; - } - DVLOG(1) << "Registering pref store: " << type; - pref_store_ptr.set_connection_error_handler( - base::Bind(&PrefStoreManagerImpl::OnPrefStoreDisconnect, - base::Unretained(this), type)); - auto it = pending_connections_.find(type); - if (it != pending_connections_.end()) { - for (const auto& connection : it->second) - connection->ProvidePrefStoreConnection(type, pref_store_ptr.get()); - pending_connections_.erase(it); - } - - const bool success = - pref_store_ptrs_.insert(std::make_pair(type, std::move(pref_store_ptr))) - .second; - DCHECK(success) << "The same pref store registered twice: " << type; -} - -void PrefStoreManagerImpl::Connect( - mojom::PrefRegistryPtr pref_registry, - const std::vector<PrefValueStore::PrefStoreType>& already_connected_types, - ConnectCallback callback) { - std::set<PrefValueStore::PrefStoreType> required_remote_types; - for (auto type : expected_pref_stores_) { - if (!base::ContainsValue(already_connected_types, type)) { - required_remote_types.insert(type); - } - } - auto connection = base::MakeRefCounted<ScopedPrefConnectionBuilder>( - std::move(pref_registry->registrations), std::move(required_remote_types), - std::move(callback)); - if (persistent_pref_store_ && persistent_pref_store_->initialized()) { - connection->ProvidePersistentPrefStore(persistent_pref_store_.get()); - } else { - pending_persistent_connections_.push_back(connection); - } - auto remaining_remote_types = - connection->ProvidePrefStoreConnections(pref_store_ptrs_); - for (auto type : remaining_remote_types) { - pending_connections_[type].push_back(connection); - } +base::OnceClosure PrefStoreManagerImpl::ShutDownClosure() { + return base::BindOnce(&PrefStoreManagerImpl::ShutDown, + weak_factory_.GetWeakPtr()); } void PrefStoreManagerImpl::BindPrefStoreConnectorRequest( - const service_manager::BindSourceInfo& source_info, - prefs::mojom::PrefStoreConnectorRequest request) { - connector_bindings_.AddBinding(this, std::move(request)); -} - -void PrefStoreManagerImpl::BindPrefStoreRegistryRequest( - const service_manager::BindSourceInfo& source_info, - prefs::mojom::PrefStoreRegistryRequest request) { - registry_bindings_.AddBinding(this, std::move(request)); -} - -void PrefStoreManagerImpl::BindPrefServiceControlRequest( - const service_manager::BindSourceInfo& source_info, - prefs::mojom::PrefServiceControlRequest request) { - if (init_binding_.is_bound()) { - LOG(ERROR) - << "Pref service received unexpected control interface connection from " - << source_info.identity.name(); - return; - } - - init_binding_.Bind(std::move(request)); -} - -void PrefStoreManagerImpl::Init( - mojom::PersistentPrefStoreConfigurationPtr configuration) { - DCHECK(!persistent_pref_store_); - - persistent_pref_store_ = CreatePersistentPrefStore( - std::move(configuration), worker_pool_.get(), - base::Bind(&PrefStoreManagerImpl::OnPersistentPrefStoreReady, - base::Unretained(this))); - DCHECK(persistent_pref_store_); - if (persistent_pref_store_->initialized()) { - OnPersistentPrefStoreReady(); - } + prefs::mojom::PrefStoreConnectorRequest request, + const service_manager::BindSourceInfo& source_info) { + connector_bindings_.AddBinding( + base::MakeUnique<ConnectorConnection>(this, source_info), + std::move(request)); } void PrefStoreManagerImpl::OnStart() {} @@ -145,14 +114,8 @@ void PrefStoreManagerImpl::OnBindInterface( const service_manager::BindSourceInfo& source_info, const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) { - registry_.BindInterface(source_info, interface_name, - std::move(interface_pipe)); -} - -void PrefStoreManagerImpl::OnPrefStoreDisconnect( - PrefValueStore::PrefStoreType type) { - DVLOG(1) << "Deregistering pref store: " << type; - pref_store_ptrs_.erase(type); + registry_.BindInterface(interface_name, std::move(interface_pipe), + source_info); } void PrefStoreManagerImpl::OnPersistentPrefStoreReady() { @@ -162,4 +125,27 @@ void PrefStoreManagerImpl::OnPersistentPrefStoreReady() { pending_persistent_connections_.clear(); } +void PrefStoreManagerImpl::OnIncognitoPersistentPrefStoreReady() { + DVLOG(1) << "Incognito PersistentPrefStore ready"; + for (const auto& connection : pending_persistent_connections_) + connection->ProvideIncognitoPersistentPrefStoreUnderlay( + incognito_persistent_pref_store_underlay_.get()); + pending_persistent_incognito_connections_.clear(); +} + +void PrefStoreManagerImpl::RegisterPrefStore(PrefValueStore::PrefStoreType type, + PrefStore* pref_store) { + if (!pref_store) + return; + + read_only_pref_stores_.emplace( + type, base::MakeUnique<PrefStoreImpl>(make_scoped_refptr(pref_store))); +} + +void PrefStoreManagerImpl::ShutDown() { + read_only_pref_stores_.clear(); + persistent_pref_store_.reset(); + context()->QuitNow(); +} + } // namespace prefs diff --git a/chromium/services/preferences/pref_store_manager_impl.h b/chromium/services/preferences/pref_store_manager_impl.h index c1b0ae83ebe..fa5ea19ba99 100644 --- a/chromium/services/preferences/pref_store_manager_impl.h +++ b/chromium/services/preferences/pref_store_manager_impl.h @@ -15,17 +15,19 @@ #include "base/memory/ref_counted.h" #include "components/prefs/pref_value_store.h" #include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/strong_binding_set.h" #include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/cpp/service.h" -class DefaultPrefStore; +class PrefRegistry; -namespace base { -class SequencedWorkerPool; +namespace service_manager { +struct BindSourceInfo; } namespace prefs { +class SharedPrefRegistry; class PersistentPrefStoreImpl; class PrefStoreImpl; class ScopedPrefConnectionBuilder; @@ -34,46 +36,26 @@ class ScopedPrefConnectionBuilder; // and the pref stores that store those preferences. Pref stores use the // |PrefStoreRegistry| interface to register themselves with the manager and // clients use the |PrefStoreConnector| interface to connect to these stores. -class PrefStoreManagerImpl : public mojom::PrefStoreRegistry, - public mojom::PrefStoreConnector, - public mojom::PrefServiceControl, - public service_manager::Service { +class PrefStoreManagerImpl : public service_manager::Service { public: - // Only replies to Connect calls when all |expected_pref_stores| have - // registered. |expected_pref_stores| must contain - // PrefValueStore::DEFAULT_STORE and PrefValueStore::USER_STORE for - // consistency, as the service always registers these - // internally. |worker_pool| is used for any I/O performed by the service. - PrefStoreManagerImpl( - std::set<PrefValueStore::PrefStoreType> expected_pref_stores, - scoped_refptr<base::SequencedWorkerPool> worker_pool); + PrefStoreManagerImpl(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PersistentPrefStore* user_prefs, + PersistentPrefStore* incognito_user_prefs_underlay, + PrefStore* recommended_prefs, + PrefRegistry* pref_registry); ~PrefStoreManagerImpl() override; + base::OnceClosure ShutDownClosure(); + private: - // mojom::PrefStoreRegistry: - void Register(PrefValueStore::PrefStoreType type, - mojom::PrefStorePtr pref_store_ptr) override; - - // mojom::PrefStoreConnector: |already_connected_types| must not include - // PrefValueStore::DEFAULT_STORE and PrefValueStore::USER_STORE as these must - // always be accessed through the service. - void Connect( - mojom::PrefRegistryPtr pref_registry, - const std::vector<PrefValueStore::PrefStoreType>& already_connected_types, - ConnectCallback callback) override; + class ConnectorConnection; void BindPrefStoreConnectorRequest( - const service_manager::BindSourceInfo& source_info, - prefs::mojom::PrefStoreConnectorRequest request); - void BindPrefStoreRegistryRequest( - const service_manager::BindSourceInfo& source_info, - prefs::mojom::PrefStoreRegistryRequest request); - void BindPrefServiceControlRequest( - const service_manager::BindSourceInfo& source_info, - prefs::mojom::PrefServiceControlRequest request); - - // PrefServiceControl: - void Init(mojom::PersistentPrefStoreConfigurationPtr configuration) override; + prefs::mojom::PrefStoreConnectorRequest request, + const service_manager::BindSourceInfo& source_info); // service_manager::Service: void OnStart() override; @@ -81,39 +63,35 @@ class PrefStoreManagerImpl : public mojom::PrefStoreRegistry, const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) override; - // Called when a PrefStore previously registered using |Register| disconnects. - void OnPrefStoreDisconnect(PrefValueStore::PrefStoreType type); - void OnPersistentPrefStoreReady(); + void OnIncognitoPersistentPrefStoreReady(); + + void RegisterPrefStore(PrefValueStore::PrefStoreType type, + PrefStore* pref_store); - // PrefStores that need to register before replying to any Connect calls. This - // does not include the PersistentPrefStore, which is handled separately. - std::set<PrefValueStore::PrefStoreType> expected_pref_stores_; + void ShutDown(); - // Registered pref stores. - std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStorePtr> - pref_store_ptrs_; + std::unordered_map<PrefValueStore::PrefStoreType, + std::unique_ptr<PrefStoreImpl>> + read_only_pref_stores_; - mojo::BindingSet<mojom::PrefStoreConnector> connector_bindings_; - mojo::BindingSet<mojom::PrefStoreRegistry> registry_bindings_; + mojo::StrongBindingSet<mojom::PrefStoreConnector> connector_bindings_; std::unique_ptr<PersistentPrefStoreImpl> persistent_pref_store_; - mojo::Binding<mojom::PrefServiceControl> init_binding_; + std::unique_ptr<PersistentPrefStoreImpl> + incognito_persistent_pref_store_underlay_; - const scoped_refptr<DefaultPrefStore> defaults_; - const std::unique_ptr<PrefStoreImpl> defaults_wrapper_; + const std::unique_ptr<SharedPrefRegistry> shared_pref_registry_; - // The same |ScopedPrefConnectionBuilder| instance may appear multiple times - // in |pending_connections_|, once per type of pref store it's waiting for, - // and at most once in |pending_persistent_connections_|. - std::unordered_map<PrefValueStore::PrefStoreType, - std::vector<scoped_refptr<ScopedPrefConnectionBuilder>>> - pending_connections_; std::vector<scoped_refptr<ScopedPrefConnectionBuilder>> pending_persistent_connections_; + std::vector<scoped_refptr<ScopedPrefConnectionBuilder>> + pending_persistent_incognito_connections_; - const scoped_refptr<base::SequencedWorkerPool> worker_pool_; + service_manager::BinderRegistryWithArgs< + const service_manager::BindSourceInfo&> + registry_; - service_manager::BinderRegistry registry_; + base::WeakPtrFactory<PrefStoreManagerImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(PrefStoreManagerImpl); }; diff --git a/chromium/services/preferences/public/cpp/BUILD.gn b/chromium/services/preferences/public/cpp/BUILD.gn index 53ead75b2c5..fb4d1ce6c26 100644 --- a/chromium/services/preferences/public/cpp/BUILD.gn +++ b/chromium/services/preferences/public/cpp/BUILD.gn @@ -12,14 +12,10 @@ source_set("cpp") { "pref_registry_serializer.h", "pref_service_factory.cc", "pref_service_factory.h", - "pref_store_adapter.cc", - "pref_store_adapter.h", "pref_store_client.cc", "pref_store_client.h", "pref_store_client_mixin.cc", "pref_store_client_mixin.h", - "pref_store_impl.cc", - "pref_store_impl.h", "scoped_pref_update.cc", "scoped_pref_update.h", ] @@ -40,11 +36,14 @@ source_set("cpp") { source_set("service_main") { deps = [ "//base", + "//components/keyed_service/core", "//components/prefs", "//services/preferences", "//services/service_manager/public/cpp", ] sources = [ + "in_process_service_factory.cc", + "in_process_service_factory.h", "pref_service_main.cc", "pref_service_main.h", ] diff --git a/chromium/services/preferences/public/cpp/DEPS b/chromium/services/preferences/public/cpp/DEPS index 45e227dd6d0..afbd4250414 100644 --- a/chromium/services/preferences/public/cpp/DEPS +++ b/chromium/services/preferences/public/cpp/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/keyed_service/core", "+components/prefs", "-services/preferences", "+services/preferences/public", diff --git a/chromium/services/preferences/public/cpp/dictionary_value_update.cc b/chromium/services/preferences/public/cpp/dictionary_value_update.cc index 513fe611bc8..afd7ae85450 100644 --- a/chromium/services/preferences/public/cpp/dictionary_value_update.cc +++ b/chromium/services/preferences/public/cpp/dictionary_value_update.cc @@ -79,8 +79,8 @@ std::unique_ptr<DictionaryValueUpdate> DictionaryValueUpdate::SetDictionary( base::StringPiece path, std::unique_ptr<base::DictionaryValue> in_value) { RecordPath(path); - base::DictionaryValue* dictionary_value = in_value.get(); - value_->Set(path, std::move(in_value)); + base::DictionaryValue* dictionary_value = + value_->SetDictionary(path, std::move(in_value)); return base::MakeUnique<DictionaryValueUpdate>( report_update_, dictionary_value, ConcatPath(path_, path)); @@ -133,8 +133,8 @@ DictionaryValueUpdate::SetDictionaryWithoutPathExpansion( base::StringPiece path, std::unique_ptr<base::DictionaryValue> in_value) { RecordKey(path); - base::DictionaryValue* dictionary_value = in_value.get(); - value_->SetWithoutPathExpansion(path, std::move(in_value)); + base::DictionaryValue* dictionary_value = + value_->SetDictionaryWithoutPathExpansion(path, std::move(in_value)); std::vector<std::string> full_path = path_; full_path.push_back(path.as_string()); diff --git a/chromium/services/preferences/public/cpp/in_process_service_factory.cc b/chromium/services/preferences/public/cpp/in_process_service_factory.cc new file mode 100644 index 00000000000..4127d29d832 --- /dev/null +++ b/chromium/services/preferences/public/cpp/in_process_service_factory.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2017 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 "services/preferences/public/cpp/in_process_service_factory.h" + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_registry.h" +#include "services/preferences/public/cpp/pref_service_main.h" + +namespace prefs { +namespace { + +static std::unique_ptr<service_manager::Service> WeakCreatePrefService( + base::WeakPtr<InProcessPrefServiceFactory> weak_factory) { + if (!weak_factory) + return base::MakeUnique<service_manager::Service>(); + + return weak_factory->CreatePrefService(); +} + +} // namespace + +// Registers all provided |PrefStore|s with the pref service. The pref stores +// remaining registered for the life time of |this|. +class InProcessPrefServiceFactory::RegisteringDelegate + : public PrefValueStore::Delegate { + public: + RegisteringDelegate(base::WeakPtr<InProcessPrefServiceFactory> factory) + : factory_(std::move(factory)) {} + + // PrefValueStore::Delegate: + void Init(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier) override { + if (!factory_) + return; + + factory_->managed_prefs_ = make_scoped_refptr(managed_prefs); + factory_->supervised_user_prefs_ = + make_scoped_refptr(supervised_user_prefs); + factory_->extension_prefs_ = make_scoped_refptr(extension_prefs); + factory_->command_line_prefs_ = make_scoped_refptr(command_line_prefs); + factory_->user_prefs_ = + make_scoped_refptr(static_cast<PersistentPrefStore*>(user_prefs)); + factory_->recommended_prefs_ = make_scoped_refptr(recommended_prefs); + } + + void InitIncognitoUnderlay( + PersistentPrefStore* incognito_user_prefs_underlay) override { + factory_->incognito_user_prefs_underlay_ = + make_scoped_refptr(incognito_user_prefs_underlay); + } + + void InitPrefRegistry(PrefRegistry* pref_registry) override { + factory_->pref_registry_ = make_scoped_refptr(pref_registry); + } + + void UpdateCommandLinePrefStore(PrefStore* command_line_prefs) override { + // TODO(tibell): Once we have a way to deregister stores, do so here. At the + // moment only local state uses this and local state doesn't use the pref + // service yet. + } + + private: + base::WeakPtr<InProcessPrefServiceFactory> factory_; + + DISALLOW_COPY_AND_ASSIGN(RegisteringDelegate); +}; + +InProcessPrefServiceFactory::InProcessPrefServiceFactory() + : weak_factory_(this) {} + +InProcessPrefServiceFactory::~InProcessPrefServiceFactory() { + if (quit_closure_) + std::move(quit_closure_).Run(); +} + +std::unique_ptr<PrefValueStore::Delegate> +InProcessPrefServiceFactory::CreateDelegate() { + return base::MakeUnique<RegisteringDelegate>(weak_factory_.GetWeakPtr()); +} + +base::Callback<std::unique_ptr<service_manager::Service>()> +InProcessPrefServiceFactory::CreatePrefServiceFactory() { + return base::Bind(&WeakCreatePrefService, weak_factory_.GetWeakPtr()); +} + +std::unique_ptr<service_manager::Service> +InProcessPrefServiceFactory::CreatePrefService() { + auto result = prefs::CreatePrefService( + managed_prefs_.get(), supervised_user_prefs_.get(), + extension_prefs_.get(), command_line_prefs_.get(), user_prefs_.get(), + incognito_user_prefs_underlay_.get(), recommended_prefs_.get(), + pref_registry_.get()); + quit_closure_ = std::move(result.second); + return std::move(result.first); +} + +} // namespace prefs diff --git a/chromium/services/preferences/public/cpp/in_process_service_factory.h b/chromium/services/preferences/public/cpp/in_process_service_factory.h new file mode 100644 index 00000000000..10bea7d9195 --- /dev/null +++ b/chromium/services/preferences/public/cpp/in_process_service_factory.h @@ -0,0 +1,56 @@ +// Copyright 2017 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 SERVICES_PREFERENCES_PUBLIC_CPP_IN_PROCESS_SERVICE_FACTORY_H_ +#define SERVICES_PREFERENCES_PUBLIC_CPP_IN_PROCESS_SERVICE_FACTORY_H_ + +#include <memory> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_value_store.h" +#include "services/service_manager/public/cpp/service.h" + +class PersistentPrefStore; +class PrefRegistry; +class PrefStore; + +namespace prefs { + +class InProcessPrefServiceFactory : public KeyedService { + public: + InProcessPrefServiceFactory(); + ~InProcessPrefServiceFactory() override; + + std::unique_ptr<PrefValueStore::Delegate> CreateDelegate(); + + base::Callback<std::unique_ptr<service_manager::Service>()> + CreatePrefServiceFactory(); + + std::unique_ptr<service_manager::Service> CreatePrefService(); + + private: + class RegisteringDelegate; + + scoped_refptr<PrefStore> managed_prefs_; + scoped_refptr<PrefStore> supervised_user_prefs_; + scoped_refptr<PrefStore> extension_prefs_; + scoped_refptr<PrefStore> command_line_prefs_; + scoped_refptr<PersistentPrefStore> user_prefs_; + scoped_refptr<PersistentPrefStore> incognito_user_prefs_underlay_; + scoped_refptr<PrefStore> recommended_prefs_; + scoped_refptr<PrefRegistry> pref_registry_; + + base::OnceClosure quit_closure_; + + base::WeakPtrFactory<InProcessPrefServiceFactory> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(InProcessPrefServiceFactory); +}; + +} // namespace prefs + +#endif // SERVICES_PREFERENCES_PUBLIC_CPP_IN_PROCESS_SERVICE_FACTORY_H_ diff --git a/chromium/services/preferences/public/cpp/lib/util.cc b/chromium/services/preferences/public/cpp/lib/util.cc index dcd9f32bfc4..493b8aab23f 100644 --- a/chromium/services/preferences/public/cpp/lib/util.cc +++ b/chromium/services/preferences/public/cpp/lib/util.cc @@ -10,6 +10,62 @@ #include "base/values.h" namespace prefs { +namespace { + +enum class MatchType { kNo, kExact, kPrefix }; + +MatchType MatchPref(base::StringPiece string, base::StringPiece prefix) { + if (string.substr(0, prefix.size()) != prefix) + return MatchType::kNo; + + if (string.size() == prefix.size()) + return MatchType::kExact; + + return string[prefix.size()] == '.' ? MatchType::kPrefix : MatchType::kNo; +} + +std::unique_ptr<base::DictionaryValue> FilterPrefsImpl( + std::unique_ptr<base::DictionaryValue> prefs, + const std::set<std::string>& observed_prefs, + const std::string& prefix) { + if (!prefs) + return nullptr; + + auto filtered_value = base::MakeUnique<base::DictionaryValue>(); + std::string full_path = prefix; + for (auto& pref : *prefs) { + full_path.resize(prefix.size()); + if (full_path.empty()) { + full_path = pref.first; + } else { + full_path.reserve(full_path.size() + 1 + pref.first.size()); + full_path += "."; + full_path += pref.first; + } + auto it = observed_prefs.lower_bound(full_path); + if (it == observed_prefs.end()) + continue; + + auto result = MatchPref(*it, full_path); + switch (result) { + case MatchType::kNo: + break; + case MatchType::kExact: + filtered_value->Set(pref.first, std::move(pref.second)); + break; + case MatchType::kPrefix: + auto filtered_subpref = + FilterPrefsImpl(base::DictionaryValue::From(std::move(pref.second)), + observed_prefs, full_path); + if (filtered_subpref) + filtered_value->Set(pref.first, std::move(filtered_subpref)); + break; + } + } + return filtered_value; +} + +} // namespace void SetValue(base::DictionaryValue* dictionary_value, const std::vector<base::StringPiece>& path_components, @@ -17,11 +73,8 @@ void SetValue(base::DictionaryValue* dictionary_value, for (size_t i = 0; i < path_components.size() - 1; ++i) { if (!dictionary_value->GetDictionaryWithoutPathExpansion( path_components[i], &dictionary_value)) { - auto new_dict_value_owner = base::MakeUnique<base::DictionaryValue>(); - auto* new_dict_value = new_dict_value_owner.get(); - dictionary_value->SetWithoutPathExpansion( - path_components[i], std::move(new_dict_value_owner)); - dictionary_value = new_dict_value; + dictionary_value = dictionary_value->SetDictionaryWithoutPathExpansion( + path_components[i], base::MakeUnique<base::DictionaryValue>()); } } const auto& key = path_components.back(); @@ -31,4 +84,10 @@ void SetValue(base::DictionaryValue* dictionary_value, dictionary_value->RemoveWithoutPathExpansion(key, nullptr); } +std::unique_ptr<base::DictionaryValue> FilterPrefs( + std::unique_ptr<base::DictionaryValue> prefs, + const std::set<std::string>& observed_prefs) { + return FilterPrefsImpl(std::move(prefs), observed_prefs, std::string()); +} + } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/lib/util.h b/chromium/services/preferences/public/cpp/lib/util.h index 3e3c88adc82..213497b2c26 100644 --- a/chromium/services/preferences/public/cpp/lib/util.h +++ b/chromium/services/preferences/public/cpp/lib/util.h @@ -6,6 +6,8 @@ #define SERVICES_PREFERENCES_PUBLIC_CPP_LIB_UTIL_H_ #include <memory> +#include <set> +#include <string> #include <vector> #include "base/strings/string_piece.h" @@ -23,6 +25,11 @@ void SetValue(base::DictionaryValue* dictionary_value, const std::vector<base::StringPiece>& path_components, std::unique_ptr<base::Value> value); +// Filters |prefs| to paths contained within |observed_prefs|. +std::unique_ptr<base::DictionaryValue> FilterPrefs( + std::unique_ptr<base::DictionaryValue> prefs, + const std::set<std::string>& observed_prefs); + } // namespace prefs #endif // SERVICES_PREFERENCES_PUBLIC_CPP_LIB_UTIL_H_ diff --git a/chromium/services/preferences/public/cpp/persistent_pref_store_client.cc b/chromium/services/preferences/public/cpp/persistent_pref_store_client.cc index baf9aa1ab3f..3131d4ad2c3 100644 --- a/chromium/services/preferences/public/cpp/persistent_pref_store_client.cc +++ b/chromium/services/preferences/public/cpp/persistent_pref_store_client.cc @@ -59,23 +59,107 @@ void RemoveRedundantPaths(std::set<std::vector<std::string>>* updated_paths) { } // namespace -PersistentPrefStoreClient::PersistentPrefStoreClient( - mojom::PrefStoreConnectorPtr connector, - scoped_refptr<PrefRegistry> pref_registry, - std::vector<PrefValueStore::PrefStoreType> already_connected_types) - : connector_(std::move(connector)), - pref_registry_(std::move(pref_registry)), - already_connected_types_(std::move(already_connected_types)), - weak_factory_(this) { - DCHECK(connector_); -} +// A trie of writes that have been applied locally and sent to the service +// backend, but have not been acked. +class PersistentPrefStoreClient::InFlightWriteTrie { + public: + // Decision on what to do with writes incoming from the service. + enum class Decision { + // The write should be allowed. + kAllow, + + // The write has already been superceded locally and should be ignored. + kIgnore, + + // The write may have been partially superceded locally and should be + // ignored but an updated value is needed from the service. + kResolve + }; + + InFlightWriteTrie() = default; + + void Add() { + std::vector<std::string> v; + Add(v.begin(), v.end()); + } + + template <typename It, typename Jt> + void Add(It path_start, Jt path_end) { + if (path_start == path_end) { + ++writes_in_flight_; + return; + } + children_[*path_start].Add(path_start + 1, path_end); + } + + bool Remove() { + std::vector<std::string> v; + return Remove(v.begin(), v.end()); + } + + template <typename It, typename Jt> + bool Remove(It path_start, Jt path_end) { + if (path_start == path_end) { + DCHECK_GT(writes_in_flight_, 0); + return --writes_in_flight_ == 0 && children_.empty(); + } + auto it = children_.find(*path_start); + DCHECK(it != children_.end()) << *path_start; + auto removed = it->second.Remove(path_start + 1, path_end); + if (removed) + children_.erase(*path_start); + + return children_.empty() && writes_in_flight_ == 0; + } + + template <typename It, typename Jt> + Decision Lookup(It path_start, Jt path_end) { + if (path_start == path_end) { + if (children_.empty()) { + DCHECK_GT(writes_in_flight_, 0); + return Decision::kIgnore; + } + return Decision::kResolve; + } + + if (writes_in_flight_ != 0) { + return Decision::kIgnore; + } + auto it = children_.find(*path_start); + if (it == children_.end()) { + return Decision::kAllow; + } + + return it->second.Lookup(path_start + 1, path_end); + } + + private: + std::map<std::string, InFlightWriteTrie> children_; + int writes_in_flight_ = 0; + + DISALLOW_COPY_AND_ASSIGN(InFlightWriteTrie); +}; + +struct PersistentPrefStoreClient::InFlightWrite { + std::string key; + std::vector<std::vector<std::string>> sub_pref_paths; +}; PersistentPrefStoreClient::PersistentPrefStoreClient( mojom::PersistentPrefStoreConnectionPtr connection) : weak_factory_(this) { - OnConnect(std::move(connection), - std::unordered_map<PrefValueStore::PrefStoreType, - prefs::mojom::PrefStoreConnectionPtr>()); + read_error_ = connection->read_error; + read_only_ = connection->read_only; + pref_store_ = std::move(connection->pref_store); + if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) + error_delegate_->OnError(read_error_); + error_delegate_.reset(); + if (connection->pref_store_connection) { + Init(std::move(connection->pref_store_connection->initial_prefs), true, + std::move(connection->pref_store_connection->observer)); + } else { + Init(nullptr, false, nullptr); + } } void PersistentPrefStoreClient::SetValue(const std::string& key, @@ -123,9 +207,6 @@ void PersistentPrefStoreClient::SetValueSilently( uint32_t flags) { DCHECK(pref_store_); GetMutableValues().Set(key, std::move(value)); - QueueWrite(key, - std::set<std::vector<std::string>>{std::vector<std::string>{}}, - flags); } bool PersistentPrefStoreClient::ReadOnly() const { @@ -138,35 +219,18 @@ PersistentPrefStore::PrefReadError PersistentPrefStoreClient::GetReadError() } PersistentPrefStore::PrefReadError PersistentPrefStoreClient::ReadPrefs() { - mojom::PersistentPrefStoreConnectionPtr connection; - std::unordered_map<PrefValueStore::PrefStoreType, - prefs::mojom::PrefStoreConnectionPtr> - other_pref_stores; - mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_calls; - bool success = connector_->Connect(SerializePrefRegistry(*pref_registry_), - already_connected_types_, &connection, - &other_pref_stores); - DCHECK(success); - pref_registry_ = nullptr; - OnConnect(std::move(connection), std::move(other_pref_stores)); - return read_error_; + return GetReadError(); } void PersistentPrefStoreClient::ReadPrefsAsync( - ReadErrorDelegate* error_delegate) { - error_delegate_.reset(error_delegate); - connector_->Connect(SerializePrefRegistry(*pref_registry_), - already_connected_types_, - base::Bind(&PersistentPrefStoreClient::OnConnect, - base::Unretained(this))); - pref_registry_ = nullptr; -} + ReadErrorDelegate* error_delegate) {} -void PersistentPrefStoreClient::CommitPendingWrite() { +void PersistentPrefStoreClient::CommitPendingWrite( + base::OnceClosure done_callback) { DCHECK(pref_store_); if (!pending_writes_.empty()) FlushPendingWrites(); - pref_store_->CommitPendingWrite(); + pref_store_->CommitPendingWrite(std::move(done_callback)); } void PersistentPrefStoreClient::SchedulePendingLossyWrites() { @@ -183,28 +247,7 @@ PersistentPrefStoreClient::~PersistentPrefStoreClient() { if (!pref_store_) return; - CommitPendingWrite(); -} - -void PersistentPrefStoreClient::OnConnect( - mojom::PersistentPrefStoreConnectionPtr connection, - std::unordered_map<PrefValueStore::PrefStoreType, - prefs::mojom::PrefStoreConnectionPtr> - other_pref_stores) { - connector_.reset(); - read_error_ = connection->read_error; - read_only_ = connection->read_only; - pref_store_ = std::move(connection->pref_store); - if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) - error_delegate_->OnError(read_error_); - error_delegate_.reset(); - - if (connection->pref_store_connection) { - Init(std::move(connection->pref_store_connection->initial_prefs), true, - std::move(connection->pref_store_connection->observer)); - } else { - Init(nullptr, false, nullptr); - } + CommitPendingWrite(base::OnceClosure()); } void PersistentPrefStoreClient::QueueWrite( @@ -229,16 +272,24 @@ void PersistentPrefStoreClient::QueueWrite( } void PersistentPrefStoreClient::FlushPendingWrites() { + weak_factory_.InvalidateWeakPtrs(); + if (pending_writes_.empty()) + return; + std::vector<mojom::PrefUpdatePtr> updates; + std::vector<InFlightWrite> writes; + for (auto& pref : pending_writes_) { auto update_value = mojom::PrefUpdateValue::New(); const base::Value* value = nullptr; if (GetValue(pref.first, &value)) { std::vector<mojom::SubPrefUpdatePtr> pref_updates; + std::vector<std::vector<std::string>> sub_pref_writes; RemoveRedundantPaths(&pref.second.first); for (const auto& path : pref.second.first) { if (path.empty()) { pref_updates.clear(); + sub_pref_writes.clear(); break; } const base::Value* nested_value = LookupPath(value, path); @@ -248,20 +299,78 @@ void PersistentPrefStoreClient::FlushPendingWrites() { } else { pref_updates.emplace_back(base::in_place, path, nullptr); } + sub_pref_writes.push_back(path); } if (pref_updates.empty()) { update_value->set_atomic_update(value->CreateDeepCopy()); + writes.push_back({pref.first}); } else { update_value->set_split_updates(std::move(pref_updates)); + writes.push_back({pref.first, std::move(sub_pref_writes)}); } } else { update_value->set_atomic_update(nullptr); + writes.push_back({pref.first}); } updates.emplace_back(base::in_place, pref.first, std::move(update_value), pref.second.second); } pref_store_->SetValues(std::move(updates)); pending_writes_.clear(); + + for (const auto& write : writes) { + auto& trie = in_flight_writes_tries_[write.key]; + if (write.sub_pref_paths.empty()) { + trie.Add(); + } else { + for (const auto& subpref_update : write.sub_pref_paths) { + trie.Add(subpref_update.begin(), subpref_update.end()); + } + } + } + in_flight_writes_queue_.push(std::move(writes)); +} + +void PersistentPrefStoreClient::OnPrefChangeAck() { + const auto& writes = in_flight_writes_queue_.front(); + for (const auto& write : writes) { + auto it = in_flight_writes_tries_.find(write.key); + DCHECK(it != in_flight_writes_tries_.end()) << write.key; + bool remove = false; + if (write.sub_pref_paths.empty()) { + remove = it->second.Remove(); + } else { + for (const auto& subpref_update : write.sub_pref_paths) { + remove = + it->second.Remove(subpref_update.begin(), subpref_update.end()); + } + } + if (remove) { + in_flight_writes_tries_.erase(it); + } + } + in_flight_writes_queue_.pop(); +} + +bool PersistentPrefStoreClient::ShouldSkipWrite( + const std::string& key, + const std::vector<std::string>& path, + const base::Value* new_value) { + if (!pending_writes_.empty()) { + FlushPendingWrites(); + } + auto it = in_flight_writes_tries_.find(key); + if (it == in_flight_writes_tries_.end()) { + return false; + } + + auto decision = it->second.Lookup(path.begin(), path.end()); + if (decision == InFlightWriteTrie::Decision::kAllow) { + return false; + } + if (decision == InFlightWriteTrie::Decision::kResolve) + pref_store_->RequestValue(key, path); + return true; } } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/persistent_pref_store_client.h b/chromium/services/preferences/public/cpp/persistent_pref_store_client.h index 67849f4245e..e00db150e63 100644 --- a/chromium/services/preferences/public/cpp/persistent_pref_store_client.h +++ b/chromium/services/preferences/public/cpp/persistent_pref_store_client.h @@ -23,8 +23,6 @@ namespace base { class Value; } -class PrefRegistry; - namespace prefs { // An implementation of PersistentPrefStore backed by a @@ -32,11 +30,6 @@ namespace prefs { class PersistentPrefStoreClient : public PrefStoreClientMixin<PersistentPrefStore> { public: - PersistentPrefStoreClient( - mojom::PrefStoreConnectorPtr connector, - scoped_refptr<PrefRegistry> pref_registry, - std::vector<PrefValueStore::PrefStoreType> already_connected_types); - explicit PersistentPrefStoreClient( mojom::PersistentPrefStoreConnectionPtr connection); @@ -60,7 +53,7 @@ class PersistentPrefStoreClient PrefReadError GetReadError() const override; PrefReadError ReadPrefs() override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; - void CommitPendingWrite() override; + void CommitPendingWrite(base::OnceClosure done_callback) override; void SchedulePendingLossyWrites() override; void ClearMutableValues() override; @@ -69,18 +62,21 @@ class PersistentPrefStoreClient ~PersistentPrefStoreClient() override; private: - void OnConnect(mojom::PersistentPrefStoreConnectionPtr connection, - std::unordered_map<PrefValueStore::PrefStoreType, - prefs::mojom::PrefStoreConnectionPtr> - other_pref_stores); + class InFlightWriteTrie; + struct InFlightWrite; void QueueWrite(const std::string& key, std::set<std::vector<std::string>> path_components, uint32_t flags); void FlushPendingWrites(); - mojom::PrefStoreConnectorPtr connector_; - scoped_refptr<PrefRegistry> pref_registry_; + // prefs::mojom::PreferenceObserver: + void OnPrefChangeAck() override; + + bool ShouldSkipWrite(const std::string& key, + const std::vector<std::string>& path, + const base::Value* new_value) override; + bool read_only_ = false; PrefReadError read_error_ = PersistentPrefStore::PREF_READ_ERROR_NONE; mojom::PersistentPrefStorePtr pref_store_; @@ -88,7 +84,9 @@ class PersistentPrefStoreClient pending_writes_; std::unique_ptr<ReadErrorDelegate> error_delegate_; - std::vector<PrefValueStore::PrefStoreType> already_connected_types_; + + std::queue<std::vector<InFlightWrite>> in_flight_writes_queue_; + std::map<std::string, InFlightWriteTrie> in_flight_writes_tries_; base::WeakPtrFactory<PersistentPrefStoreClient> weak_factory_; diff --git a/chromium/services/preferences/public/cpp/pref_registry_serializer.cc b/chromium/services/preferences/public/cpp/pref_registry_serializer.cc index 484e8ab9aba..64ccbe59cbb 100644 --- a/chromium/services/preferences/public/cpp/pref_registry_serializer.cc +++ b/chromium/services/preferences/public/cpp/pref_registry_serializer.cc @@ -8,10 +8,20 @@ namespace prefs { -mojom::PrefRegistryPtr SerializePrefRegistry(PrefRegistry& pref_registry) { +mojom::PrefRegistryPtr SerializePrefRegistry( + const PrefRegistry& pref_registry) { auto registry = mojom::PrefRegistry::New(); for (auto& pref : pref_registry) { - registry->registrations.push_back(pref.first); + auto flags = pref_registry.GetRegistrationFlags(pref.first); + if (flags & PrefRegistry::PUBLIC) { + registry->public_registrations.emplace_back(mojom::PrefRegistration::New( + pref.first, pref.second->CreateDeepCopy(), flags)); + } else { + registry->private_registrations.push_back(pref.first); + } + } + for (auto& pref : pref_registry.foreign_pref_keys()) { + registry->foreign_registrations.push_back(pref); } return registry; } diff --git a/chromium/services/preferences/public/cpp/pref_registry_serializer.h b/chromium/services/preferences/public/cpp/pref_registry_serializer.h index 4e016ce9519..9df4eb5ed62 100644 --- a/chromium/services/preferences/public/cpp/pref_registry_serializer.h +++ b/chromium/services/preferences/public/cpp/pref_registry_serializer.h @@ -11,7 +11,7 @@ class PrefRegistry; namespace prefs { -mojom::PrefRegistryPtr SerializePrefRegistry(PrefRegistry& pref_registry); +mojom::PrefRegistryPtr SerializePrefRegistry(const PrefRegistry& pref_registry); } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_service_factory.cc b/chromium/services/preferences/public/cpp/pref_service_factory.cc index 2f1f4316492..d7868021344 100644 --- a/chromium/services/preferences/public/cpp/pref_service_factory.cc +++ b/chromium/services/preferences/public/cpp/pref_service_factory.cc @@ -5,6 +5,7 @@ #include "services/preferences/public/cpp/pref_service_factory.h" #include "base/callback_helpers.h" +#include "components/prefs/overlay_user_pref_store.h" #include "components/prefs/persistent_pref_store.h" #include "components/prefs/pref_notifier_impl.h" #include "components/prefs/pref_registry.h" @@ -44,10 +45,28 @@ scoped_refptr<PrefStore> CreatePrefStoreClient( mojom::PrefStoreConnectionPtr>* connections) { auto pref_store_it = connections->find(store_type); if (pref_store_it != connections->end()) { - return make_scoped_refptr( - new PrefStoreClient(std::move(pref_store_it->second))); + return base::MakeRefCounted<PrefStoreClient>( + std::move(pref_store_it->second)); + } + return nullptr; +} + +void OnPrefServiceInit(std::unique_ptr<PrefService> pref_service, + ConnectCallback callback, + bool success) { + if (success) { + callback.Run(std::move(pref_service)); } else { - return nullptr; + callback.Run(nullptr); + } +} + +void RegisterRemoteDefaults(PrefRegistry* pref_registry, + std::vector<mojom::PrefRegistrationPtr> defaults) { + for (auto& registration : defaults) { + pref_registry->SetDefaultForeignPrefValue( + registration->key, std::move(registration->default_value), + registration->flags); } } @@ -57,6 +76,8 @@ void OnConnect( scoped_refptr<PrefRegistry> pref_registry, ConnectCallback callback, mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection, + mojom::PersistentPrefStoreConnectionPtr incognito_connection, + std::vector<mojom::PrefRegistrationPtr> defaults, std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStoreConnectionPtr> connections) { scoped_refptr<PrefStore> managed_prefs = @@ -69,20 +90,41 @@ void OnConnect( CreatePrefStoreClient(PrefValueStore::COMMAND_LINE_STORE, &connections); scoped_refptr<PrefStore> recommended_prefs = CreatePrefStoreClient(PrefValueStore::RECOMMENDED_STORE, &connections); - // TODO(crbug.com/719770): Once owning registrations are supported, pass the - // default values of owned prefs to the service and connect to the service's - // defaults pref store instead of using a local one. + RegisterRemoteDefaults(pref_registry.get(), std::move(defaults)); scoped_refptr<PersistentPrefStore> persistent_pref_store( new PersistentPrefStoreClient( std::move(persistent_pref_store_connection))); + // If in incognito mode, |persistent_pref_store| above will be a connection to + // an in-memory pref store and |incognito_connection| will refer to the + // underlying profile's user pref store. + scoped_refptr<PersistentPrefStore> user_pref_store = + incognito_connection + ? new OverlayUserPrefStore( + persistent_pref_store.get(), + new PersistentPrefStoreClient(std::move(incognito_connection))) + : persistent_pref_store; PrefNotifierImpl* pref_notifier = new PrefNotifierImpl(); auto* pref_value_store = new PrefValueStore( managed_prefs.get(), supervised_user_prefs.get(), extension_prefs.get(), - command_line_prefs.get(), persistent_pref_store.get(), - recommended_prefs.get(), pref_registry->defaults().get(), pref_notifier); - callback.Run(base::MakeUnique<::PrefService>( - pref_notifier, pref_value_store, persistent_pref_store.get(), - pref_registry.get(), base::Bind(&DoNothingHandleReadError), true)); + command_line_prefs.get(), user_pref_store.get(), recommended_prefs.get(), + pref_registry->defaults().get(), pref_notifier); + auto pref_service = base::MakeUnique<PrefService>( + pref_notifier, pref_value_store, user_pref_store.get(), + pref_registry.get(), base::Bind(&DoNothingHandleReadError), true); + switch (pref_service->GetInitializationStatus()) { + case PrefService::INITIALIZATION_STATUS_WAITING: + pref_service->AddPrefInitObserver(base::Bind(&OnPrefServiceInit, + base::Passed(&pref_service), + base::Passed(&callback))); + break; + case PrefService::INITIALIZATION_STATUS_SUCCESS: + case PrefService::INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE: + callback.Run(std::move(pref_service)); + break; + case PrefService::INITIALIZATION_STATUS_ERROR: + callback.Run(nullptr); + break; + } connector_ptr->reset(); } @@ -99,10 +141,8 @@ void OnConnectError( void ConnectToPrefService( service_manager::Connector* connector, scoped_refptr<PrefRegistry> pref_registry, - std::vector<PrefValueStore::PrefStoreType> already_connected_types, ConnectCallback callback, base::StringPiece service_name) { - already_connected_types.push_back(PrefValueStore::DEFAULT_STORE); auto connector_ptr = make_scoped_refptr( new RefCountedInterfacePtr<mojom::PrefStoreConnector>()); connector->BindInterface(service_name.as_string(), &connector_ptr->get()); @@ -110,9 +150,9 @@ void ConnectToPrefService( &OnConnectError, connector_ptr, base::Passed(ConnectCallback{callback}))); auto serialized_pref_registry = SerializePrefRegistry(*pref_registry); connector_ptr->get()->Connect( - std::move(serialized_pref_registry), std::move(already_connected_types), - base::Bind(&OnConnect, connector_ptr, base::Passed(&pref_registry), - base::Passed(&callback))); + std::move(serialized_pref_registry), + base::BindOnce(&OnConnect, connector_ptr, std::move(pref_registry), + std::move(callback))); } } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_service_factory.h b/chromium/services/preferences/public/cpp/pref_service_factory.h index e3cb3f344d3..9bae4322fda 100644 --- a/chromium/services/preferences/public/cpp/pref_service_factory.h +++ b/chromium/services/preferences/public/cpp/pref_service_factory.h @@ -39,7 +39,6 @@ using ConnectCallback = base::Callback<void(std::unique_ptr<::PrefService>)>; void ConnectToPrefService( service_manager::Connector* connector, scoped_refptr<PrefRegistry> pref_registry, - std::vector<PrefValueStore::PrefStoreType> already_connected_types, ConnectCallback callback, base::StringPiece service_name = mojom::kServiceName); diff --git a/chromium/services/preferences/public/cpp/pref_service_main.cc b/chromium/services/preferences/public/cpp/pref_service_main.cc index b4c92c53060..c481b61b8a7 100644 --- a/chromium/services/preferences/public/cpp/pref_service_main.cc +++ b/chromium/services/preferences/public/cpp/pref_service_main.cc @@ -10,11 +10,21 @@ namespace prefs { -std::unique_ptr<service_manager::Service> CreatePrefService( - std::set<PrefValueStore::PrefStoreType> expected_pref_stores, - scoped_refptr<base::SequencedWorkerPool> worker_pool) { - return base::MakeUnique<PrefStoreManagerImpl>(expected_pref_stores, - std::move(worker_pool)); +std::pair<std::unique_ptr<service_manager::Service>, base::OnceClosure> +CreatePrefService(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PersistentPrefStore* user_prefs, + PersistentPrefStore* incognito_user_prefs_underlay, + PrefStore* recommended_prefs, + PrefRegistry* pref_registry) { + auto service = base::MakeUnique<PrefStoreManagerImpl>( + managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs, + user_prefs, incognito_user_prefs_underlay, recommended_prefs, + pref_registry); + auto quit_closure = service->ShutDownClosure(); + return std::make_pair(std::move(service), std::move(quit_closure)); } } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_service_main.h b/chromium/services/preferences/public/cpp/pref_service_main.h index 6b51efaf3c1..e0e50bff426 100644 --- a/chromium/services/preferences/public/cpp/pref_service_main.h +++ b/chromium/services/preferences/public/cpp/pref_service_main.h @@ -11,9 +11,8 @@ #include "base/memory/ref_counted.h" #include "components/prefs/pref_value_store.h" -namespace base { -class SequencedWorkerPool; -} +class PersistentPrefStore; +class PrefRegistry; namespace service_manager { class Service; @@ -21,9 +20,16 @@ class Service; namespace prefs { -std::unique_ptr<service_manager::Service> CreatePrefService( - std::set<PrefValueStore::PrefStoreType> expected_pref_stores, - scoped_refptr<base::SequencedWorkerPool> worker_pool); +// Creates a PrefService and a closure that can be used to shut it down. +std::pair<std::unique_ptr<service_manager::Service>, base::OnceClosure> +CreatePrefService(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PersistentPrefStore* user_prefs, + PersistentPrefStore* incognito_user_prefs_underlay, + PrefStore* recommended_prefs, + PrefRegistry* pref_registry); } // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_store_adapter.cc b/chromium/services/preferences/public/cpp/pref_store_adapter.cc deleted file mode 100644 index 2cfe85a9167..00000000000 --- a/chromium/services/preferences/public/cpp/pref_store_adapter.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2017 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 "services/preferences/public/cpp/pref_store_adapter.h" - -namespace prefs { - -PrefStoreAdapter::PrefStoreAdapter(scoped_refptr<PrefStore> pref_store, - std::unique_ptr<PrefStoreImpl> impl) - : pref_store_(std::move(pref_store)), impl_(std::move(impl)) {} - -void PrefStoreAdapter::AddObserver(PrefStore::Observer* observer) { - pref_store_->AddObserver(observer); -} - -void PrefStoreAdapter::RemoveObserver(PrefStore::Observer* observer) { - pref_store_->RemoveObserver(observer); -} - -bool PrefStoreAdapter::HasObservers() const { - return pref_store_->HasObservers(); -} - -bool PrefStoreAdapter::IsInitializationComplete() const { - return pref_store_->IsInitializationComplete(); -} - -bool PrefStoreAdapter::GetValue(const std::string& key, - const base::Value** result) const { - return pref_store_->GetValue(key, result); -} - -std::unique_ptr<base::DictionaryValue> PrefStoreAdapter::GetValues() const { - return pref_store_->GetValues(); -} - -PrefStoreAdapter::~PrefStoreAdapter() = default; - -} // namespace prefs diff --git a/chromium/services/preferences/public/cpp/pref_store_adapter.h b/chromium/services/preferences/public/cpp/pref_store_adapter.h deleted file mode 100644 index c734606344e..00000000000 --- a/chromium/services/preferences/public/cpp/pref_store_adapter.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017 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 SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_ADAPTER_H_ -#define SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_ADAPTER_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/values.h" -#include "components/prefs/pref_store.h" -#include "services/preferences/public/cpp/pref_store_impl.h" - -namespace prefs { - -// Ties the lifetime of a |PrefStoreImpl| to a |PrefStore|. Otherwise acts as a -// |PrefStore|, forwarding all calls to the wrapped |PrefStore|. -class PrefStoreAdapter : public PrefStore { - public: - PrefStoreAdapter(scoped_refptr<PrefStore> pref_store, - std::unique_ptr<PrefStoreImpl> impl); - - // PrefStore: - void AddObserver(PrefStore::Observer* observer) override; - void RemoveObserver(PrefStore::Observer* observer) override; - bool HasObservers() const override; - bool IsInitializationComplete() const override; - bool GetValue(const std::string& key, - const base::Value** result) const override; - std::unique_ptr<base::DictionaryValue> GetValues() const override; - - private: - ~PrefStoreAdapter() override; - - scoped_refptr<PrefStore> pref_store_; - std::unique_ptr<PrefStoreImpl> impl_; - - DISALLOW_COPY_AND_ASSIGN(PrefStoreAdapter); -}; - -} // namespace prefs - -#endif // SERVICES_PREFERENCES_PUBLIC_CPP_PREF_STORE_ADAPTER_H_ diff --git a/chromium/services/preferences/public/cpp/pref_store_client.h b/chromium/services/preferences/public/cpp/pref_store_client.h index b4d82451100..b7ca96e98c4 100644 --- a/chromium/services/preferences/public/cpp/pref_store_client.h +++ b/chromium/services/preferences/public/cpp/pref_store_client.h @@ -12,8 +12,6 @@ namespace prefs { -// TODO(tibell): Make PrefObserverStore use PrefStoreClient as a base class. - // An implementation of PrefStore which uses prefs::mojom::PrefStore as // the backing store of the preferences. // diff --git a/chromium/services/preferences/public/cpp/pref_store_client_mixin.cc b/chromium/services/preferences/public/cpp/pref_store_client_mixin.cc index f78cf76e184..842e533da74 100644 --- a/chromium/services/preferences/public/cpp/pref_store_client_mixin.cc +++ b/chromium/services/preferences/public/cpp/pref_store_client_mixin.cc @@ -101,12 +101,19 @@ void PrefStoreClientMixin<BasePrefStore>::OnInitializationCompleted( } template <typename BasePrefStore> +void PrefStoreClientMixin<BasePrefStore>::OnPrefChangeAck() {} + +template <typename BasePrefStore> void PrefStoreClientMixin<BasePrefStore>::OnPrefChanged( const std::string& key, mojom::PrefUpdateValuePtr update_value) { DCHECK(cached_prefs_); bool changed = false; if (update_value->is_atomic_update()) { + if (ShouldSkipWrite(key, std::vector<std::string>(), + update_value->get_atomic_update().get())) { + return; + } auto& value = update_value->get_atomic_update(); if (!value) { // Delete if (cached_prefs_->RemovePath(key, nullptr)) @@ -129,9 +136,10 @@ void PrefStoreClientMixin<BasePrefStore>::OnPrefChanged( changed = true; for (auto& update : updates) { // Clients shouldn't send empty paths. - if (update->path.empty()) + if (update->path.empty() || + ShouldSkipWrite(key, update->path, update->value.get())) { continue; - + } std::vector<base::StringPiece> full_path = base::SplitStringPiece( key, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); full_path.insert(full_path.end(), update->path.begin(), @@ -143,6 +151,14 @@ void PrefStoreClientMixin<BasePrefStore>::OnPrefChanged( ReportPrefValueChanged(key); } +template <typename BasePrefStore> +bool PrefStoreClientMixin<BasePrefStore>::ShouldSkipWrite( + const std::string& key, + const std::vector<std::string>& path, + const base::Value* new_value) { + return false; +} + template class PrefStoreClientMixin<::PrefStore>; template class PrefStoreClientMixin<::PersistentPrefStore>; diff --git a/chromium/services/preferences/public/cpp/pref_store_client_mixin.h b/chromium/services/preferences/public/cpp/pref_store_client_mixin.h index 68286ec4bff..29cb6041f88 100644 --- a/chromium/services/preferences/public/cpp/pref_store_client_mixin.h +++ b/chromium/services/preferences/public/cpp/pref_store_client_mixin.h @@ -57,10 +57,17 @@ class PrefStoreClientMixin : public BasePrefStore, // prefs::mojom::PreferenceObserver: void OnPrefsChanged(std::vector<mojom::PrefUpdatePtr> updates) override; void OnInitializationCompleted(bool succeeded) override; + void OnPrefChangeAck() override; void OnPrefChanged(const std::string& key, mojom::PrefUpdateValuePtr update_value); + // Should this client ignore a write received from the service? The default + // implementation never skips writes. + virtual bool ShouldSkipWrite(const std::string& key, + const std::vector<std::string>& path, + const base::Value* new_value); + // Cached preferences. // If null, indicates that initialization failed. std::unique_ptr<base::DictionaryValue> cached_prefs_; diff --git a/chromium/services/preferences/public/cpp/tests/BUILD.gn b/chromium/services/preferences/public/cpp/tests/BUILD.gn index 3f0a9c2c0e8..6e9cbc61e02 100644 --- a/chromium/services/preferences/public/cpp/tests/BUILD.gn +++ b/chromium/services/preferences/public/cpp/tests/BUILD.gn @@ -7,7 +7,6 @@ source_set("tests") { sources = [ "persistent_pref_store_client_unittest.cc", "pref_store_client_unittest.cc", - "pref_store_impl_unittest.cc", ] deps = [ "//base", diff --git a/chromium/services/preferences/public/cpp/tracked/configuration.h b/chromium/services/preferences/public/cpp/tracked/configuration.h index bbb0ebff4ca..2b06c56f8dd 100644 --- a/chromium/services/preferences/public/cpp/tracked/configuration.h +++ b/chromium/services/preferences/public/cpp/tracked/configuration.h @@ -5,7 +5,7 @@ #ifndef SERVICES_PREFERENCES_PUBLIC_CPP_TRACKED_CONFIGURATION_H_ #define SERVICES_PREFERENCES_PUBLIC_CPP_TRACKED_CONFIGURATION_H_ -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" namespace prefs { diff --git a/chromium/services/preferences/public/cpp/tracked/mock_validation_delegate.h b/chromium/services/preferences/public/cpp/tracked/mock_validation_delegate.h index 5538c69301f..44f9fb87d1e 100644 --- a/chromium/services/preferences/public/cpp/tracked/mock_validation_delegate.h +++ b/chromium/services/preferences/public/cpp/tracked/mock_validation_delegate.h @@ -12,7 +12,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom.h" class MockValidationDelegate; diff --git a/chromium/services/preferences/public/interfaces/BUILD.gn b/chromium/services/preferences/public/interfaces/BUILD.gn index 8bc3749fe60..58d8191fb9c 100644 --- a/chromium/services/preferences/public/interfaces/BUILD.gn +++ b/chromium/services/preferences/public/interfaces/BUILD.gn @@ -7,7 +7,6 @@ import("//mojo/public/tools/bindings/mojom.gni") mojom("interfaces") { sources = [ "preferences.mojom", - "preferences_configuration.mojom", "tracked_preference_validation_delegate.mojom", ] public_deps = [ diff --git a/chromium/services/preferences/public/interfaces/preferences.mojom b/chromium/services/preferences/public/interfaces/preferences.mojom index eb4494567ae..02a8bf9c885 100644 --- a/chromium/services/preferences/public/interfaces/preferences.mojom +++ b/chromium/services/preferences/public/interfaces/preferences.mojom @@ -4,10 +4,13 @@ module prefs.mojom; +import "mojo/common/file_path.mojom"; +import "mojo/common/string16.mojom"; import "mojo/common/values.mojom"; -import "services/preferences/public/interfaces/preferences_configuration.mojom"; +import "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom"; const string kServiceName = "preferences"; +const string kLocalStateServiceName = "local_state"; const string kForwarderServiceName = "preferences_forwarder"; // The know pref store types. @@ -30,6 +33,13 @@ interface PrefStoreObserver { // The PrefStore has been initialized (asynchronously). OnInitializationCompleted(bool succeeded); + + // A preference write by this client has been applied. If this + // PrefStoreObserver is associated with a PersistentPrefStore, one + // OnPrefChangeAck() message is sent in response to each SetValues() message. + // This exists to ensure acks are ordered with respect to OnPrefsChanged + // messages. + OnPrefChangeAck(); }; // Captures the connections to a PrefStore by supplying the initial state of the @@ -68,34 +78,21 @@ struct PersistentPrefStoreConnection { bool read_only; }; -// Manages actual read of preference data. Accepts observers who subscribe to -// preferences, notifying them of changes. -interface PrefStore { - // Add an observer of changes to prefs contained in |prefs_to_observe|. This - // current values of all prefs will not be communicated through a call to - // |observer| but instead be returned in |initial_prefs|. - AddObserver(array<string> prefs_to_observe) => ( - PrefStoreConnection connection); -}; - -// Manages a registry of all pref stores. Registered pref stores can be -// connected to through the |PrefStoreConnector| interface. -interface PrefStoreRegistry { - // Register a pref store. - Register(PrefStoreType type, PrefStore pref_store); -}; - // Allows connections to pref stores registered with |PrefStoreRegistry|. interface PrefStoreConnector { // Connect to all registered pref stores, retrieving the current values of all // prefs in each store and an |observer| interfaces through which updates can - // be received. The client asserts that it is already connected to the - // |already_connected_types| pref stores through some other means, so the - // Connect call will not connect to those. - [Sync] - Connect(PrefRegistry pref_registry, - array<PrefStoreType> already_connected_types) => + // be received. + // + // The returned |connection| is the connection to the main writable user pref + // store. + // + // Calls to |Connect| before |Init| are allowed and will cause the calls to + // queue and connect once |Init| has been called. + Connect(PrefRegistry pref_registry) => (PersistentPrefStoreConnection connection, + PersistentPrefStoreConnection? underlay, + array<PrefRegistration> remote_defaults, map<PrefStoreType, PrefStoreConnection> connections); }; @@ -130,18 +127,36 @@ interface PersistentPrefStore { // Sets the values for prefs. SetValues(array<PrefUpdate> updates); + // Requests that the pref service transmits its value for a pref (or sub-pref + // if |sub_pref_path| is non-empty). The value will be transmitted over the + // corresponding PrefStoreObserver interface previous returned by + // PrefStoreConnector.Connect(). + RequestValue(string key, array<string> sub_pref_path); + // These mirror the C++ PersistentPrefStore methods. - CommitPendingWrite(); + CommitPendingWrite() => (); SchedulePendingLossyWrites(); ClearMutableValues(); }; // A registry of all prefs registered by a single client. struct PrefRegistry { - array<string> registrations; + // A list of pref keys that are private to this client. This client claims + // exclusive access to these prefs. + array<string> private_registrations; + + // A list of pref keys that are public, but owned by another client. + array<string> foreign_registrations; + + // A list of prefs that are publicly owned by this client. Each registration + // contains the key, the default value and flags. These are shared with + // clients that list the same pref in |foreign_registrations|. + array<PrefRegistration> public_registrations; }; struct PrefRegistration { + string key; + mojo.common.mojom.Value default_value; // A bitfield of flags. Flag values are defined in @@ -150,8 +165,49 @@ struct PrefRegistration { uint32 flags; }; -interface PrefServiceControl { - // Initializes the pref service. This must be called before the service can - // be used. - Init(PersistentPrefStoreConfiguration configuration); +// --------------------------------------------------------------------- +// Service Configuration + +// These parameters are passed to prefs::CreateTrackedPersistentPrefStore() in +// services/preferences/persistent_pref_store_factory.cc. +struct TrackedPersistentPrefStoreConfiguration { + mojo.common.mojom.FilePath unprotected_pref_filename; + mojo.common.mojom.FilePath protected_pref_filename; + array<TrackedPreferenceMetadata> tracking_configuration; + uint64 reporting_ids_count; + string seed; + string legacy_device_id; + string registry_seed; + mojo.common.mojom.String16 registry_path; + TrackedPreferenceValidationDelegate? validation_delegate; + ResetOnLoadObserver? reset_on_load_observer; +}; + +struct TrackedPreferenceMetadata { + enum EnforcementLevel { NO_ENFORCEMENT, ENFORCE_ON_LOAD }; + + enum PrefTrackingStrategy { + // Atomic preferences are tracked as a whole. + ATOMIC, + // Split preferences are dictionaries for which each top-level entry is + // tracked independently. Note: preferences using this strategy must be kept + // in sync with TrackedSplitPreferences in histograms.xml. + SPLIT, + }; + + enum ValueType { + IMPERSONAL, + // The preference value may contain personal information. + PERSONAL, + }; + + uint64 reporting_id; + string name; + EnforcementLevel enforcement_level; + PrefTrackingStrategy strategy; + ValueType value_type; +}; + +interface ResetOnLoadObserver { + OnResetOnLoad(); }; diff --git a/chromium/services/preferences/public/interfaces/preferences_configuration.mojom b/chromium/services/preferences/public/interfaces/preferences_configuration.mojom deleted file mode 100644 index 76f7785ae06..00000000000 --- a/chromium/services/preferences/public/interfaces/preferences_configuration.mojom +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 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. - -module prefs.mojom; - -import "mojo/common/file_path.mojom"; -import "mojo/common/string16.mojom"; -import "services/preferences/public/interfaces/tracked_preference_validation_delegate.mojom"; - -union PersistentPrefStoreConfiguration { - SimplePersistentPrefStoreConfiguration simple_configuration; - TrackedPersistentPrefStoreConfiguration tracked_configuration; -}; - -struct SimplePersistentPrefStoreConfiguration { - mojo.common.mojom.FilePath pref_filename; -}; - -// These parameters are passed to prefs::CreateTrackedPersistentPrefStore() in -// services/preferences/persistent_pref_store_factory.cc. -struct TrackedPersistentPrefStoreConfiguration { - mojo.common.mojom.FilePath unprotected_pref_filename; - mojo.common.mojom.FilePath protected_pref_filename; - array<TrackedPreferenceMetadata> tracking_configuration; - uint64 reporting_ids_count; - string seed; - string legacy_device_id; - string registry_seed; - mojo.common.mojom.String16 registry_path; - TrackedPreferenceValidationDelegate? validation_delegate; - ResetOnLoadObserver? reset_on_load_observer; -}; - -struct TrackedPreferenceMetadata { - enum EnforcementLevel { NO_ENFORCEMENT, ENFORCE_ON_LOAD }; - - enum PrefTrackingStrategy { - // Atomic preferences are tracked as a whole. - ATOMIC, - // Split preferences are dictionaries for which each top-level entry is - // tracked independently. Note: preferences using this strategy must be kept - // in sync with TrackedSplitPreferences in histograms.xml. - SPLIT, - }; - - enum ValueType { - IMPERSONAL, - // The preference value may contain personal information. - PERSONAL, - }; - - uint64 reporting_id; - string name; - EnforcementLevel enforcement_level; - PrefTrackingStrategy strategy; - ValueType value_type; -}; - -interface ResetOnLoadObserver { - OnResetOnLoad(); -}; diff --git a/chromium/services/preferences/scoped_pref_connection_builder.cc b/chromium/services/preferences/scoped_pref_connection_builder.cc index 8e6af170064..5ad903c52e2 100644 --- a/chromium/services/preferences/scoped_pref_connection_builder.cc +++ b/chromium/services/preferences/scoped_pref_connection_builder.cc @@ -5,37 +5,31 @@ #include "services/preferences/scoped_pref_connection_builder.h" #include "services/preferences/persistent_pref_store_impl.h" +#include "services/preferences/pref_store_impl.h" namespace prefs { ScopedPrefConnectionBuilder::ScopedPrefConnectionBuilder( std::vector<std::string> observed_prefs, - std::set<PrefValueStore::PrefStoreType> required_types, mojom::PrefStoreConnector::ConnectCallback callback) : callback_(std::move(callback)), - observed_prefs_(std::move(observed_prefs)), - required_types_(std::move(required_types)) {} + observed_prefs_(std::move(observed_prefs)) {} -std::set<PrefValueStore::PrefStoreType> -ScopedPrefConnectionBuilder::ProvidePrefStoreConnections( +void ScopedPrefConnectionBuilder::ProvidePrefStoreConnections( const std::unordered_map<PrefValueStore::PrefStoreType, - mojom::PrefStorePtr>& pref_stores) { + std::unique_ptr<PrefStoreImpl>>& pref_stores) { for (const auto& pref_store : pref_stores) { - auto it = required_types_.find(pref_store.first); - if (it != required_types_.end()) { - ProvidePrefStoreConnection(pref_store.first, pref_store.second.get()); - required_types_.erase(it); - } + ProvidePrefStoreConnection(pref_store.first, pref_store.second.get()); } - return std::move(required_types_); } void ScopedPrefConnectionBuilder::ProvidePrefStoreConnection( PrefValueStore::PrefStoreType type, - mojom::PrefStore* pref_store) { - pref_store->AddObserver( - observed_prefs_, - base::Bind(&ScopedPrefConnectionBuilder::OnConnect, this, type)); + PrefStoreImpl* pref_store) { + bool inserted = + connections_.emplace(type, pref_store->AddObserver(observed_prefs_)) + .second; + DCHECK(inserted); } void ScopedPrefConnectionBuilder::ProvidePersistentPrefStore( @@ -46,16 +40,22 @@ void ScopedPrefConnectionBuilder::ProvidePersistentPrefStore( observed_prefs_.end())); } -ScopedPrefConnectionBuilder::~ScopedPrefConnectionBuilder() { - std::move(callback_).Run(std::move(persistent_pref_store_connection_), - std::move(connections_)); +void ScopedPrefConnectionBuilder::ProvideIncognitoPersistentPrefStoreUnderlay( + PersistentPrefStoreImpl* persistent_pref_store) { + incognito_connection_ = persistent_pref_store->CreateConnection( + PersistentPrefStoreImpl::ObservedPrefs(observed_prefs_.begin(), + observed_prefs_.end())); } -void ScopedPrefConnectionBuilder::OnConnect( - PrefValueStore::PrefStoreType type, - mojom::PrefStoreConnectionPtr connection_ptr) { - bool inserted = connections_.emplace(type, std::move(connection_ptr)).second; - DCHECK(inserted); +void ScopedPrefConnectionBuilder::ProvideDefaults( + std::vector<mojom::PrefRegistrationPtr> defaults) { + defaults_ = std::move(defaults); +} + +ScopedPrefConnectionBuilder::~ScopedPrefConnectionBuilder() { + std::move(callback_).Run(std::move(persistent_pref_store_connection_), + std::move(incognito_connection_), + std::move(defaults_), std::move(connections_)); } } // namespace prefs diff --git a/chromium/services/preferences/scoped_pref_connection_builder.h b/chromium/services/preferences/scoped_pref_connection_builder.h index c73e97e88c9..270b31bbaee 100644 --- a/chromium/services/preferences/scoped_pref_connection_builder.h +++ b/chromium/services/preferences/scoped_pref_connection_builder.h @@ -16,47 +16,47 @@ namespace prefs { class PersistentPrefStoreImpl; +class PrefStoreImpl; // A builder for connections to pref stores. When all references are released, // the connection is created. class ScopedPrefConnectionBuilder : public base::RefCounted<ScopedPrefConnectionBuilder> { public: - // |required_types| lists all the |Provide*| calls the client of this object - // is expected to make. Once all these |Provide*| calls have been made, the - // |callback| will be invoked. ScopedPrefConnectionBuilder( std::vector<std::string> observed_prefs, - std::set<PrefValueStore::PrefStoreType> required_types, mojom::PrefStoreConnector::ConnectCallback callback); - std::set<PrefValueStore::PrefStoreType> ProvidePrefStoreConnections( + void ProvidePrefStoreConnections( const std::unordered_map<PrefValueStore::PrefStoreType, - mojom::PrefStorePtr>& pref_stores); + std::unique_ptr<PrefStoreImpl>>& pref_stores); void ProvidePrefStoreConnection(PrefValueStore::PrefStoreType type, - mojom::PrefStore* ptr); + PrefStoreImpl* ptr); void ProvidePersistentPrefStore( PersistentPrefStoreImpl* persistent_pref_store); + void ProvideIncognitoPersistentPrefStoreUnderlay( + PersistentPrefStoreImpl* persistent_pref_store); + + void ProvideDefaults(std::vector<mojom::PrefRegistrationPtr> defaults); + private: friend class base::RefCounted<ScopedPrefConnectionBuilder>; ~ScopedPrefConnectionBuilder(); - void OnConnect(PrefValueStore::PrefStoreType type, - mojom::PrefStoreConnectionPtr connection_ptr); - mojom::PrefStoreConnector::ConnectCallback callback_; std::vector<std::string> observed_prefs_; - std::set<PrefValueStore::PrefStoreType> required_types_; - std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStoreConnectionPtr> connections_; + std::vector<mojom::PrefRegistrationPtr> defaults_; + mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection_; + mojom::PersistentPrefStoreConnectionPtr incognito_connection_; DISALLOW_COPY_AND_ASSIGN(ScopedPrefConnectionBuilder); }; diff --git a/chromium/services/preferences/shared_pref_registry.cc b/chromium/services/preferences/shared_pref_registry.cc new file mode 100644 index 00000000000..4889f581f2f --- /dev/null +++ b/chromium/services/preferences/shared_pref_registry.cc @@ -0,0 +1,190 @@ +// Copyright 2017 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 "services/preferences/shared_pref_registry.h" + +#include "components/prefs/pref_registry.h" +#include "components/prefs/pref_store.h" +#include "services/preferences/scoped_pref_connection_builder.h" +#include "services/service_manager/public/cpp/identity.h" + +namespace prefs { + +class SharedPrefRegistry::PendingConnection { + public: + PendingConnection(SharedPrefRegistry* owner, + scoped_refptr<ScopedPrefConnectionBuilder> connection, + std::vector<std::string> foreign_prefs, + std::set<std::string> pending_prefs) + : owner_(owner), + connection_(std::move(connection)), + foreign_prefs_(std::move(foreign_prefs)), + pending_prefs_(std::move(pending_prefs)) {} + + PendingConnection(PendingConnection&& other) = default; + PendingConnection& operator=(PendingConnection&& other) = default; + + bool NewPublicPrefsRegistered(const std::vector<std::string>& keys) { + for (auto& key : keys) { + pending_prefs_.erase(key); + } + if (pending_prefs_.empty()) { + owner_->ProvideDefaultPrefs(connection_.get(), std::move(foreign_prefs_)); + return true; + } + return false; + } + + private: + SharedPrefRegistry* owner_; + scoped_refptr<ScopedPrefConnectionBuilder> connection_; + std::vector<std::string> foreign_prefs_; + std::set<std::string> pending_prefs_; + + DISALLOW_COPY_AND_ASSIGN(PendingConnection); +}; + +SharedPrefRegistry::SharedPrefRegistry(scoped_refptr<PrefRegistry> registry) + : registry_(std::move(registry)) { + for (const auto& pref : *registry_) { +#if DCHECK_IS_ON() + all_registered_pref_keys_.insert(pref.first); +#endif + if (registry_->GetRegistrationFlags(pref.first) & PrefRegistry::PUBLIC) + public_pref_keys_.insert(std::move(pref.first)); + } +} + +SharedPrefRegistry::~SharedPrefRegistry() = default; + +scoped_refptr<ScopedPrefConnectionBuilder> +SharedPrefRegistry::CreateConnectionBuilder( + mojom::PrefRegistryPtr pref_registry, + const service_manager::Identity& identity, + mojom::PrefStoreConnector::ConnectCallback callback) { + bool is_initial_connection = connected_services_.insert(identity).second; +#if DCHECK_IS_ON() + if (is_initial_connection) { + for (const auto& key : pref_registry->private_registrations) { + bool inserted = all_registered_pref_keys_.insert(key).second; + DCHECK(inserted) << "Multiple services claimed ownership of pref \"" + << key << "\""; + } + } +#endif + + std::vector<std::string>& observed_prefs = + pref_registry->private_registrations; + observed_prefs.reserve(observed_prefs.size() + + pref_registry->foreign_registrations.size() + + pref_registry->public_registrations.size()); + + std::set<std::string> remaining_foreign_registrations = + GetPendingForeignPrefs(pref_registry->foreign_registrations, + &observed_prefs); + + ProcessPublicPrefs(std::move(pref_registry->public_registrations), + is_initial_connection, &observed_prefs); +#if DCHECK_IS_ON() + if (is_initial_connection) { + per_service_registered_pref_keys_.emplace( + identity, + std::set<std::string>(observed_prefs.begin(), observed_prefs.end())); + } else { + DCHECK(per_service_registered_pref_keys_[identity] == + std::set<std::string>(observed_prefs.begin(), observed_prefs.end())); + } +#endif + + auto connection_builder = base::MakeRefCounted<ScopedPrefConnectionBuilder>( + std::move(observed_prefs), std::move(callback)); + if (remaining_foreign_registrations.empty()) { + ProvideDefaultPrefs(connection_builder.get(), + pref_registry->foreign_registrations); + } else { + pending_connections_.emplace_back( + this, connection_builder, pref_registry->foreign_registrations, + std::move(remaining_foreign_registrations)); + } + return connection_builder; +} + +std::set<std::string> SharedPrefRegistry::GetPendingForeignPrefs( + std::vector<std::string> foreign_prefs, + std::vector<std::string>* observed_prefs) const { + std::set<std::string> pending_foreign_prefs; + if (foreign_prefs.empty()) + return pending_foreign_prefs; + + observed_prefs->insert(observed_prefs->end(), foreign_prefs.begin(), + foreign_prefs.end()); + std::sort(foreign_prefs.begin(), foreign_prefs.end()); + std::set_difference( + std::make_move_iterator(foreign_prefs.begin()), + std::make_move_iterator(foreign_prefs.end()), public_pref_keys_.begin(), + public_pref_keys_.end(), + std::inserter(pending_foreign_prefs, pending_foreign_prefs.end())); + return pending_foreign_prefs; +} + +void SharedPrefRegistry::ProcessPublicPrefs( + std::vector<mojom::PrefRegistrationPtr> public_pref_registrations, + bool is_initial_connection, + std::vector<std::string>* observed_prefs) { + if (public_pref_registrations.empty()) + return; + + if (is_initial_connection) { + std::vector<std::string> new_public_prefs; + for (auto& registration : public_pref_registrations) { + auto& key = registration->key; + auto& default_value = registration->default_value; +#if DCHECK_IS_ON() + bool inserted = all_registered_pref_keys_.insert(key).second; + DCHECK(inserted) << "Multiple services claimed ownership of pref \"" + << key << "\""; +#endif + registry_->RegisterForeignPref(key); + registry_->SetDefaultForeignPrefValue(key, std::move(default_value), + registration->flags); + + observed_prefs->push_back(key); + new_public_prefs.push_back(key); + public_pref_keys_.insert(std::move(key)); + } + pending_connections_.erase( + std::remove_if(pending_connections_.begin(), pending_connections_.end(), + [&](PendingConnection& connection) { + return connection.NewPublicPrefsRegistered( + new_public_prefs); + }), + pending_connections_.end()); + } else { + for (auto& registration : public_pref_registrations) { + observed_prefs->push_back(registration->key); +#if DCHECK_IS_ON() + const base::Value* existing_default_value = nullptr; + DCHECK(registry_->defaults()->GetValue(registration->key, + &existing_default_value)); + DCHECK_EQ(*existing_default_value, *registration->default_value); +#endif + } + } +} + +void SharedPrefRegistry::ProvideDefaultPrefs( + ScopedPrefConnectionBuilder* connection, + std::vector<std::string> foreign_prefs) { + std::vector<mojom::PrefRegistrationPtr> defaults; + for (const auto& key : foreign_prefs) { + const base::Value* value = nullptr; + registry_->defaults()->GetValue(key, &value); + defaults.emplace_back(base::in_place, key, value->CreateDeepCopy(), + registry_->GetRegistrationFlags(key)); + } + + connection->ProvideDefaults(std::move(defaults)); +} + +} // namespace prefs diff --git a/chromium/services/preferences/shared_pref_registry.h b/chromium/services/preferences/shared_pref_registry.h new file mode 100644 index 00000000000..397fa6d4da7 --- /dev/null +++ b/chromium/services/preferences/shared_pref_registry.h @@ -0,0 +1,80 @@ +// Copyright 2017 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 SERVICES_PREFERENCES_SHARED_PREF_REGISTRY_H_ +#define SERVICES_PREFERENCES_SHARED_PREF_REGISTRY_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" + +class PrefRegistry; + +namespace service_manager { +class Identity; +} + +namespace prefs { +class ScopedPrefConnectionBuilder; + +// A registry of default pref values for public prefs. It records public default +// pref values from connections and provides public default pref values to +// connections that require them. +class SharedPrefRegistry { + public: + explicit SharedPrefRegistry(scoped_refptr<PrefRegistry> registry); + ~SharedPrefRegistry(); + + scoped_refptr<ScopedPrefConnectionBuilder> CreateConnectionBuilder( + mojom::PrefRegistryPtr pref_registry, + const service_manager::Identity& identity, + mojom::PrefStoreConnector::ConnectCallback callback); + + private: + class PendingConnection; + + std::set<std::string> GetPendingForeignPrefs( + std::vector<std::string> foreign_prefs, + std::vector<std::string>* observed_prefs) const; + + void ProcessPublicPrefs( + std::vector<mojom::PrefRegistrationPtr> public_pref_registrations, + bool enforce_single_owner_check, + std::vector<std::string>* observed_prefs); + + void ProvideDefaultPrefs(ScopedPrefConnectionBuilder* connection, + std::vector<std::string> foreign_prefs); + + scoped_refptr<PrefRegistry> registry_; + + std::set<std::string> public_pref_keys_; + + std::set<service_manager::Identity> connected_services_; + + std::vector<PendingConnection> pending_connections_; + +#if DCHECK_IS_ON() + // The set of all registered pref keys. This enforces that only one service + // claims ownership over any pref. This is relatively expensive so skip + // unless dchecks are enabled. + std::set<std::string> all_registered_pref_keys_; + + // Maps from service identity to the set of pref keys registered by that + // service. This enforces that each service registers the same keys for each + // connect. This is relatively expensive so skip unless dchecks are enabled. + std::map<service_manager::Identity, std::set<std::string>> + per_service_registered_pref_keys_; +#endif + + DISALLOW_COPY_AND_ASSIGN(SharedPrefRegistry); +}; + +} // namespace prefs + +#endif // SERVICES_PREFERENCES_SHARED_PREF_REGISTRY_H_ diff --git a/chromium/services/preferences/tracked/BUILD.gn b/chromium/services/preferences/tracked/BUILD.gn index 16be8a2d696..00aca10ad58 100644 --- a/chromium/services/preferences/tracked/BUILD.gn +++ b/chromium/services/preferences/tracked/BUILD.gn @@ -25,6 +25,7 @@ source_set("tracked") { "registry_hash_store_contents_win.h", "segregated_pref_store.cc", "segregated_pref_store.h", + "temp_scoped_dir_cleaner.h", "tracked_atomic_preference.cc", "tracked_atomic_preference.h", "tracked_persistent_pref_store_factory.cc", diff --git a/chromium/services/preferences/tracked/dictionary_hash_store_contents.cc b/chromium/services/preferences/tracked/dictionary_hash_store_contents.cc index 3cef69b05a1..2901657e7c6 100644 --- a/chromium/services/preferences/tracked/dictionary_hash_store_contents.cc +++ b/chromium/services/preferences/tracked/dictionary_hash_store_contents.cc @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/values.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/persistent_pref_store.h" @@ -94,7 +95,7 @@ void DictionaryHashStoreContents::SetSplitMac(const std::string& path, void DictionaryHashStoreContents::ImportEntry(const std::string& path, const base::Value* in_value) { base::DictionaryValue* macs_dict = GetMutableContents(true); - macs_dict->Set(path, in_value->DeepCopy()); + macs_dict->Set(path, base::MakeUnique<base::Value>(*in_value)); } bool DictionaryHashStoreContents::RemoveEntry(const std::string& path) { @@ -126,8 +127,8 @@ base::DictionaryValue* DictionaryHashStoreContents::GetMutableContents( base::DictionaryValue* macs_dict = NULL; storage_->GetDictionary(kPreferenceMACs, &macs_dict); if (!macs_dict && create_if_null) { - macs_dict = new base::DictionaryValue; - storage_->Set(kPreferenceMACs, macs_dict); + macs_dict = storage_->SetDictionary( + kPreferenceMACs, base::MakeUnique<base::DictionaryValue>()); } return macs_dict; } diff --git a/chromium/services/preferences/tracked/pref_hash_calculator_unittest.cc b/chromium/services/preferences/tracked/pref_hash_calculator_unittest.cc index df3746a09e0..0dc9765102d 100644 --- a/chromium/services/preferences/tracked/pref_hash_calculator_unittest.cc +++ b/chromium/services/preferences/tracked/pref_hash_calculator_unittest.cc @@ -6,6 +6,7 @@ #include <memory> #include <string> +#include <utility> #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -18,7 +19,8 @@ TEST(PrefHashCalculatorTest, TestCurrentAlgorithm) { base::Value string_value_2("string value 2"); base::DictionaryValue dictionary_value_1; dictionary_value_1.SetInteger("int value", 1); - dictionary_value_1.Set("nested empty map", new base::DictionaryValue); + dictionary_value_1.Set("nested empty map", + base::MakeUnique<base::DictionaryValue>()); base::DictionaryValue dictionary_value_1_equivalent; dictionary_value_1_equivalent.SetInteger("int value", 1); base::DictionaryValue dictionary_value_2; @@ -77,34 +79,33 @@ TEST(PrefHashCalculatorTest, CatchHashChanges) { static const char kDeviceId[] = "test_device_id1"; auto null_value = base::MakeUnique<base::Value>(); - std::unique_ptr<base::Value> bool_value(new base::Value(false)); - std::unique_ptr<base::Value> int_value(new base::Value(1234567890)); - std::unique_ptr<base::Value> double_value(new base::Value(123.0987654321)); - std::unique_ptr<base::Value> string_value( - new base::Value("testing with special chars:\n<>{}:^^@#$\\/")); + auto bool_value = base::MakeUnique<base::Value>(false); + auto int_value = base::MakeUnique<base::Value>(1234567890); + auto double_value = base::MakeUnique<base::Value>(123.0987654321); + auto string_value = base::MakeUnique<base::Value>( + "testing with special chars:\n<>{}:^^@#$\\/"); // For legacy reasons, we have to support pruning of empty lists/dictionaries // and nested empty ists/dicts in the hash generation algorithm. - std::unique_ptr<base::DictionaryValue> nested_empty_dict( - new base::DictionaryValue); - nested_empty_dict->Set("a", new base::DictionaryValue); - nested_empty_dict->Set("b", new base::ListValue); - std::unique_ptr<base::ListValue> nested_empty_list(new base::ListValue); + auto nested_empty_dict = base::MakeUnique<base::DictionaryValue>(); + nested_empty_dict->Set("a", base::MakeUnique<base::DictionaryValue>()); + nested_empty_dict->Set("b", base::MakeUnique<base::ListValue>()); + auto nested_empty_list = base::MakeUnique<base::ListValue>(); nested_empty_list->Append(base::MakeUnique<base::DictionaryValue>()); nested_empty_list->Append(base::MakeUnique<base::ListValue>()); - nested_empty_list->Append(nested_empty_dict->CreateDeepCopy()); + nested_empty_list->Append(base::MakeUnique<base::Value>(*nested_empty_dict)); // A dictionary with an empty dictionary, an empty list, and nested empty // dictionaries/lists in it. - std::unique_ptr<base::DictionaryValue> dict_value(new base::DictionaryValue); - dict_value->Set("a", new base::Value("foo")); - dict_value->Set("d", new base::ListValue); - dict_value->Set("b", new base::DictionaryValue); - dict_value->Set("c", new base::Value("baz")); - dict_value->Set("e", nested_empty_dict.release()); - dict_value->Set("f", nested_empty_list.release()); - - std::unique_ptr<base::ListValue> list_value(new base::ListValue); + auto dict_value = base::MakeUnique<base::DictionaryValue>(); + dict_value->SetString("a", "foo"); + dict_value->Set("d", base::MakeUnique<base::ListValue>()); + dict_value->Set("b", base::MakeUnique<base::DictionaryValue>()); + dict_value->SetString("c", "baz"); + dict_value->Set("e", std::move(nested_empty_dict)); + dict_value->Set("f", std::move(nested_empty_list)); + + auto list_value = base::MakeUnique<base::ListValue>(); list_value->AppendBoolean(true); list_value->AppendInteger(100); list_value->AppendDouble(1.0); @@ -166,13 +167,13 @@ TEST(PrefHashCalculatorTest, CatchHashChanges) { // Also test every value type together in the same dictionary. base::DictionaryValue everything; - everything.Set("null", null_value.release()); - everything.Set("bool", bool_value.release()); - everything.Set("int", int_value.release()); - everything.Set("double", double_value.release()); - everything.Set("string", string_value.release()); - everything.Set("list", list_value.release()); - everything.Set("dict", dict_value.release()); + everything.Set("null", std::move(null_value)); + everything.Set("bool", std::move(bool_value)); + everything.Set("int", std::move(int_value)); + everything.Set("double", std::move(double_value)); + everything.Set("string", std::move(string_value)); + everything.Set("list", std::move(list_value)); + everything.Set("dict", std::move(dict_value)); static const char kExpectedEverythingValue[] = "B97D09BE7005693574DCBDD03D8D9E44FB51F4008B73FB56A49A9FA671A1999B"; EXPECT_EQ(PrefHashCalculator::VALID, diff --git a/chromium/services/preferences/tracked/pref_hash_filter.cc b/chromium/services/preferences/tracked/pref_hash_filter.cc index 29d6b9ab830..a0b94dfff32 100644 --- a/chromium/services/preferences/tracked/pref_hash_filter.cc +++ b/chromium/services/preferences/tracked/pref_hash_filter.cc @@ -243,9 +243,9 @@ void PrefHashFilter::FinalizeFilterOnLoad( } if (did_reset) { - pref_store_contents->Set(user_prefs::kPreferenceResetTime, - new base::Value(base::Int64ToString( - base::Time::Now().ToInternalValue()))); + pref_store_contents->SetString( + user_prefs::kPreferenceResetTime, + base::Int64ToString(base::Time::Now().ToInternalValue())); FilterUpdate(user_prefs::kPreferenceResetTime); if (reset_on_load_observer_) diff --git a/chromium/services/preferences/tracked/pref_hash_filter.h b/chromium/services/preferences/tracked/pref_hash_filter.h index 4c3cdaa4996..6467eb10dba 100644 --- a/chromium/services/preferences/tracked/pref_hash_filter.h +++ b/chromium/services/preferences/tracked/pref_hash_filter.h @@ -18,7 +18,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/optional.h" -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" #include "services/preferences/tracked/hash_store_contents.h" #include "services/preferences/tracked/interceptable_pref_filter.h" #include "services/preferences/tracked/tracked_preference.h" diff --git a/chromium/services/preferences/tracked/pref_hash_filter_unittest.cc b/chromium/services/preferences/tracked/pref_hash_filter_unittest.cc index 50b7ee29427..b1e0258ca85 100644 --- a/chromium/services/preferences/tracked/pref_hash_filter_unittest.cc +++ b/chromium/services/preferences/tracked/pref_hash_filter_unittest.cc @@ -583,13 +583,15 @@ class PrefHashFilterTest : public testing::TestWithParam<EnforcementLevel>, temp_mock_external_validation_pref_hash_store.get(); mock_external_validation_hash_store_contents_ = temp_mock_external_validation_hash_store_contents.get(); + prefs::mojom::ResetOnLoadObserverPtr reset_on_load_observer; + reset_on_load_observer_bindings_.AddBinding( + this, mojo::MakeRequest(&reset_on_load_observer)); pref_hash_filter_.reset(new PrefHashFilter( std::move(temp_mock_pref_hash_store), PrefHashFilter::StoreContentsPair( std::move(temp_mock_external_validation_pref_hash_store), std::move(temp_mock_external_validation_hash_store_contents)), - std::move(configuration), - reset_on_load_observer_bindings_.CreateInterfacePtrAndBind(this), + std::move(configuration), std::move(reset_on_load_observer), &mock_validation_delegate_, arraysize(kTestTrackedPrefs), true)); } @@ -682,9 +684,7 @@ TEST_P(PrefHashFilterTest, StampSuperMACAltersStore) { TEST_P(PrefHashFilterTest, FilterTrackedPrefUpdate) { base::DictionaryValue root_dict; - // Ownership of |string_value| is transfered to |root_dict|. - base::Value* string_value = new base::Value("string value"); - root_dict.Set(kAtomicPref, string_value); + base::Value* string_value = root_dict.SetString(kAtomicPref, "string value"); // No path should be stored on FilterUpdate. pref_hash_filter_->FilterUpdate(kAtomicPref); @@ -740,11 +740,10 @@ TEST_P(PrefHashFilterTest, ReportSuperMacValidity) { TEST_P(PrefHashFilterTest, FilterSplitPrefUpdate) { base::DictionaryValue root_dict; - // Ownership of |dict_value| is transfered to |root_dict|. - base::DictionaryValue* dict_value = new base::DictionaryValue; + base::DictionaryValue* dict_value = root_dict.SetDictionary( + kSplitPref, base::MakeUnique<base::DictionaryValue>()); dict_value->SetString("a", "foo"); dict_value->SetInteger("b", 1234); - root_dict.Set(kSplitPref, dict_value); // No path should be stored on FilterUpdate. pref_hash_filter_->FilterUpdate(kSplitPref); @@ -764,7 +763,7 @@ TEST_P(PrefHashFilterTest, FilterSplitPrefUpdate) { TEST_P(PrefHashFilterTest, FilterUntrackedPrefUpdate) { base::DictionaryValue root_dict; - root_dict.Set("untracked", new base::Value("some value")); + root_dict.SetString("untracked", "some value"); pref_hash_filter_->FilterUpdate("untracked"); // No paths should be stored on FilterUpdate. @@ -781,18 +780,13 @@ TEST_P(PrefHashFilterTest, FilterUntrackedPrefUpdate) { TEST_P(PrefHashFilterTest, MultiplePrefsFilterSerializeData) { base::DictionaryValue root_dict; - // Ownership of the following values is transfered to |root_dict|. - base::Value* int_value1 = new base::Value(1); - base::Value* int_value2 = new base::Value(2); - base::Value* int_value3 = new base::Value(3); - base::Value* int_value4 = new base::Value(4); - base::DictionaryValue* dict_value = new base::DictionaryValue; - dict_value->Set("a", new base::Value(true)); - root_dict.Set(kAtomicPref, int_value1); - root_dict.Set(kAtomicPref2, int_value2); - root_dict.Set(kAtomicPref3, int_value3); - root_dict.Set("untracked", int_value4); - root_dict.Set(kSplitPref, dict_value); + base::Value* int_value1 = root_dict.SetInteger(kAtomicPref, 1); + root_dict.SetInteger(kAtomicPref2, 2); + root_dict.SetInteger(kAtomicPref3, 3); + root_dict.SetInteger("untracked", 4); + base::DictionaryValue* dict_value = root_dict.SetDictionary( + kSplitPref, base::MakeUnique<base::DictionaryValue>()); + dict_value->SetBoolean("a", true); // Only update kAtomicPref, kAtomicPref3, and kSplitPref. pref_hash_filter_->FilterUpdate(kAtomicPref); @@ -801,8 +795,7 @@ TEST_P(PrefHashFilterTest, MultiplePrefsFilterSerializeData) { ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count()); // Update kAtomicPref3 again, nothing should be stored still. - base::Value* int_value5 = new base::Value(5); - root_dict.Set(kAtomicPref3, int_value5); + base::Value* int_value5 = root_dict.SetInteger(kAtomicPref3, 5); ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count()); // On FilterSerializeData, only kAtomicPref, kAtomicPref3, and kSplitPref @@ -870,14 +863,13 @@ TEST_P(PrefHashFilterTest, UnknownNullValue) { } TEST_P(PrefHashFilterTest, InitialValueUnknown) { - // Ownership of these values is transfered to |pref_store_contents_|. - base::Value* string_value = new base::Value("string value"); - pref_store_contents_->Set(kAtomicPref, string_value); + base::Value* string_value = + pref_store_contents_->SetString(kAtomicPref, "string value"); - base::DictionaryValue* dict_value = new base::DictionaryValue; + base::DictionaryValue* dict_value = pref_store_contents_->SetDictionary( + kSplitPref, base::MakeUnique<base::DictionaryValue>()); dict_value->SetString("a", "foo"); dict_value->SetInteger("b", 1234); - pref_store_contents_->Set(kSplitPref, dict_value); ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref, NULL)); ASSERT_TRUE(pref_store_contents_->Get(kSplitPref, NULL)); @@ -936,14 +928,13 @@ TEST_P(PrefHashFilterTest, InitialValueUnknown) { } TEST_P(PrefHashFilterTest, InitialValueTrustedUnknown) { - // Ownership of this value is transfered to |pref_store_contents_|. - base::Value* string_value = new base::Value("test"); - pref_store_contents_->Set(kAtomicPref, string_value); + base::Value* string_value = + pref_store_contents_->SetString(kAtomicPref, "test"); - base::DictionaryValue* dict_value = new base::DictionaryValue; + auto* dict_value = pref_store_contents_->SetDictionary( + kSplitPref, base::MakeUnique<base::DictionaryValue>()); dict_value->SetString("a", "foo"); dict_value->SetInteger("b", 1234); - pref_store_contents_->Set(kSplitPref, dict_value); ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref, NULL)); ASSERT_TRUE(pref_store_contents_->Get(kSplitPref, NULL)); @@ -986,16 +977,14 @@ TEST_P(PrefHashFilterTest, InitialValueTrustedUnknown) { } TEST_P(PrefHashFilterTest, InitialValueChanged) { - // Ownership of this value is transfered to |pref_store_contents_|. - base::Value* int_value = new base::Value(1234); - pref_store_contents_->Set(kAtomicPref, int_value); + base::Value* int_value = pref_store_contents_->SetInteger(kAtomicPref, 1234); - base::DictionaryValue* dict_value = new base::DictionaryValue; + base::DictionaryValue* dict_value = pref_store_contents_->SetDictionary( + kSplitPref, base::MakeUnique<base::DictionaryValue>()); dict_value->SetString("a", "foo"); dict_value->SetInteger("b", 1234); dict_value->SetInteger("c", 56); dict_value->SetBoolean("d", false); - pref_store_contents_->Set(kSplitPref, dict_value); ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref, NULL)); ASSERT_TRUE(pref_store_contents_->Get(kSplitPref, NULL)); @@ -1096,14 +1085,13 @@ TEST_P(PrefHashFilterTest, EmptyCleared) { } TEST_P(PrefHashFilterTest, InitialValueUnchangedLegacyId) { - // Ownership of these values is transfered to |pref_store_contents_|. - base::Value* string_value = new base::Value("string value"); - pref_store_contents_->Set(kAtomicPref, string_value); + base::Value* string_value = + pref_store_contents_->SetString(kAtomicPref, "string value"); - base::DictionaryValue* dict_value = new base::DictionaryValue; + base::DictionaryValue* dict_value = pref_store_contents_->SetDictionary( + kSplitPref, base::MakeUnique<base::DictionaryValue>()); dict_value->SetString("a", "foo"); dict_value->SetInteger("b", 1234); - pref_store_contents_->Set(kSplitPref, dict_value); ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref, NULL)); ASSERT_TRUE(pref_store_contents_->Get(kSplitPref, NULL)); @@ -1150,16 +1138,14 @@ TEST_P(PrefHashFilterTest, InitialValueUnchangedLegacyId) { } TEST_P(PrefHashFilterTest, DontResetReportOnly) { - // Ownership of these values is transfered to |pref_store_contents_|. - base::Value* int_value1 = new base::Value(1); - base::Value* int_value2 = new base::Value(2); - base::Value* report_only_val = new base::Value(3); - base::DictionaryValue* report_only_split_val = new base::DictionaryValue; + base::Value* int_value1 = pref_store_contents_->SetInteger(kAtomicPref, 1); + base::Value* int_value2 = pref_store_contents_->SetInteger(kAtomicPref2, 2); + base::Value* report_only_val = + pref_store_contents_->SetInteger(kReportOnlyPref, 3); + base::DictionaryValue* report_only_split_val = + pref_store_contents_->SetDictionary( + kReportOnlySplitPref, base::MakeUnique<base::DictionaryValue>()); report_only_split_val->SetInteger("a", 1234); - pref_store_contents_->Set(kAtomicPref, int_value1); - pref_store_contents_->Set(kAtomicPref2, int_value2); - pref_store_contents_->Set(kReportOnlyPref, report_only_val); - pref_store_contents_->Set(kReportOnlySplitPref, report_only_split_val); ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref, NULL)); ASSERT_TRUE(pref_store_contents_->Get(kAtomicPref2, NULL)); @@ -1224,14 +1210,11 @@ TEST_P(PrefHashFilterTest, DontResetReportOnly) { TEST_P(PrefHashFilterTest, CallFilterSerializeDataCallbacks) { base::DictionaryValue root_dict; - // Ownership of the following values is transfered to |root_dict|. - base::Value* int_value1 = new base::Value(1); - base::Value* int_value2 = new base::Value(2); - base::DictionaryValue* dict_value = new base::DictionaryValue; - dict_value->Set("a", new base::Value(true)); - root_dict.Set(kAtomicPref, int_value1); - root_dict.Set(kAtomicPref2, int_value2); - root_dict.Set(kSplitPref, dict_value); + auto dict_value = base::MakeUnique<base::DictionaryValue>(); + dict_value->SetBoolean("a", true); + root_dict.SetInteger(kAtomicPref, 1); + root_dict.SetInteger(kAtomicPref2, 2); + root_dict.Set(kSplitPref, std::move(dict_value)); // Skip updating kAtomicPref2. pref_hash_filter_->FilterUpdate(kAtomicPref); @@ -1273,9 +1256,7 @@ TEST_P(PrefHashFilterTest, CallFilterSerializeDataCallbacks) { TEST_P(PrefHashFilterTest, CallFilterSerializeDataCallbacksWithFailure) { base::DictionaryValue root_dict; - // Ownership of the following values is transfered to |root_dict|. - base::Value* int_value1 = new base::Value(1); - root_dict.Set(kAtomicPref, int_value1); + root_dict.SetInteger(kAtomicPref, 1); // Only update kAtomicPref. pref_hash_filter_->FilterUpdate(kAtomicPref); @@ -1301,16 +1282,14 @@ TEST_P(PrefHashFilterTest, CallFilterSerializeDataCallbacksWithFailure) { } TEST_P(PrefHashFilterTest, ExternalValidationValueChanged) { - // Ownership of this value is transfered to |pref_store_contents_|. - base::Value* int_value = new base::Value(1234); - pref_store_contents_->Set(kAtomicPref, int_value); + pref_store_contents_->SetInteger(kAtomicPref, 1234); - base::DictionaryValue* dict_value = new base::DictionaryValue; + auto dict_value = base::MakeUnique<base::DictionaryValue>(); dict_value->SetString("a", "foo"); dict_value->SetInteger("b", 1234); dict_value->SetInteger("c", 56); dict_value->SetBoolean("d", false); - pref_store_contents_->Set(kSplitPref, dict_value); + pref_store_contents_->Set(kSplitPref, std::move(dict_value)); mock_external_validation_pref_hash_store_->SetCheckResult( kAtomicPref, ValueState::CHANGED); diff --git a/chromium/services/preferences/tracked/pref_hash_store_impl.cc b/chromium/services/preferences/tracked/pref_hash_store_impl.cc index 522b013c5bc..f0cfd319685 100644 --- a/chromium/services/preferences/tracked/pref_hash_store_impl.cc +++ b/chromium/services/preferences/tracked/pref_hash_store_impl.cc @@ -90,7 +90,7 @@ PrefHashStoreImpl::~PrefHashStoreImpl() {} std::unique_ptr<PrefHashStoreTransaction> PrefHashStoreImpl::BeginTransaction( HashStoreContents* storage) { return std::unique_ptr<PrefHashStoreTransaction>( - new PrefHashStoreTransactionImpl(this, std::move(storage))); + new PrefHashStoreTransactionImpl(this, storage)); } std::string PrefHashStoreImpl::ComputeMac(const std::string& path, @@ -126,7 +126,7 @@ PrefHashStoreImpl::PrefHashStoreTransactionImpl::PrefHashStoreTransactionImpl( PrefHashStoreImpl* outer, HashStoreContents* storage) : outer_(outer), - contents_(std::move(storage)), + contents_(storage), super_mac_valid_(false), super_mac_dirty_(false) { if (!outer_->use_super_mac_) diff --git a/chromium/services/preferences/tracked/pref_hash_store_impl_unittest.cc b/chromium/services/preferences/tracked/pref_hash_store_impl_unittest.cc index 3c6ca13126f..18014a8485f 100644 --- a/chromium/services/preferences/tracked/pref_hash_store_impl_unittest.cc +++ b/chromium/services/preferences/tracked/pref_hash_store_impl_unittest.cc @@ -51,8 +51,8 @@ TEST_F(PrefHashStoreImplTest, ComputeMac) { TEST_F(PrefHashStoreImplTest, ComputeSplitMacs) { base::DictionaryValue dict; - dict.Set("a", new base::Value("string1")); - dict.Set("b", new base::Value("string2")); + dict.SetString("a", "string1"); + dict.SetString("b", "string2"); PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); std::unique_ptr<base::DictionaryValue> computed_macs = @@ -96,10 +96,10 @@ TEST_F(PrefHashStoreImplTest, AtomicHashStoreAndCheck) { EXPECT_EQ(ValueState::CHANGED, transaction->CheckValue("path1", &string_2)); base::DictionaryValue dict; - dict.Set("a", new base::Value("foo")); - dict.Set("d", new base::Value("bad")); - dict.Set("b", new base::Value("bar")); - dict.Set("c", new base::Value("baz")); + dict.SetString("a", "foo"); + dict.SetString("d", "bad"); + dict.SetString("b", "bar"); + dict.SetString("c", "baz"); transaction->StoreHash("path1", &dict); EXPECT_EQ(ValueState::UNCHANGED, transaction->CheckValue("path1", &dict)); @@ -311,14 +311,14 @@ TEST_F(PrefHashStoreImplTest, SuperMACDisabled) { TEST_F(PrefHashStoreImplTest, SplitHashStoreAndCheck) { base::DictionaryValue dict; - dict.Set("a", new base::Value("to be replaced")); - dict.Set("b", new base::Value("same")); - dict.Set("o", new base::Value("old")); + dict.SetString("a", "to be replaced"); + dict.SetString("b", "same"); + dict.SetString("o", "old"); base::DictionaryValue modified_dict; - modified_dict.Set("a", new base::Value("replaced")); - modified_dict.Set("b", new base::Value("same")); - modified_dict.Set("c", new base::Value("new")); + modified_dict.SetString("a", "replaced"); + modified_dict.SetString("b", "same"); + modified_dict.SetString("c", "new"); base::DictionaryValue empty_dict; @@ -428,7 +428,7 @@ TEST_F(PrefHashStoreImplTest, EmptyAndNULLSplitDict) { // Store hashes for a random dict to be overwritten below. base::DictionaryValue initial_dict; - initial_dict.Set("a", new base::Value("foo")); + initial_dict.SetString("a", "foo"); transaction->StoreSplitHash("path1", &initial_dict); // Verify stored empty dictionary matches NULL and empty dictionary back. @@ -461,8 +461,8 @@ TEST_F(PrefHashStoreImplTest, EmptyAndNULLSplitDict) { pref_hash_store2.BeginTransaction(GetHashStoreContents())); base::DictionaryValue tested_dict; - tested_dict.Set("a", new base::Value("foo")); - tested_dict.Set("b", new base::Value("bar")); + tested_dict.SetString("a", "foo"); + tested_dict.SetString("b", "bar"); EXPECT_EQ( ValueState::TRUSTED_UNKNOWN_VALUE, transaction->CheckSplitValue("new_path", &tested_dict, &invalid_keys)); @@ -480,10 +480,10 @@ TEST_F(PrefHashStoreImplTest, TrustedUnknownSplitValueFromExistingAtomic) { base::Value string("string1"); base::DictionaryValue dict; - dict.Set("a", new base::Value("foo")); - dict.Set("d", new base::Value("bad")); - dict.Set("b", new base::Value("bar")); - dict.Set("c", new base::Value("baz")); + dict.SetString("a", "foo"); + dict.SetString("d", "bad"); + dict.SetString("b", "bar"); + dict.SetString("c", "baz"); { PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true); diff --git a/chromium/services/preferences/tracked/registry_hash_store_contents_win.cc b/chromium/services/preferences/tracked/registry_hash_store_contents_win.cc index 76ee57f3cd1..354a5bfac4b 100644 --- a/chromium/services/preferences/tracked/registry_hash_store_contents_win.cc +++ b/chromium/services/preferences/tracked/registry_hash_store_contents_win.cc @@ -6,12 +6,10 @@ #include <windows.h> -#include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_split.h" -#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" @@ -68,21 +66,43 @@ bool ClearSplitMac(const base::string16& reg_key_name, return false; } +// Deletes |reg_key_name| if it exists. +void DeleteRegistryKey(const base::string16& reg_key_name) { + base::win::RegKey key; + if (key.Open(HKEY_CURRENT_USER, reg_key_name.c_str(), + KEY_SET_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) { + LONG result = key.DeleteKey(L""); + DCHECK(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND) << result; + } +} + } // namespace +void TempScopedDirRegistryCleaner::SetRegistryPath( + const base::string16& registry_path) { + if (registry_path_.empty()) + registry_path_ = registry_path; + else + DCHECK_EQ(registry_path_, registry_path); +} + +TempScopedDirRegistryCleaner::~TempScopedDirRegistryCleaner() { + DCHECK(!registry_path_.empty()); + DeleteRegistryKey(registry_path_); +} + RegistryHashStoreContentsWin::RegistryHashStoreContentsWin( const base::string16& registry_path, - const base::string16& store_key) + const base::string16& store_key, + scoped_refptr<TempScopedDirCleaner> temp_dir_cleaner) : preference_key_name_(registry_path + L"\\PreferenceMACs\\" + store_key), - reset_on_delete_(base::StartsWith(store_key, - base::ScopedTempDir::GetTempDirPrefix(), - base::CompareCase::INSENSITIVE_ASCII)) { + temp_dir_cleaner_(std::move(temp_dir_cleaner)) { + if (temp_dir_cleaner_) + static_cast<TempScopedDirRegistryCleaner*>(temp_dir_cleaner_.get()) + ->SetRegistryPath(preference_key_name_); } -RegistryHashStoreContentsWin::~RegistryHashStoreContentsWin() { - if (reset_on_delete_) - Reset(); -} +RegistryHashStoreContentsWin::~RegistryHashStoreContentsWin() = default; RegistryHashStoreContentsWin::RegistryHashStoreContentsWin( const RegistryHashStoreContentsWin& other) = default; @@ -101,12 +121,7 @@ base::StringPiece RegistryHashStoreContentsWin::GetUMASuffix() const { } void RegistryHashStoreContentsWin::Reset() { - base::win::RegKey key; - if (key.Open(HKEY_CURRENT_USER, preference_key_name_.c_str(), - KEY_SET_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) { - LONG result = key.DeleteKey(L""); - DCHECK(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND) << result; - } + DeleteRegistryKey(preference_key_name_); } bool RegistryHashStoreContentsWin::GetMac(const std::string& path, diff --git a/chromium/services/preferences/tracked/registry_hash_store_contents_win.h b/chromium/services/preferences/tracked/registry_hash_store_contents_win.h index 0b3b396205d..dd556698157 100644 --- a/chromium/services/preferences/tracked/registry_hash_store_contents_win.h +++ b/chromium/services/preferences/tracked/registry_hash_store_contents_win.h @@ -8,18 +8,29 @@ #include "base/macros.h" #include "base/strings/string16.h" #include "services/preferences/tracked/hash_store_contents.h" +#include "services/preferences/tracked/temp_scoped_dir_cleaner.h" + +// Helper object to clear registry entries for scoped temporary pref stores. +class TempScopedDirRegistryCleaner : public TempScopedDirCleaner { + public: + void SetRegistryPath(const base::string16& registry_path); + + private: + friend class base::RefCountedThreadSafe<TempScopedDirRegistryCleaner>; + ~TempScopedDirRegistryCleaner() override; + + base::string16 registry_path_; +}; // Implements HashStoreContents by storing MACs in the Windows registry. class RegistryHashStoreContentsWin : public HashStoreContents { public: // Constructs a RegistryHashStoreContents which acts on a registry entry - // defined by |registry_path| and |store_key|. If |store_key| begins with - // base::ScopedTempDir::GetTempDirPrefix(), this RegistryHashStoreContentsWin - // will self Reset() on destruction to avoid proliferating keys in tests that - // create a profile in a ScopedTempDir (https://crbug.com/721245). - explicit RegistryHashStoreContentsWin(const base::string16& registry_path, - const base::string16& store_key); - + // defined by |registry_path| and |store_key|. + explicit RegistryHashStoreContentsWin( + const base::string16& registry_path, + const base::string16& store_key, + scoped_refptr<TempScopedDirCleaner> temp_dir_cleaner); ~RegistryHashStoreContentsWin() override; // HashStoreContents overrides: @@ -49,7 +60,7 @@ class RegistryHashStoreContentsWin : public HashStoreContents { const RegistryHashStoreContentsWin& other); const base::string16 preference_key_name_; - const bool reset_on_delete_; + scoped_refptr<TempScopedDirCleaner> temp_dir_cleaner_; }; #endif // SERVICES_PREFERENCES_TRACKED_REGISTRY_HASH_STORE_CONTENTS_WIN_H_ diff --git a/chromium/services/preferences/tracked/registry_hash_store_contents_win_unittest.cc b/chromium/services/preferences/tracked/registry_hash_store_contents_win_unittest.cc index 468843437fb..d85fdbf1e8b 100644 --- a/chromium/services/preferences/tracked/registry_hash_store_contents_win_unittest.cc +++ b/chromium/services/preferences/tracked/registry_hash_store_contents_win_unittest.cc @@ -4,9 +4,13 @@ #include "services/preferences/tracked/registry_hash_store_contents_win.h" +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ptr_util.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_reg_util_win.h" +#include "base/threading/thread.h" #include "base/values.h" #include "base/win/registry.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,7 +37,8 @@ class RegistryHashStoreContentsWinTest : public testing::Test { ASSERT_NO_FATAL_FAILURE( registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER)); - contents.reset(new RegistryHashStoreContentsWin(kRegistryPath, kStoreKey)); + contents.reset( + new RegistryHashStoreContentsWin(kRegistryPath, kStoreKey, nullptr)); } std::unique_ptr<HashStoreContents> contents; @@ -118,3 +123,90 @@ TEST_F(RegistryHashStoreContentsWinTest, TestReset) { EXPECT_FALSE(contents->GetSplitMacs(kSplitPrefPath, &split_macs)); EXPECT_EQ(0U, split_macs.size()); } + +TEST(RegistryHashStoreContentsWinScopedTest, TestScopedDirsCleared) { + std::string stored_mac; + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + const base::string16 registry_path = + temp_dir.GetPath().DirName().BaseName().LossyDisplayName(); + + RegistryHashStoreContentsWin verifying_contents(registry_path, kStoreKey, + nullptr); + + scoped_refptr<TempScopedDirRegistryCleaner> temp_scoped_dir_cleaner = + base::MakeRefCounted<TempScopedDirRegistryCleaner>(); + std::unique_ptr<RegistryHashStoreContentsWin> contentsA = + base::MakeUnique<RegistryHashStoreContentsWin>(registry_path, kStoreKey, + temp_scoped_dir_cleaner); + std::unique_ptr<RegistryHashStoreContentsWin> contentsB = + base::MakeUnique<RegistryHashStoreContentsWin>(registry_path, kStoreKey, + temp_scoped_dir_cleaner); + + contentsA->SetMac(kAtomicPrefPath, kTestStringA); + contentsB->SetMac(kAtomicPrefPath, kTestStringB); + + temp_scoped_dir_cleaner = nullptr; + EXPECT_TRUE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); + EXPECT_EQ(kTestStringB, stored_mac); + + contentsB.reset(); + EXPECT_TRUE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); + EXPECT_EQ(kTestStringB, stored_mac); + + contentsA.reset(); + EXPECT_FALSE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); +} + +void OffThreadTempScopedDirDestructor( + base::string16 registry_path, + std::unique_ptr<HashStoreContents> contents) { + std::string stored_mac; + + RegistryHashStoreContentsWin verifying_contents(registry_path, kStoreKey, + nullptr); + + contents->SetMac(kAtomicPrefPath, kTestStringB); + EXPECT_TRUE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); + EXPECT_EQ(kTestStringB, stored_mac); + + contents.reset(); + EXPECT_FALSE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); +} + +TEST(RegistryHashStoreContentsWinScopedTest, TestScopedDirsClearedMultiThread) { + std::string stored_mac; + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + const base::string16 registry_path = + temp_dir.GetPath().DirName().BaseName().LossyDisplayName(); + + RegistryHashStoreContentsWin verifying_contents(registry_path, kStoreKey, + nullptr); + + base::Thread test_thread("scoped_dir_cleaner_test_thread"); + test_thread.StartAndWaitForTesting(); + + scoped_refptr<TempScopedDirRegistryCleaner> temp_scoped_dir_cleaner = + base::MakeRefCounted<TempScopedDirRegistryCleaner>(); + std::unique_ptr<RegistryHashStoreContentsWin> contents = + base::MakeUnique<RegistryHashStoreContentsWin>( + registry_path, kStoreKey, std::move(temp_scoped_dir_cleaner)); + base::OnceClosure other_thread_closure = + base::BindOnce(&OffThreadTempScopedDirDestructor, registry_path, + base::Passed(contents->MakeCopy())); + + contents->SetMac(kAtomicPrefPath, kTestStringA); + contents.reset(); + + EXPECT_TRUE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); + EXPECT_EQ(kTestStringA, stored_mac); + + test_thread.task_runner()->PostTask(FROM_HERE, + std::move(other_thread_closure)); + test_thread.FlushForTesting(); + + EXPECT_FALSE(verifying_contents.GetMac(kAtomicPrefPath, &stored_mac)); +} diff --git a/chromium/services/preferences/tracked/segregated_pref_store.cc b/chromium/services/preferences/tracked/segregated_pref_store.cc index af7349e0382..ced018e7c67 100644 --- a/chromium/services/preferences/tracked/segregated_pref_store.cc +++ b/chromium/services/preferences/tracked/segregated_pref_store.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/barrier_closure.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/values.h" @@ -160,9 +161,12 @@ void SegregatedPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { selected_pref_store_->ReadPrefsAsync(NULL); } -void SegregatedPrefStore::CommitPendingWrite() { - default_pref_store_->CommitPendingWrite(); - selected_pref_store_->CommitPendingWrite(); +void SegregatedPrefStore::CommitPendingWrite(base::OnceClosure done_callback) { + base::RepeatingClosure done_callback_wrapper = + done_callback ? base::BarrierClosure(2, std::move(done_callback)) + : base::RepeatingClosure(); + default_pref_store_->CommitPendingWrite(done_callback_wrapper); + selected_pref_store_->CommitPendingWrite(done_callback_wrapper); } void SegregatedPrefStore::SchedulePendingLossyWrites() { diff --git a/chromium/services/preferences/tracked/segregated_pref_store.h b/chromium/services/preferences/tracked/segregated_pref_store.h index 3621e49aa07..a2b714a4812 100644 --- a/chromium/services/preferences/tracked/segregated_pref_store.h +++ b/chromium/services/preferences/tracked/segregated_pref_store.h @@ -72,7 +72,7 @@ class SegregatedPrefStore : public PersistentPrefStore { PrefReadError GetReadError() const override; PrefReadError ReadPrefs() override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; - void CommitPendingWrite() override; + void CommitPendingWrite(base::OnceClosure done_callback) override; void SchedulePendingLossyWrites() override; void ClearMutableValues() override; diff --git a/chromium/services/preferences/tracked/segregated_pref_store_unittest.cc b/chromium/services/preferences/tracked/segregated_pref_store_unittest.cc index a237f38285e..d10903620a2 100644 --- a/chromium/services/preferences/tracked/segregated_pref_store_unittest.cc +++ b/chromium/services/preferences/tracked/segregated_pref_store_unittest.cc @@ -13,6 +13,8 @@ #include "base/callback.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" #include "base/values.h" #include "components/prefs/persistent_pref_store.h" #include "components/prefs/pref_store_observer_mock.h" @@ -54,9 +56,13 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { Data* data_; }; -} // namespace +enum class CommitPendingWriteMode { + WITHOUT_CALLBACK, + WITH_CALLBACK, +}; -class SegregatedPrefStoreTest : public testing::Test { +class SegregatedPrefStoreTest + : public testing::TestWithParam<CommitPendingWriteMode> { public: SegregatedPrefStoreTest() : read_error_delegate_data_(false, @@ -87,6 +93,8 @@ class SegregatedPrefStoreTest : public testing::Test { return std::move(read_error_delegate_); } + base::test::ScopedTaskEnvironment scoped_task_environment_; + PrefStoreObserverMock observer_; scoped_refptr<TestingPrefStore> default_store_; @@ -99,7 +107,9 @@ class SegregatedPrefStoreTest : public testing::Test { std::unique_ptr<MockReadErrorDelegate> read_error_delegate_; }; -TEST_F(SegregatedPrefStoreTest, StoreValues) { +} // namespace + +TEST_P(SegregatedPrefStoreTest, StoreValues) { ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, segregated_store_->ReadPrefs()); @@ -122,7 +132,14 @@ TEST_F(SegregatedPrefStoreTest, StoreValues) { ASSERT_FALSE(selected_store_->committed()); ASSERT_FALSE(default_store_->committed()); - segregated_store_->CommitPendingWrite(); + if (GetParam() == CommitPendingWriteMode::WITHOUT_CALLBACK) { + segregated_store_->CommitPendingWrite(base::OnceClosure()); + base::RunLoop().RunUntilIdle(); + } else { + base::RunLoop run_loop; + segregated_store_->CommitPendingWrite(run_loop.QuitClosure()); + run_loop.Run(); + } ASSERT_TRUE(selected_store_->committed()); ASSERT_TRUE(default_store_->committed()); @@ -302,3 +319,12 @@ TEST_F(SegregatedPrefStoreTest, GetValues) { ASSERT_TRUE(values->Get(kSharedPref, &value)); EXPECT_TRUE(base::Value(kValue1).Equals(value)); } + +INSTANTIATE_TEST_CASE_P( + WithoutCallback, + SegregatedPrefStoreTest, + ::testing::Values(CommitPendingWriteMode::WITHOUT_CALLBACK)); +INSTANTIATE_TEST_CASE_P( + WithCallback, + SegregatedPrefStoreTest, + ::testing::Values(CommitPendingWriteMode::WITH_CALLBACK)); diff --git a/chromium/services/preferences/tracked/temp_scoped_dir_cleaner.h b/chromium/services/preferences/tracked/temp_scoped_dir_cleaner.h new file mode 100644 index 00000000000..4359b760bd1 --- /dev/null +++ b/chromium/services/preferences/tracked/temp_scoped_dir_cleaner.h @@ -0,0 +1,18 @@ +// Copyright 2017 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 SERVICES_PREFERENCES_TRACKED_TEMP_SCOPED_DIR_CLEANER_H_ +#define SERVICES_PREFERENCES_TRACKED_TEMP_SCOPED_DIR_CLEANER_H_ + +#include "base/memory/ref_counted.h" + +// Helper object to clear additional data for scoped temporary pref stores. +class TempScopedDirCleaner + : public base::RefCountedThreadSafe<TempScopedDirCleaner> { + protected: + friend class base::RefCountedThreadSafe<TempScopedDirCleaner>; + virtual ~TempScopedDirCleaner(){}; +}; + +#endif // SERVICES_PREFERENCES_TRACKED_TEMP_SCOPED_DIR_CLEANER_H_ diff --git a/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.cc b/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.cc index ffb5ab2e9ac..2c32319d946 100644 --- a/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.cc +++ b/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.cc @@ -16,9 +16,12 @@ #include "services/preferences/tracked/pref_hash_filter.h" #include "services/preferences/tracked/pref_hash_store_impl.h" #include "services/preferences/tracked/segregated_pref_store.h" +#include "services/preferences/tracked/temp_scoped_dir_cleaner.h" #include "services/preferences/tracked/tracked_preferences_migration.h" #if defined(OS_WIN) +#include "base/files/scoped_temp_dir.h" +#include "base/strings/string_util.h" #include "services/preferences/tracked/registry_hash_store_contents_win.h" #endif @@ -41,16 +44,18 @@ std::unique_ptr<PrefHashStore> CreatePrefHashStore( std::pair<std::unique_ptr<PrefHashStore>, std::unique_ptr<HashStoreContents>> GetExternalVerificationPrefHashStorePair( - const prefs::mojom::TrackedPersistentPrefStoreConfiguration& config) { + const prefs::mojom::TrackedPersistentPrefStoreConfiguration& config, + scoped_refptr<TempScopedDirCleaner> temp_dir_cleaner) { #if defined(OS_WIN) - return std::make_pair( - base::MakeUnique<PrefHashStoreImpl>(config.registry_seed, - config.legacy_device_id, - false /* use_super_mac */), - base::MakeUnique<RegistryHashStoreContentsWin>( - config.registry_path, config.unprotected_pref_filename.DirName() - .BaseName() - .LossyDisplayName())); + return std::make_pair(base::MakeUnique<PrefHashStoreImpl>( + config.registry_seed, config.legacy_device_id, + false /* use_super_mac */), + base::MakeUnique<RegistryHashStoreContentsWin>( + config.registry_path, + config.unprotected_pref_filename.DirName() + .BaseName() + .LossyDisplayName(), + std::move(temp_dir_cleaner))); #else return std::make_pair(nullptr, nullptr); #endif @@ -60,10 +65,7 @@ GetExternalVerificationPrefHashStorePair( PersistentPrefStore* CreateTrackedPersistentPrefStore( prefs::mojom::TrackedPersistentPrefStoreConfigurationPtr config, - base::SequencedWorkerPool* worker_pool) { - auto io_task_runner = JsonPrefStore::GetTaskRunnerForFile( - config->unprotected_pref_filename.DirName(), worker_pool); - + scoped_refptr<base::SequencedTaskRunner> io_task_runner) { std::vector<prefs::mojom::TrackedPreferenceMetadataPtr> unprotected_configuration; std::vector<prefs::mojom::TrackedPreferenceMetadataPtr> @@ -82,15 +84,33 @@ PersistentPrefStore* CreateTrackedPersistentPrefStore( } config->tracking_configuration.clear(); + scoped_refptr<TempScopedDirCleaner> temp_scoped_dir_cleaner; +#if defined(OS_WIN) + // For tests that create a profile in a ScopedTempDir, share a ref_counted + // object between the unprotected and protected hash filter's + // RegistryHashStoreContentsWin which will clear the registry keys when + // destroyed. (https://crbug.com/721245) + if (base::StartsWith(config->unprotected_pref_filename.DirName() + .BaseName() + .LossyDisplayName(), + base::ScopedTempDir::GetTempDirPrefix(), + base::CompareCase::INSENSITIVE_ASCII)) { + temp_scoped_dir_cleaner = + base::MakeRefCounted<TempScopedDirRegistryCleaner>(); + } +#endif + std::unique_ptr<PrefHashFilter> unprotected_pref_hash_filter( new PrefHashFilter(CreatePrefHashStore(*config, false), - GetExternalVerificationPrefHashStorePair(*config), + GetExternalVerificationPrefHashStorePair( + *config, temp_scoped_dir_cleaner), unprotected_configuration, nullptr, config->validation_delegate.get(), config->reporting_ids_count, false)); std::unique_ptr<PrefHashFilter> protected_pref_hash_filter(new PrefHashFilter( CreatePrefHashStore(*config, true), - GetExternalVerificationPrefHashStorePair(*config), + GetExternalVerificationPrefHashStorePair(*config, + temp_scoped_dir_cleaner), protected_configuration, std::move(config->reset_on_load_observer), config->validation_delegate.get(), config->reporting_ids_count, true)); @@ -125,9 +145,10 @@ PersistentPrefStore* CreateTrackedPersistentPrefStore( void InitializeMasterPrefsTracking( prefs::mojom::TrackedPersistentPrefStoreConfigurationPtr configuration, base::DictionaryValue* master_prefs) { - PrefHashFilter(CreatePrefHashStore(*configuration, false), - GetExternalVerificationPrefHashStorePair(*configuration), - configuration->tracking_configuration, nullptr, nullptr, - configuration->reporting_ids_count, false) + PrefHashFilter( + CreatePrefHashStore(*configuration, false), + GetExternalVerificationPrefHashStorePair(*configuration, nullptr), + configuration->tracking_configuration, nullptr, nullptr, + configuration->reporting_ids_count, false) .Initialize(master_prefs); } diff --git a/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.h b/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.h index fb426bab47d..ca08959b9fd 100644 --- a/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.h +++ b/chromium/services/preferences/tracked/tracked_persistent_pref_store_factory.h @@ -6,18 +6,17 @@ #define SERVICES_PREFERENCES_TRACKED_TRACKED_PERSISTENT_PREF_STORE_FACTORY_H_ #include <utility> -#include "services/preferences/public/interfaces/preferences_configuration.mojom.h" +#include "services/preferences/public/interfaces/preferences.mojom.h" namespace base { class DictionaryValue; -class SequencedWorkerPool; } class PersistentPrefStore; PersistentPrefStore* CreateTrackedPersistentPrefStore( prefs::mojom::TrackedPersistentPrefStoreConfigurationPtr config, - base::SequencedWorkerPool* worker_pool); + scoped_refptr<base::SequencedTaskRunner> io_task_runner); // TODO(sammc): This should move somewhere more appropriate in the longer term. void InitializeMasterPrefsTracking( diff --git a/chromium/services/preferences/tracked/tracked_preferences_migration.cc b/chromium/services/preferences/tracked/tracked_preferences_migration.cc index f2af336e350..61a0c80cf0b 100644 --- a/chromium/services/preferences/tracked/tracked_preferences_migration.cc +++ b/chromium/services/preferences/tracked/tracked_preferences_migration.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/metrics/histogram.h" #include "base/values.h" @@ -164,7 +165,8 @@ void MigratePrefsFromOldToNewStore(const std::set<std::string>& pref_names, // |new_store| having equivalently been successfully flushed to disk // (e.g., on crash or in cases where |new_store| is read-only following // a read error on startup). - new_store->Set(pref_name, value_in_old_store->DeepCopy()); + new_store->Set(pref_name, + base::MakeUnique<base::Value>(*value_in_old_store)); migrated_value = true; *new_store_altered = true; } diff --git a/chromium/services/preferences/unittest_helper_manifest.json b/chromium/services/preferences/unittest_helper_manifest.json new file mode 100644 index 00000000000..2c46470b16d --- /dev/null +++ b/chromium/services/preferences/unittest_helper_manifest.json @@ -0,0 +1,14 @@ +{ + "name": "prefs_unittest_helper", + "display_name": "Prefs Unittest helper", + "interface_provider_specs": { + "service_manager:connector": { + "provides": { + "test": [ "*" ] + }, + "requires": { + "preferences": [ "pref_client" ] + } + } + } +} diff --git a/chromium/services/preferences/unittest_manifest.json b/chromium/services/preferences/unittest_manifest.json index df6d7441066..5354cb88376 100644 --- a/chromium/services/preferences/unittest_manifest.json +++ b/chromium/services/preferences/unittest_manifest.json @@ -9,7 +9,8 @@ ] }, "requires": { - "preferences": [ "pref_client", "pref_control" ] + "preferences": [ "pref_client" ], + "prefs_unittest_helper": [ "test" ] } } } |