1 // Copyright 2018 The Chromium Authors. All rights reserved.
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/unknown_user_pref_accessor.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/values.h"
15 #include "components/prefs/persistent_pref_store.h"
16 #include "components/prefs/pref_service.h"
17 #include "components/sync_preferences/pref_service_syncable.h"
19 namespace sync_preferences {
21 UnknownUserPrefAccessor::UnknownUserPrefAccessor(
22 PrefService* pref_service,
23 user_prefs::PrefRegistrySyncable* pref_registry,
24 PersistentPrefStore* user_prefs)
25 : pref_service_(pref_service),
26 pref_registry_(pref_registry),
27 user_prefs_(user_prefs) {}
29 UnknownUserPrefAccessor::~UnknownUserPrefAccessor() {}
31 UnknownUserPrefAccessor::PreferenceState
32 UnknownUserPrefAccessor::GetPreferenceState(
33 syncer::ModelType type,
34 const std::string& pref_name) const {
35 PreferenceState result;
36 result.registration_state = GetRegistrationState(type, pref_name);
37 switch (result.registration_state) {
38 case RegistrationState::kUnknown:
39 case RegistrationState::kUnknownWhitelisted:
40 if (!user_prefs_->GetValue(pref_name, &result.persisted_value)) {
41 result.persisted_value = nullptr;
44 case RegistrationState::kSyncable:
45 case RegistrationState::kNotSyncable:
46 result.persisted_value = pref_service_->GetUserPrefValue(pref_name);
52 void UnknownUserPrefAccessor::ClearPref(
53 const std::string& pref_name,
54 const PreferenceState& local_pref_state) {
55 switch (local_pref_state.registration_state) {
56 case RegistrationState::kUnknown:
57 NOTREACHED() << "Sync attempted to update an unknown pref which is not "
61 case RegistrationState::kUnknownWhitelisted:
62 user_prefs_->RemoveValue(pref_name,
63 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
65 case RegistrationState::kSyncable:
66 pref_service_->ClearPref(pref_name);
68 case RegistrationState::kNotSyncable:
69 // As this can happen if different clients disagree about which
70 // preferences should be synced, we only log a warning.
72 << "Sync attempted to update a pref which is not registered as "
73 "syncable. Ignoring the remote change for pref: "
79 int UnknownUserPrefAccessor::GetNumberOfSyncingUnknownPrefs() const {
80 return synced_unknown_prefs_.size();
85 bool VerifyTypesBeforeSet(const std::string& pref_name,
86 const base::Value* local_value,
87 const base::Value& new_value) {
88 if (local_value == nullptr || local_value->type() == new_value.type()) {
91 UMA_HISTOGRAM_BOOLEAN("Sync.Preferences.RemotePrefTypeMismatch", true);
92 DLOG(WARNING) << "Unexpected type mis-match for pref. "
93 << "Synced value for " << pref_name << " is of type "
94 << new_value.type() << " which doesn't match the locally "
95 << "present pref type: " << local_value->type();
101 void UnknownUserPrefAccessor::SetPref(const std::string& pref_name,
102 const PreferenceState& local_pref_state,
103 const base::Value& value) {
104 // On type mis-match, we trust the local preference DB and ignore the remote
106 switch (local_pref_state.registration_state) {
107 case RegistrationState::kUnknown:
108 NOTREACHED() << "Sync attempted to update a unknown pref which is not "
112 case RegistrationState::kUnknownWhitelisted:
113 if (VerifyTypesBeforeSet(pref_name, local_pref_state.persisted_value,
115 user_prefs_->SetValue(pref_name, value.CreateDeepCopy(),
116 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
118 synced_unknown_prefs_.insert(pref_name);
120 case RegistrationState::kSyncable:
121 if (VerifyTypesBeforeSet(pref_name, local_pref_state.persisted_value,
123 pref_service_->Set(pref_name, value);
126 case RegistrationState::kNotSyncable:
127 // As this can happen if different clients disagree about which
128 // preferences should be synced, we only log a warning.
130 << "Sync attempted to update a pref which is not registered as "
131 "syncable. Ignoring the remote change for pref: "
137 void UnknownUserPrefAccessor::EnforceRegisteredTypeInStore(
138 const std::string& pref_name) {
139 const base::Value* persisted_value = nullptr;
140 if (user_prefs_->GetValue(pref_name, &persisted_value)) {
141 // Get the registered type (typically from the default value).
142 const PrefService::Preference* pref =
143 pref_service_->FindPreference(pref_name);
145 if (pref->GetType() != persisted_value->type()) {
146 // We see conflicting type information and there's a chance the local
147 // type-conflicting data came in via sync. Remove it.
148 // TODO(tschumann): The value should get removed silently. Add a method
149 // RemoveValueSilently() to WriteablePrefStore. Note, that as of today
150 // that removal will only notify other pref stores but not sync -- that's
151 // done on a higher level.
152 user_prefs_->RemoveValue(pref_name,
153 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
154 UMA_HISTOGRAM_BOOLEAN("Sync.Preferences.ClearedLocalPrefOnTypeMismatch",
158 synced_unknown_prefs_.erase(pref_name);
161 UnknownUserPrefAccessor::RegistrationState
162 UnknownUserPrefAccessor::GetRegistrationState(
163 syncer::ModelType type,
164 const std::string& pref_name) const {
165 uint32_t type_flag = 0;
167 case syncer::PRIORITY_PREFERENCES:
168 type_flag = user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF;
170 case syncer::PREFERENCES:
171 type_flag = user_prefs::PrefRegistrySyncable::SYNCABLE_PREF;
174 NOTREACHED() << "unexpected model type for preferences: " << type;
176 if (pref_registry_->defaults()->GetValue(pref_name, nullptr)) {
177 uint32_t flags = pref_registry_->GetRegistrationFlags(pref_name);
178 if (flags & type_flag) {
179 return RegistrationState::kSyncable;
181 // Imagine the case where a preference has been synced as SYNCABLE_PREF
182 // first and then got changed to SYNCABLE_PRIORITY_PREF:
183 // In that situation, it could be argued for both, the preferences to be
184 // considered unknown or not synced. However, as we plan to eventually also
185 // sync unknown preferences, we cannot label them as unknown and treat them
186 // as not synced instead. (The underlying problem is that priority
187 // preferences are a concept only known to sync. The persistent stores don't
188 // distinguish between those two).
189 return RegistrationState::kNotSyncable;
191 if (pref_registry_->IsWhitelistedLateRegistrationPref(pref_name)) {
192 return RegistrationState::kUnknownWhitelisted;
194 return RegistrationState::kUnknown;
197 } // namespace sync_preferences