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(); |