1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/sync_preferences/pref_model_associator.h"
12 #include "base/auto_reset.h"
13 #include "base/callback.h"
14 #include "base/containers/contains.h"
15 #include "base/feature_list.h"
16 #include "base/json/json_reader.h"
17 #include "base/json/json_string_value_serializer.h"
18 #include "base/location.h"
19 #include "base/logging.h"
20 #include "base/memory/ptr_util.h"
21 #include "base/observer_list.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/values.h"
24 #include "build/chromeos_buildflags.h"
25 #include "components/prefs/persistent_pref_store.h"
26 #include "components/prefs/pref_service.h"
27 #include "components/sync/base/model_type.h"
28 #include "components/sync/model/sync_change.h"
29 #include "components/sync/model/sync_change_processor.h"
30 #include "components/sync/model/sync_error_factory.h"
31 #include "components/sync/protocol/entity_specifics.pb.h"
32 #include "components/sync/protocol/preference_specifics.pb.h"
33 #include "components/sync_preferences/pref_model_associator_client.h"
34 #include "components/sync_preferences/pref_service_syncable.h"
36 namespace sync_preferences {
40 const sync_pb::PreferenceSpecifics& GetSpecifics(const syncer::SyncData& pref) {
41 switch (pref.GetDataType()) {
42 case syncer::PREFERENCES:
43 return pref.GetSpecifics().preference();
44 case syncer::PRIORITY_PREFERENCES:
45 return pref.GetSpecifics().priority_preference().preference();
46 #if BUILDFLAG(IS_CHROMEOS_ASH)
47 case syncer::OS_PREFERENCES:
48 return pref.GetSpecifics().os_preference().preference();
49 case syncer::OS_PRIORITY_PREFERENCES:
50 return pref.GetSpecifics().os_priority_preference().preference();
54 return pref.GetSpecifics().preference();
60 PrefModelAssociator::PrefModelAssociator(
61 const PrefModelAssociatorClient* client,
62 syncer::ModelType type,
63 PersistentPrefStore* user_pref_store)
64 : type_(type), client_(client), user_pref_store_(user_pref_store) {
65 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
66 #if BUILDFLAG(IS_CHROMEOS_ASH)
67 DCHECK(type_ == syncer::PREFERENCES ||
68 type_ == syncer::PRIORITY_PREFERENCES ||
69 type_ == syncer::OS_PREFERENCES ||
70 type_ == syncer::OS_PRIORITY_PREFERENCES);
72 DCHECK(type_ == syncer::PREFERENCES || type_ == syncer::PRIORITY_PREFERENCES);
74 DCHECK(user_pref_store_);
77 PrefModelAssociator::~PrefModelAssociator() {
78 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
79 pref_service_ = nullptr;
81 synced_pref_observers_.clear();
85 sync_pb::PreferenceSpecifics* PrefModelAssociator::GetMutableSpecifics(
86 syncer::ModelType type,
87 sync_pb::EntitySpecifics* specifics) {
89 case syncer::PREFERENCES:
90 return specifics->mutable_preference();
91 case syncer::PRIORITY_PREFERENCES:
92 return specifics->mutable_priority_preference()->mutable_preference();
93 #if BUILDFLAG(IS_CHROMEOS_ASH)
94 case syncer::OS_PREFERENCES:
95 return specifics->mutable_os_preference()->mutable_preference();
96 case syncer::OS_PRIORITY_PREFERENCES:
97 return specifics->mutable_os_priority_preference()->mutable_preference();
105 void PrefModelAssociator::InitPrefAndAssociate(
106 const syncer::SyncData& sync_pref,
107 const std::string& pref_name,
108 syncer::SyncChangeList* sync_changes) {
109 const base::Value* user_pref_value =
110 pref_service_->GetUserPrefValue(pref_name);
111 VLOG(1) << "Associating preference " << pref_name;
113 if (sync_pref.IsValid()) {
114 const sync_pb::PreferenceSpecifics& preference = GetSpecifics(sync_pref);
115 DCHECK(pref_name == preference.name());
116 base::JSONReader::Result parsed_json =
117 base::JSONReader::ReadAndReturnValueWithError(preference.value());
118 if (!parsed_json.has_value()) {
119 LOG(ERROR) << "Failed to deserialize value of preference '" << pref_name
120 << "': " << parsed_json.error().message;
123 std::unique_ptr<base::Value> sync_value =
124 base::Value::ToUniquePtrValue(std::move(*parsed_json));
126 if (user_pref_value) {
127 DVLOG(1) << "Found user pref value for " << pref_name;
128 // We have both server and local values. Merge them.
129 base::Value new_value(
130 MergePreference(pref_name, *user_pref_value, *sync_value));
132 // Update the local preference based on what we got from the
133 // sync server. Note: this only updates the user value store, which is
134 // ignored if the preference is policy controlled.
135 if (new_value.is_none()) {
136 LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
137 pref_service_->ClearPref(pref_name);
138 } else if (*user_pref_value != new_value) {
139 SetPrefWithTypeCheck(pref_name, new_value);
142 // If the merge resulted in an updated value, inform the syncer.
143 if (*sync_value != new_value) {
144 syncer::SyncData sync_data;
145 if (!CreatePrefSyncData(pref_name, new_value, &sync_data)) {
146 LOG(ERROR) << "Failed to update preference.";
150 sync_changes->push_back(syncer::SyncChange(
151 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
153 } else if (!sync_value->is_none()) {
154 // Only a server value exists. Just set the local user value.
155 SetPrefWithTypeCheck(pref_name, *sync_value);
157 LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
159 synced_preferences_.insert(preference.name());
160 } else if (user_pref_value) {
161 // The server does not know about this preference and should be added
162 // to the syncer's database.
163 syncer::SyncData sync_data;
164 if (!CreatePrefSyncData(pref_name, *user_pref_value, &sync_data)) {
165 LOG(ERROR) << "Failed to update preference.";
168 sync_changes->push_back(syncer::SyncChange(
169 FROM_HERE, syncer::SyncChange::ACTION_ADD, sync_data));
170 synced_preferences_.insert(pref_name);
173 // Else this pref does not have a sync value but also does not have a user
174 // controlled value (either it's a default value or it's policy controlled,
175 // either way it's not interesting). We can ignore it. Once it gets changed,
176 // we'll send the new user controlled value to the syncer.
179 void PrefModelAssociator::WaitUntilReadyToSync(base::OnceClosure done) {
180 // Prefs are loaded very early during profile initialization.
181 DCHECK_NE(pref_service_->GetInitializationStatus(),
182 PrefService::INITIALIZATION_STATUS_WAITING);
183 std::move(done).Run();
186 absl::optional<syncer::ModelError>
187 PrefModelAssociator::MergeDataAndStartSyncing(
188 syncer::ModelType type,
189 const syncer::SyncDataList& initial_sync_data,
190 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
191 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
192 DCHECK_EQ(type_, type);
193 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
194 DCHECK(pref_service_);
195 DCHECK(!sync_processor_.get());
196 DCHECK(sync_processor.get());
197 DCHECK(sync_error_factory.get());
198 sync_processor_ = std::move(sync_processor);
199 sync_error_factory_ = std::move(sync_error_factory);
201 syncer::SyncChangeList new_changes;
202 std::set<std::string> remaining_preferences = registered_preferences_;
204 // Go through and check for all preferences we care about that sync already
206 for (auto sync_iter = initial_sync_data.begin();
207 sync_iter != initial_sync_data.end(); ++sync_iter) {
208 DCHECK_EQ(type_, sync_iter->GetDataType());
210 const sync_pb::PreferenceSpecifics& preference = GetSpecifics(*sync_iter);
211 std::string sync_pref_name = preference.name();
213 if (remaining_preferences.count(sync_pref_name) == 0) {
214 // We're not syncing this preference locally, ignore the sync data.
215 // TODO(zea): Eventually we want to be able to have the syncable service
216 // reconstruct all sync data for its datatype (therefore having
217 // GetAllSyncDataForTesting be a complete representation). We should store
218 // this data somewhere, even if we don't use it.
222 remaining_preferences.erase(sync_pref_name);
223 InitPrefAndAssociate(*sync_iter, sync_pref_name, &new_changes);
224 NotifyStartedSyncing(sync_pref_name);
227 // Go through and build sync data for any remaining preferences.
228 for (const std::string& remaining_preference : remaining_preferences) {
229 InitPrefAndAssociate(syncer::SyncData(), remaining_preference,
233 for (const std::string& legacy_pref_name : legacy_model_type_preferences_) {
234 // Track preferences for which we have a local user-controlled value. That
235 // could be a value from last run, or a value just set by the initial sync.
236 // We don't call InitPrefAndAssociate because we don't want the initial sync
237 // to trigger outgoing changes -- these prefs are only tracked to send
238 // updates back to older clients.
239 if (pref_service_->GetUserPrefValue(legacy_pref_name)) {
240 synced_preferences_.insert(legacy_pref_name);
244 // Push updates to sync.
245 absl::optional<syncer::ModelError> error =
246 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
247 if (!error.has_value()) {
248 models_associated_ = true;
249 pref_service_->OnIsSyncingChanged();
254 void PrefModelAssociator::StopSyncing(syncer::ModelType type) {
255 DCHECK_EQ(type_, type);
256 models_associated_ = false;
257 sync_processor_.reset();
258 sync_error_factory_.reset();
259 pref_service_->OnIsSyncingChanged();
262 base::Value PrefModelAssociator::MergePreference(
263 const std::string& name,
264 const base::Value& local_value,
265 const base::Value& server_value) {
266 // This function special cases preferences individually, so don't attempt
267 // to merge for all migrated values.
269 std::string new_pref_name;
270 if (client_->IsMergeableListPreference(name)) {
271 return std::move(*MergeListValues(local_value, server_value));
273 if (client_->IsMergeableDictionaryPreference(name)) {
274 return MergeDictionaryValues(local_value, server_value);
276 base::Value merged_value =
277 client_->MaybeMergePreferenceValues(name, local_value, server_value);
278 if (!merged_value.is_none()) {
283 // If this is not a specially handled preference, server wins.
284 return server_value.Clone();
287 bool PrefModelAssociator::CreatePrefSyncData(
288 const std::string& name,
289 const base::Value& value,
290 syncer::SyncData* sync_data) const {
291 if (value.is_none()) {
292 LOG(ERROR) << "Attempting to sync a null pref value for " << name;
296 std::string serialized;
297 // TODO(zea): consider JSONWriter::Write since you don't have to check
298 // failures to deserialize.
299 JSONStringValueSerializer json(&serialized);
300 if (!json.Serialize(value)) {
301 LOG(ERROR) << "Failed to serialize preference value.";
305 sync_pb::EntitySpecifics specifics;
306 sync_pb::PreferenceSpecifics* pref_specifics =
307 GetMutableSpecifics(type_, &specifics);
309 pref_specifics->set_name(name);
310 pref_specifics->set_value(serialized);
311 *sync_data = syncer::SyncData::CreateLocalData(name, name, specifics);
315 std::unique_ptr<base::Value> PrefModelAssociator::MergeListValues(
316 const base::Value& from_value,
317 const base::Value& to_value) {
318 if (from_value.is_none()) {
319 return base::Value::ToUniquePtrValue(to_value.Clone());
321 if (to_value.is_none()) {
322 return base::Value::ToUniquePtrValue(from_value.Clone());
325 DCHECK(from_value.type() == base::Value::Type::LIST);
326 DCHECK(to_value.type() == base::Value::Type::LIST);
328 base::Value result = to_value.Clone();
329 for (const auto& value : from_value.GetList()) {
330 if (!base::Contains(result.GetList(), value)) {
331 result.Append(value.Clone());
335 return base::Value::ToUniquePtrValue(std::move(result));
338 base::Value PrefModelAssociator::MergeDictionaryValues(
339 const base::Value& from_value,
340 const base::Value& to_value) {
341 if (from_value.is_none()) {
342 return to_value.Clone();
344 if (to_value.is_none()) {
345 return from_value.Clone();
348 DCHECK(from_value.is_dict());
349 DCHECK(to_value.is_dict());
350 base::Value result = to_value.Clone();
352 for (auto it : from_value.DictItems()) {
353 // It's not clear whether using a C++17 structured binding here would cause
354 // a copy of the value or not, so in doubt unpack the old way.
355 const base::Value* from_key_value = &it.second;
356 base::Value* to_key_value = result.FindKey(it.first);
358 if (from_key_value->is_dict() && to_key_value->is_dict()) {
359 *to_key_value = MergeDictionaryValues(*from_key_value, *to_key_value);
361 // Note that for all other types we want to preserve the "to"
362 // values so we do nothing here.
364 result.SetKey(it.first, from_key_value->Clone());
370 // Note: This will build a model of all preferences registered as syncable
371 // with user controlled data. We do not track any information for preferences
372 // not registered locally as syncable and do not inform the syncer of
373 // non-user controlled preferences.
374 syncer::SyncDataList PrefModelAssociator::GetAllSyncDataForTesting(
375 syncer::ModelType type) const {
376 DCHECK_EQ(type_, type);
377 syncer::SyncDataList current_data;
378 for (const std::string& name : synced_preferences_) {
379 const PrefService::Preference* pref = pref_service_->FindPreference(name);
381 if (!pref->IsUserControlled() || pref->IsDefaultValue()) {
382 continue; // This is not data we care about.
384 // TODO(zea): plumb a way to read the user controlled value.
385 syncer::SyncData sync_data;
386 if (!CreatePrefSyncData(name, *pref->GetValue(), &sync_data)) {
389 current_data.push_back(sync_data);
394 absl::optional<syncer::ModelError> PrefModelAssociator::ProcessSyncChanges(
395 const base::Location& from_here,
396 const syncer::SyncChangeList& change_list) {
397 if (!models_associated_) {
398 return syncer::ModelError(FROM_HERE, "Models not yet associated.");
400 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
401 syncer::SyncChangeList::const_iterator iter;
402 for (iter = change_list.begin(); iter != change_list.end(); ++iter) {
403 DCHECK_EQ(type_, iter->sync_data().GetDataType());
405 const sync_pb::PreferenceSpecifics& pref_specifics =
406 GetSpecifics(iter->sync_data());
408 // It is possible that we may receive a change to a preference we do not
409 // want to sync. For example if the user is syncing a Mac client and a
410 // Windows client, the Windows client does not support
411 // kConfirmToQuitEnabled. Ignore updates from these preferences.
412 std::string pref_name = pref_specifics.name();
413 if (!IsPrefRegistered(pref_name)) {
417 if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
418 pref_service_->ClearPref(pref_name);
422 absl::optional<base::Value> new_value(
423 ReadPreferenceSpecifics(pref_specifics));
425 // Skip values we can't deserialize.
426 // TODO(zea): consider taking some further action such as erasing the
431 if (!TypeMatchesUserPrefStore(pref_name, *new_value)) {
432 // Ignore updates where the server type doesn't match the local type.
433 // Don't use SetPrefWithTypeCheck() because we want to skip notifying
434 // observers and inserting into |synced_preferences_|.
438 // This will only modify the user controlled value store, which takes
439 // priority over the default value but is ignored if the preference is
440 // policy controlled.
441 pref_service_->Set(pref_name, *new_value);
443 NotifySyncedPrefObservers(pref_specifics.name(), true /*from_sync*/);
445 // Keep track of any newly synced preferences.
446 if (iter->change_type() == syncer::SyncChange::ACTION_ADD) {
447 synced_preferences_.insert(pref_specifics.name());
450 return absl::nullopt;
454 absl::optional<base::Value> PrefModelAssociator::ReadPreferenceSpecifics(
455 const sync_pb::PreferenceSpecifics& preference) {
456 base::JSONReader::Result parsed_json =
457 base::JSONReader::ReadAndReturnValueWithError(preference.value());
458 if (!parsed_json.has_value()) {
459 LOG(ERROR) << "Failed to deserialize preference value: "
460 << parsed_json.error().message;
461 return absl::nullopt;
463 return std::move(*parsed_json);
466 void PrefModelAssociator::AddSyncedPrefObserver(const std::string& name,
467 SyncedPrefObserver* observer) {
468 auto& observers = synced_pref_observers_[name];
470 observers = std::make_unique<SyncedPrefObserverList>();
473 observers->AddObserver(observer);
476 void PrefModelAssociator::RemoveSyncedPrefObserver(
477 const std::string& name,
478 SyncedPrefObserver* observer) {
479 auto observer_iter = synced_pref_observers_.find(name);
480 if (observer_iter == synced_pref_observers_.end()) {
483 observer_iter->second->RemoveObserver(observer);
486 bool PrefModelAssociator::IsPrefSyncedForTesting(
487 const std::string& name) const {
488 return synced_preferences_.find(name) != synced_preferences_.end();
491 void PrefModelAssociator::RegisterPref(const std::string& name) {
492 DCHECK(!base::Contains(registered_preferences_, name));
493 registered_preferences_.insert(name);
495 // Make sure data in the local store matches the registered type (where "type"
496 // means base::Value data type like string, not ModelType like PREFERENCES).
497 // If this results in a modification of the local pref store, we don't want
498 // to tell ChromeSync about these -- it's a local anomaly,
499 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
500 EnforceRegisteredTypeInStore(name);
503 void PrefModelAssociator::RegisterPrefWithLegacyModelType(
504 const std::string& name) {
505 DCHECK(!base::Contains(legacy_model_type_preferences_, name));
506 DCHECK(!base::Contains(registered_preferences_, name));
507 legacy_model_type_preferences_.insert(name);
510 bool PrefModelAssociator::IsPrefRegistered(const std::string& name) const {
511 return registered_preferences_.count(name) > 0;
514 bool PrefModelAssociator::IsLegacyModelTypePref(const std::string& name) const {
515 return legacy_model_type_preferences_.count(name) > 0;
518 void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
519 if (processing_syncer_changes_) {
520 return; // These are changes originating from us, ignore.
523 // We only process changes if we've already associated models.
524 // This also filters out local changes during the initial merge.
525 if (!models_associated_) {
529 const PrefService::Preference* preference =
530 pref_service_->FindPreference(name);
531 // TODO(tschumann): When can this ever happen? Should this be a DCHECK?
536 if (!IsPrefRegistered(name) && !IsLegacyModelTypePref(name)) {
537 // We are not syncing this preference -- this also filters out synced
538 // preferences of the wrong type (e.g. priority preference are handled by a
539 // separate associator). Legacy model type preferences are allowed to
540 // continue because we want to push updates to old clients using the
545 syncer::SyncChangeList changes;
547 if (!preference->IsUserModifiable()) {
548 // If the preference is no longer user modifiable, it must now be
549 // controlled by policy, whose values we do not sync. Just return. If the
550 // preference stops being controlled by policy, it will revert back to the
551 // user value (which we continue to update with sync changes).
555 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
557 NotifySyncedPrefObservers(name, false /*from_sync*/);
559 if (synced_preferences_.count(name) == 0) {
560 // Not in synced_preferences_ means no synced data. InitPrefAndAssociate(..)
561 // will determine if we care about its data (e.g. if it has a default value
562 // and hasn't been changed yet we don't) and take care syncing any new data.
563 InitPrefAndAssociate(syncer::SyncData(), name, &changes);
565 // We are already syncing this preference, just update or delete its sync
567 syncer::SyncData sync_data;
568 if (!CreatePrefSyncData(name, *preference->GetValue(), &sync_data)) {
569 LOG(ERROR) << "Failed to update preference.";
572 if (pref_service_->GetUserPrefValue(name)) {
573 // If the pref was updated, update it.
574 changes.push_back(syncer::SyncChange(
575 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
577 // Otherwise, the pref must have been cleared and hence delete it.
578 changes.push_back(syncer::SyncChange(
579 FROM_HERE, syncer::SyncChange::ACTION_DELETE, sync_data));
583 sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
586 void PrefModelAssociator::SetPrefService(PrefServiceSyncable* pref_service) {
587 DCHECK(pref_service_ == nullptr);
588 pref_service_ = pref_service;
591 void PrefModelAssociator::NotifySyncedPrefObservers(const std::string& path,
592 bool from_sync) const {
593 auto observer_iter = synced_pref_observers_.find(path);
594 if (observer_iter == synced_pref_observers_.end()) {
597 // Don't notify for prefs we are only observing to support old clients.
598 // The PrefModelAssociator for the new ModelType will notify.
599 if (IsLegacyModelTypePref(path)) {
603 for (auto& observer : *observer_iter->second) {
604 observer.OnSyncedPrefChanged(path, from_sync);
608 void PrefModelAssociator::SetPrefWithTypeCheck(const std::string& pref_name,
609 const base::Value& new_value) {
610 if (TypeMatchesUserPrefStore(pref_name, new_value)) {
611 pref_service_->Set(pref_name, new_value);
615 bool PrefModelAssociator::TypeMatchesUserPrefStore(
616 const std::string& pref_name,
617 const base::Value& new_value) const {
618 const base::Value* local_value = nullptr;
619 user_pref_store_->GetValue(pref_name, &local_value);
620 if (!local_value || local_value->type() == new_value.type()) {
624 DLOG(WARNING) << "Unexpected type mis-match for pref. "
625 << "Synced value for " << pref_name << " is of type "
626 << new_value.type() << " which doesn't match the locally "
627 << "present pref type: " << local_value->type();
631 void PrefModelAssociator::EnforceRegisteredTypeInStore(
632 const std::string& pref_name) {
633 const base::Value* persisted_value = nullptr;
634 if (user_pref_store_->GetValue(pref_name, &persisted_value)) {
635 // Get the registered type (typically from the default value).
636 const PrefService::Preference* pref =
637 pref_service_->FindPreference(pref_name);
639 if (pref->GetType() != persisted_value->type()) {
640 // We see conflicting type information and there's a chance the local
641 // type-conflicting data came in via sync. Remove it.
642 // TODO(tschumann): The value should get removed silently. Add a method
643 // RemoveValueSilently() to WriteablePrefStore. Note, that as of today
644 // that removal will only notify other pref stores but not sync -- that's
645 // done on a higher level.
646 user_pref_store_->RemoveValue(
647 pref_name, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
652 void PrefModelAssociator::NotifyStartedSyncing(const std::string& path) const {
653 auto observer_iter = synced_pref_observers_.find(path);
654 if (observer_iter == synced_pref_observers_.end()) {
658 for (auto& observer : *observer_iter->second) {
659 observer.OnStartedSyncing(path);
663 } // namespace sync_preferences