Upload upstream chromium 108.0.5359.1
[platform/framework/web/chromium-efl.git] / components / sync_preferences / pref_model_associator.cc
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.
4
5 #include "components/sync_preferences/pref_model_associator.h"
6
7 #include <algorithm>
8 #include <iterator>
9 #include <memory>
10 #include <utility>
11
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"
35
36 namespace sync_preferences {
37
38 namespace {
39
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();
51 #endif
52     default:
53       NOTREACHED();
54       return pref.GetSpecifics().preference();
55   }
56 }
57
58 }  // namespace
59
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);
71 #else
72   DCHECK(type_ == syncer::PREFERENCES || type_ == syncer::PRIORITY_PREFERENCES);
73 #endif
74   DCHECK(user_pref_store_);
75 }
76
77 PrefModelAssociator::~PrefModelAssociator() {
78   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
79   pref_service_ = nullptr;
80
81   synced_pref_observers_.clear();
82 }
83
84 // static
85 sync_pb::PreferenceSpecifics* PrefModelAssociator::GetMutableSpecifics(
86     syncer::ModelType type,
87     sync_pb::EntitySpecifics* specifics) {
88   switch (type) {
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();
98 #endif
99     default:
100       NOTREACHED();
101       return nullptr;
102   }
103 }
104
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;
112
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;
121       return;
122     }
123     std::unique_ptr<base::Value> sync_value =
124         base::Value::ToUniquePtrValue(std::move(*parsed_json));
125
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));
131
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);
140       }
141
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.";
147           return;
148         }
149
150         sync_changes->push_back(syncer::SyncChange(
151             FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
152       }
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);
156     } else {
157       LOG(WARNING) << "Sync has null value for pref " << pref_name.c_str();
158     }
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.";
166       return;
167     }
168     sync_changes->push_back(syncer::SyncChange(
169         FROM_HERE, syncer::SyncChange::ACTION_ADD, sync_data));
170     synced_preferences_.insert(pref_name);
171   }
172
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.
177 }
178
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();
184 }
185
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);
200
201   syncer::SyncChangeList new_changes;
202   std::set<std::string> remaining_preferences = registered_preferences_;
203
204   // Go through and check for all preferences we care about that sync already
205   // knows about.
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());
209
210     const sync_pb::PreferenceSpecifics& preference = GetSpecifics(*sync_iter);
211     std::string sync_pref_name = preference.name();
212
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.
219       continue;
220     }
221
222     remaining_preferences.erase(sync_pref_name);
223     InitPrefAndAssociate(*sync_iter, sync_pref_name, &new_changes);
224     NotifyStartedSyncing(sync_pref_name);
225   }
226
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,
230                          &new_changes);
231   }
232
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);
241     }
242   }
243
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();
250   }
251   return error;
252 }
253
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();
260 }
261
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.
268   if (client_) {
269     std::string new_pref_name;
270     if (client_->IsMergeableListPreference(name)) {
271       return std::move(*MergeListValues(local_value, server_value));
272     }
273     if (client_->IsMergeableDictionaryPreference(name)) {
274       return MergeDictionaryValues(local_value, server_value);
275     }
276     base::Value merged_value =
277         client_->MaybeMergePreferenceValues(name, local_value, server_value);
278     if (!merged_value.is_none()) {
279       return merged_value;
280     }
281   }
282
283   // If this is not a specially handled preference, server wins.
284   return server_value.Clone();
285 }
286
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;
293     return false;
294   }
295
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.";
302     return false;
303   }
304
305   sync_pb::EntitySpecifics specifics;
306   sync_pb::PreferenceSpecifics* pref_specifics =
307       GetMutableSpecifics(type_, &specifics);
308
309   pref_specifics->set_name(name);
310   pref_specifics->set_value(serialized);
311   *sync_data = syncer::SyncData::CreateLocalData(name, name, specifics);
312   return true;
313 }
314
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());
320   }
321   if (to_value.is_none()) {
322     return base::Value::ToUniquePtrValue(from_value.Clone());
323   }
324
325   DCHECK(from_value.type() == base::Value::Type::LIST);
326   DCHECK(to_value.type() == base::Value::Type::LIST);
327
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());
332     }
333   }
334
335   return base::Value::ToUniquePtrValue(std::move(result));
336 }
337
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();
343   }
344   if (to_value.is_none()) {
345     return from_value.Clone();
346   }
347
348   DCHECK(from_value.is_dict());
349   DCHECK(to_value.is_dict());
350   base::Value result = to_value.Clone();
351
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);
357     if (to_key_value) {
358       if (from_key_value->is_dict() && to_key_value->is_dict()) {
359         *to_key_value = MergeDictionaryValues(*from_key_value, *to_key_value);
360       }
361       // Note that for all other types we want to preserve the "to"
362       // values so we do nothing here.
363     } else {
364       result.SetKey(it.first, from_key_value->Clone());
365     }
366   }
367   return result;
368 }
369
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);
380     DCHECK(pref);
381     if (!pref->IsUserControlled() || pref->IsDefaultValue()) {
382       continue;  // This is not data we care about.
383     }
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)) {
387       continue;
388     }
389     current_data.push_back(sync_data);
390   }
391   return current_data;
392 }
393
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.");
399   }
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());
404
405     const sync_pb::PreferenceSpecifics& pref_specifics =
406         GetSpecifics(iter->sync_data());
407
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)) {
414       continue;
415     }
416
417     if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
418       pref_service_->ClearPref(pref_name);
419       continue;
420     }
421
422     absl::optional<base::Value> new_value(
423         ReadPreferenceSpecifics(pref_specifics));
424     if (!new_value) {
425       // Skip values we can't deserialize.
426       // TODO(zea): consider taking some further action such as erasing the
427       // bad data.
428       continue;
429     }
430
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_|.
435       continue;
436     }
437
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);
442
443     NotifySyncedPrefObservers(pref_specifics.name(), true /*from_sync*/);
444
445     // Keep track of any newly synced preferences.
446     if (iter->change_type() == syncer::SyncChange::ACTION_ADD) {
447       synced_preferences_.insert(pref_specifics.name());
448     }
449   }
450   return absl::nullopt;
451 }
452
453 // static
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;
462   }
463   return std::move(*parsed_json);
464 }
465
466 void PrefModelAssociator::AddSyncedPrefObserver(const std::string& name,
467                                                 SyncedPrefObserver* observer) {
468   auto& observers = synced_pref_observers_[name];
469   if (!observers) {
470     observers = std::make_unique<SyncedPrefObserverList>();
471   }
472
473   observers->AddObserver(observer);
474 }
475
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()) {
481     return;
482   }
483   observer_iter->second->RemoveObserver(observer);
484 }
485
486 bool PrefModelAssociator::IsPrefSyncedForTesting(
487     const std::string& name) const {
488   return synced_preferences_.find(name) != synced_preferences_.end();
489 }
490
491 void PrefModelAssociator::RegisterPref(const std::string& name) {
492   DCHECK(!base::Contains(registered_preferences_, name));
493   registered_preferences_.insert(name);
494
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);
501 }
502
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);
508 }
509
510 bool PrefModelAssociator::IsPrefRegistered(const std::string& name) const {
511   return registered_preferences_.count(name) > 0;
512 }
513
514 bool PrefModelAssociator::IsLegacyModelTypePref(const std::string& name) const {
515   return legacy_model_type_preferences_.count(name) > 0;
516 }
517
518 void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
519   if (processing_syncer_changes_) {
520     return;  // These are changes originating from us, ignore.
521   }
522
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_) {
526     return;
527   }
528
529   const PrefService::Preference* preference =
530       pref_service_->FindPreference(name);
531   // TODO(tschumann): When can this ever happen? Should this be a DCHECK?
532   if (!preference) {
533     return;
534   }
535
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
541     // old ModelType.
542     return;
543   }
544
545   syncer::SyncChangeList changes;
546
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).
552     return;
553   }
554
555   base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
556
557   NotifySyncedPrefObservers(name, false /*from_sync*/);
558
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);
564   } else {
565     // We are already syncing this preference, just update or delete its sync
566     // node.
567     syncer::SyncData sync_data;
568     if (!CreatePrefSyncData(name, *preference->GetValue(), &sync_data)) {
569       LOG(ERROR) << "Failed to update preference.";
570       return;
571     }
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));
576     } else {
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));
580     }
581   }
582
583   sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
584 }
585
586 void PrefModelAssociator::SetPrefService(PrefServiceSyncable* pref_service) {
587   DCHECK(pref_service_ == nullptr);
588   pref_service_ = pref_service;
589 }
590
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()) {
595     return;
596   }
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)) {
600     DCHECK(!from_sync);
601     return;
602   }
603   for (auto& observer : *observer_iter->second) {
604     observer.OnSyncedPrefChanged(path, from_sync);
605   }
606 }
607
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);
612   }
613 }
614
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()) {
621     return true;
622   }
623
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();
628   return false;
629 }
630
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);
638     DCHECK(pref);
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);
648     }
649   }
650 }
651
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()) {
655     return;
656   }
657
658   for (auto& observer : *observer_iter->second) {
659     observer.OnStartedSyncing(path);
660   }
661 }
662
663 }  // namespace sync_preferences