| Index: services/preferences/public/cpp/persistent_pref_store_client.cc
|
| diff --git a/services/preferences/public/cpp/persistent_pref_store_client.cc b/services/preferences/public/cpp/persistent_pref_store_client.cc
|
| index 9bcb92fb1b6e3859e745c8360fc93dea0bcf42c5..c152b8471ada48cfb1ed32aeafb1492a7bb109f5 100644
|
| --- a/services/preferences/public/cpp/persistent_pref_store_client.cc
|
| +++ b/services/preferences/public/cpp/persistent_pref_store_client.cc
|
| @@ -4,14 +4,59 @@
|
|
|
| #include "services/preferences/public/cpp/persistent_pref_store_client.h"
|
|
|
| -#include <utility>
|
| -
|
| #include "base/values.h"
|
| #include "components/prefs/pref_registry.h"
|
| +#include "mojo/common/values.mojom.h"
|
| +#include "mojo/common/values_struct_traits.h"
|
| #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
|
| #include "services/preferences/public/cpp/pref_registry_serializer.h"
|
|
|
| namespace prefs {
|
| +namespace {
|
| +
|
| +const base::Value* LookupPath(const base::Value* root,
|
| + const std::vector<std::string>& path_components) {
|
| + const base::DictionaryValue* dictionary_value = nullptr;
|
| + if (!root->GetAsDictionary(&dictionary_value))
|
| + NOTREACHED();
|
| +
|
| + for (size_t i = 0; i < path_components.size() - 1; ++i) {
|
| + if (!dictionary_value->GetDictionaryWithoutPathExpansion(
|
| + path_components[i], &dictionary_value)) {
|
| + return nullptr;
|
| + }
|
| + }
|
| + const base::Value* result = nullptr;
|
| + dictionary_value->GetWithoutPathExpansion(path_components.back(), &result);
|
| + return result;
|
| +}
|
| +
|
| +template <typename StringType>
|
| +bool IsPrefix(const std::vector<StringType>& prefix,
|
| + const std::vector<StringType>& full_path) {
|
| + if (prefix.size() >= full_path.size())
|
| + return false;
|
| +
|
| + for (size_t i = 0; i < prefix.size(); i++) {
|
| + if (prefix[i] != full_path[i])
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void RemoveRedundantPaths(std::set<std::vector<std::string>>* updated_paths) {
|
| + for (auto it = updated_paths->begin(), previous_it = updated_paths->end();
|
| + it != updated_paths->end();) {
|
| + if (previous_it != updated_paths->end() && IsPrefix(*previous_it, *it)) {
|
| + it = updated_paths->erase(it);
|
| + } else {
|
| + previous_it = it;
|
| + ++it;
|
| + }
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
|
|
| PersistentPrefStoreClient::PersistentPrefStoreClient(
|
| mojom::PrefStoreConnectorPtr connector,
|
| @@ -57,10 +102,17 @@ bool PersistentPrefStoreClient::GetMutableValue(const std::string& key,
|
| void PersistentPrefStoreClient::ReportValueChanged(const std::string& key,
|
| uint32_t flags) {
|
| DCHECK(pref_store_);
|
| - const base::Value* local_value = nullptr;
|
| - GetMutableValues().Get(key, &local_value);
|
|
|
| - QueueWrite(key, flags);
|
| + ReportSubValuesChanged(
|
| + key, std::set<std::vector<std::string>>{std::vector<std::string>{}},
|
| + flags);
|
| +}
|
| +
|
| +void PersistentPrefStoreClient::ReportSubValuesChanged(
|
| + const std::string& key,
|
| + std::set<std::vector<std::string>> path_components,
|
| + uint32_t flags) {
|
| + QueueWrite(key, std::move(path_components), flags);
|
| ReportPrefValueChanged(key);
|
| }
|
|
|
| @@ -69,8 +121,10 @@ void PersistentPrefStoreClient::SetValueSilently(
|
| std::unique_ptr<base::Value> value,
|
| uint32_t flags) {
|
| DCHECK(pref_store_);
|
| - QueueWrite(key, flags);
|
| GetMutableValues().Set(key, std::move(value));
|
| + QueueWrite(key,
|
| + std::set<std::vector<std::string>>{std::vector<std::string>{}},
|
| + flags);
|
| }
|
|
|
| bool PersistentPrefStoreClient::ReadOnly() const {
|
| @@ -153,29 +207,58 @@ void PersistentPrefStoreClient::OnConnect(
|
| }
|
| }
|
|
|
| -void PersistentPrefStoreClient::QueueWrite(const std::string& key,
|
| - uint32_t flags) {
|
| +void PersistentPrefStoreClient::QueueWrite(
|
| + const std::string& key,
|
| + std::set<std::vector<std::string>> path_components,
|
| + uint32_t flags) {
|
| + DCHECK(!path_components.empty());
|
| if (pending_writes_.empty()) {
|
| // Use a weak pointer since a pending write should not prolong the life of
|
| - // |this|. Instead, the destruction of |this| will flush any pending writes.
|
| + // |this|. Instead, the destruction of |this| will flush any pending
|
| + // writes.
|
| base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| FROM_HERE, base::Bind(&PersistentPrefStoreClient::FlushPendingWrites,
|
| weak_factory_.GetWeakPtr()));
|
| }
|
| - pending_writes_.insert(std::make_pair(key, flags));
|
| + RemoveRedundantPaths(&path_components);
|
| + auto& entry = pending_writes_[key];
|
| + entry.second = flags;
|
| + for (auto& path : path_components) {
|
| + entry.first.insert(std::move(path));
|
| + }
|
| }
|
|
|
| void PersistentPrefStoreClient::FlushPendingWrites() {
|
| std::vector<mojom::PrefUpdatePtr> updates;
|
| - for (const auto& pref : pending_writes_) {
|
| + for (auto& pref : pending_writes_) {
|
| + auto update_value = mojom::PrefUpdateValue::New();
|
| const base::Value* value = nullptr;
|
| if (GetValue(pref.first, &value)) {
|
| - updates.push_back(mojom::PrefUpdate::New(
|
| - pref.first, value->CreateDeepCopy(), pref.second));
|
| + std::vector<mojom::SubPrefUpdatePtr> pref_updates;
|
| + RemoveRedundantPaths(&pref.second.first);
|
| + for (const auto& path : pref.second.first) {
|
| + if (path.empty()) {
|
| + pref_updates.clear();
|
| + break;
|
| + }
|
| + const base::Value* nested_value = LookupPath(value, path);
|
| + if (nested_value) {
|
| + pref_updates.emplace_back(base::in_place, path,
|
| + nested_value->CreateDeepCopy());
|
| + } else {
|
| + pref_updates.emplace_back(base::in_place, path, nullptr);
|
| + }
|
| + }
|
| + if (pref_updates.empty()) {
|
| + update_value->set_atomic_update(value->CreateDeepCopy());
|
| + } else {
|
| + update_value->set_split_updates(std::move(pref_updates));
|
| + }
|
| } else {
|
| - updates.push_back(
|
| - mojom::PrefUpdate::New(pref.first, nullptr, pref.second));
|
| + update_value->set_atomic_update(nullptr);
|
| }
|
| + updates.emplace_back(base::in_place, pref.first, std::move(update_value),
|
| + pref.second.second);
|
| }
|
| pref_store_->SetValues(std::move(updates));
|
| pending_writes_.clear();
|
|
|