OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/prefs/pref_model_associator.h" | 5 #include "chrome/browser/prefs/pref_model_associator.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
11 #include "base/values.h" | 11 #include "base/values.h" |
12 #include "chrome/browser/profiles/profile.h" | 12 #include "chrome/browser/sync/api/sync_change.h" |
13 #include "chrome/browser/sync/engine/syncapi.h" | |
14 #include "chrome/browser/sync/glue/generic_change_processor.h" | |
15 #include "chrome/browser/sync/profile_sync_service.h" | |
16 #include "chrome/browser/sync/protocol/preference_specifics.pb.h" | 13 #include "chrome/browser/sync/protocol/preference_specifics.pb.h" |
17 #include "chrome/common/pref_names.h" | 14 #include "chrome/common/pref_names.h" |
18 #include "content/browser/browser_thread.h" | |
19 #include "content/common/json_value_serializer.h" | 15 #include "content/common/json_value_serializer.h" |
20 #include "content/common/notification_service.h" | 16 #include "content/common/notification_service.h" |
21 | 17 |
22 using syncable::PREFERENCES; | 18 using syncable::PREFERENCES; |
23 | 19 |
24 PrefModelAssociator::PrefModelAssociator() | 20 PrefModelAssociator::PrefModelAssociator() |
25 : pref_service_(NULL), | 21 : models_associated_(false), |
26 sync_service_(NULL), | |
27 models_associated_(false), | |
28 processing_syncer_changes_(false), | 22 processing_syncer_changes_(false), |
29 change_processor_(NULL) { | 23 pref_service_(NULL), |
| 24 sync_processor_(NULL) { |
30 } | 25 } |
31 | 26 |
32 PrefModelAssociator::PrefModelAssociator( | 27 PrefModelAssociator::PrefModelAssociator( |
33 PrefService* pref_service) | 28 PrefService* pref_service) |
34 : pref_service_(pref_service), | 29 : models_associated_(false), |
35 sync_service_(NULL), | |
36 models_associated_(false), | |
37 processing_syncer_changes_(false), | 30 processing_syncer_changes_(false), |
38 change_processor_(NULL) { | 31 pref_service_(pref_service), |
| 32 sync_processor_(NULL) { |
39 DCHECK(CalledOnValidThread()); | 33 DCHECK(CalledOnValidThread()); |
40 } | 34 } |
41 | 35 |
42 PrefModelAssociator::~PrefModelAssociator() { | 36 PrefModelAssociator::~PrefModelAssociator() { |
43 DCHECK(CalledOnValidThread()); | 37 DCHECK(CalledOnValidThread()); |
44 change_processor_ = NULL; | 38 sync_processor_ = NULL; |
45 sync_service_ = NULL; | |
46 pref_service_ = NULL; | 39 pref_service_ = NULL; |
47 } | 40 } |
48 | 41 |
49 bool PrefModelAssociator::InitPrefNodeAndAssociate( | 42 void PrefModelAssociator::InitPrefAndAssociate( |
50 sync_api::WriteTransaction* trans, | 43 const SyncData& sync_pref, |
51 const sync_api::BaseNode& root, | 44 const std::string& pref_name, |
52 const PrefService::Preference* pref) { | 45 SyncChangeList* sync_changes) { |
53 DCHECK(pref); | 46 const PrefService::Preference* pref = |
| 47 pref_service_->FindPreference(pref_name.c_str()); |
| 48 VLOG(1) << "Associating preference " << pref_name; |
| 49 if (!pref->IsUserModifiable()) { |
| 50 // This preference is controlled by policy. We don't need sync it, but if |
| 51 // there is sync data we want to track it for possible future use. |
| 52 if (sync_pref.IsValid()) |
| 53 untracked_pref_sync_data_[pref_name] = sync_pref; |
| 54 return; |
| 55 } |
54 | 56 |
55 base::JSONReader reader; | 57 base::JSONReader reader; |
56 std::string tag = pref->name(); | 58 if (sync_pref.IsValid()) { |
57 sync_api::WriteNode node(trans); | 59 // The server has a value for the preference, we have to reconcile it with |
58 if (node.InitByClientTagLookup(PREFERENCES, tag)) { | 60 // ours. |
59 // The server has a value for the preference. | 61 const sync_pb::PreferenceSpecifics& preference = |
60 const sync_pb::PreferenceSpecifics& preference( | 62 sync_pref.GetSpecifics().GetExtension(sync_pb::preference); |
61 node.GetPreferenceSpecifics()); | 63 DCHECK_EQ(pref->name(), preference.name()); |
62 DCHECK_EQ(tag, preference.name()); | |
63 | |
64 if (!pref->IsUserModifiable()) { | |
65 Associate(pref, node.GetId()); | |
66 return true; | |
67 } | |
68 | 64 |
69 scoped_ptr<Value> value( | 65 scoped_ptr<Value> value( |
70 reader.JsonToValue(preference.value(), false, false)); | 66 reader.JsonToValue(preference.value(), false, false)); |
71 std::string pref_name = preference.name(); | |
72 if (!value.get()) { | 67 if (!value.get()) { |
73 LOG(ERROR) << "Failed to deserialize preference value: " | 68 LOG(ERROR) << "Failed to deserialize preference value: " |
74 << reader.GetErrorMessage(); | 69 << reader.GetErrorMessage(); |
75 return false; | 70 return; |
76 } | 71 } |
77 | 72 |
78 // Merge the server value of this preference with the local value. | 73 // Merge the server value of this preference with the local value. |
79 scoped_ptr<Value> new_value(MergePreference(*pref, *value)); | 74 scoped_ptr<Value> new_value(MergePreference(*pref, *value)); |
80 | 75 |
81 // Update the local preference based on what we got from the | 76 // Update the local preference based on what we got from the |
82 // sync server. | 77 // sync server. |
83 if (new_value->IsType(Value::TYPE_NULL)) { | 78 if (new_value->IsType(Value::TYPE_NULL)) { |
84 pref_service_->ClearPref(pref_name.c_str()); | 79 pref_service_->ClearPref(pref_name.c_str()); |
85 } else if (!new_value->IsType(pref->GetType())) { | 80 } else if (!new_value->IsType(pref->GetType())) { |
86 LOG(WARNING) << "Synced value for " << preference.name() | 81 LOG(WARNING) << "Synced value for " << preference.name() |
87 << " is of type " << new_value->GetType() | 82 << " is of type " << new_value->GetType() |
88 << " which doesn't match pref type " << pref->GetType(); | 83 << " which doesn't match pref type " << pref->GetType(); |
89 } else if (!pref->GetValue()->Equals(new_value.get())) { | 84 } else if (!pref->GetValue()->Equals(new_value.get())) { |
90 pref_service_->Set(pref_name.c_str(), *new_value); | 85 pref_service_->Set(pref_name.c_str(), *new_value); |
91 } | 86 } |
92 | 87 |
93 SendUpdateNotificationsIfNecessary(pref_name); | 88 SendUpdateNotificationsIfNecessary(pref_name); |
94 | 89 |
95 // If the merge resulted in an updated value, write it back to | 90 // If the merge resulted in an updated value, inform the syncer. |
96 // the sync node. | 91 if (!value->Equals(new_value.get())) { |
97 if (!value->Equals(new_value.get()) && | 92 SyncData sync_data; |
98 !WritePreferenceToNode(pref->name(), *new_value, &node)) { | 93 if (!CreatePrefSyncData(pref->name(), *new_value, &sync_data)) { |
99 return false; | 94 LOG(ERROR) << "Failed to update preference."; |
| 95 return; |
| 96 } |
| 97 sync_changes->push_back(SyncChange(SyncChange::ACTION_UPDATE, sync_data)); |
100 } | 98 } |
101 Associate(pref, node.GetId()); | |
102 } else if (pref->IsUserControlled()) { | 99 } else if (pref->IsUserControlled()) { |
103 // The server doesn't have a value, but we have a user-controlled value, | 100 // The server does not know about this preference and should be added |
104 // so we push it to the server. | 101 // to the syncer's database. |
105 sync_api::WriteNode write_node(trans); | 102 SyncData sync_data; |
106 if (!write_node.InitUniqueByCreation(PREFERENCES, root, tag)) { | 103 if (!CreatePrefSyncData(pref->name(), *pref->GetValue(), &sync_data)) { |
107 LOG(ERROR) << "Failed to create preference sync node."; | 104 LOG(ERROR) << "Failed to update preference."; |
108 return false; | 105 return; |
| 106 } |
| 107 sync_changes->push_back(SyncChange(SyncChange::ACTION_ADD, sync_data)); |
| 108 } else { |
| 109 // This pref has a default value, we can ignore it. Once it gets changed, |
| 110 // we'll send the new custom value to the syncer. |
| 111 return; |
| 112 } |
| 113 |
| 114 // Make sure we add it to our list of synced preferences so we know what |
| 115 // the server is aware of. |
| 116 synced_preferences_.insert(pref_name); |
| 117 return; |
| 118 } |
| 119 |
| 120 bool PrefModelAssociator::MergeDataAndStartSyncing( |
| 121 syncable::ModelType type, |
| 122 const SyncDataList& initial_sync_data, |
| 123 SyncChangeProcessor* sync_processor) { |
| 124 DCHECK_EQ(type, PREFERENCES); |
| 125 DCHECK(CalledOnValidThread()); |
| 126 DCHECK(!sync_processor_); |
| 127 sync_processor_ = sync_processor; |
| 128 |
| 129 SyncChangeList new_changes; |
| 130 std::set<std::string> remaining_preferences = registered_preferences_; |
| 131 |
| 132 // Go through and check for all preferences we care about that sync already |
| 133 // knows about. |
| 134 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); |
| 135 sync_iter != initial_sync_data.end(); |
| 136 ++sync_iter) { |
| 137 DCHECK_EQ(PREFERENCES, sync_iter->GetDataType()); |
| 138 std::string sync_pref_name = sync_iter->GetSpecifics(). |
| 139 GetExtension(sync_pb::preference).name(); |
| 140 if (remaining_preferences.count(sync_pref_name) == 0) { |
| 141 // We're not syncing this preference locally, ignore the sync data. |
| 142 // TODO(zea): Eventually we want to be able to have the syncable service |
| 143 // reconstruct all sync data for it's datatype (therefore having |
| 144 // GetAllSyncData be a complete representation). We should store this data |
| 145 // somewhere, even if we don't use it. |
| 146 continue; |
109 } | 147 } |
110 | 148 |
111 // Update the sync node with the local value for this preference. | 149 remaining_preferences.erase(sync_pref_name); |
112 if (!WritePreferenceToNode(pref->name(), *pref->GetValue(), &write_node)) | 150 InitPrefAndAssociate(*sync_iter, sync_pref_name, &new_changes); |
113 return false; | |
114 | |
115 Associate(pref, write_node.GetId()); | |
116 } else { | |
117 // This preference is handled by policy, not the user, and therefore | |
118 // we do not associate it. | |
119 } | 151 } |
120 | 152 |
121 return true; | 153 // Go through and build sync data for any remaining preferences. |
122 } | 154 for (std::set<std::string>::iterator pref_name_iter = |
123 | 155 remaining_preferences.begin(); |
124 bool PrefModelAssociator::AssociateModels() { | 156 pref_name_iter != remaining_preferences.end(); |
125 DCHECK(CalledOnValidThread()); | 157 ++pref_name_iter) { |
126 | 158 InitPrefAndAssociate(SyncData(), *pref_name_iter, &new_changes); |
127 int64 root_id; | |
128 if (!GetSyncIdForTaggedNode(syncable::ModelTypeToRootTag(PREFERENCES), | |
129 &root_id)) { | |
130 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
131 << "might be running against an out-of-date server."; | |
132 return false; | |
133 } | 159 } |
134 | 160 |
135 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); | 161 // Push updates to sync. |
136 sync_api::ReadNode root(&trans); | 162 sync_processor_->ProcessSyncChanges(new_changes); |
137 if (!root.InitByIdLookup(root_id)) { | |
138 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
139 << "might be running against an out-of-date server."; | |
140 return false; | |
141 } | |
142 | |
143 for (std::set<std::string>::iterator it = synced_preferences_.begin(); | |
144 it != synced_preferences_.end(); ++it) { | |
145 std::string name = *it; | |
146 const PrefService::Preference* pref = | |
147 pref_service_->FindPreference(name.c_str()); | |
148 VLOG(1) << "Associating preference " << name; | |
149 DCHECK(pref); | |
150 if (!pref->IsUserModifiable()) | |
151 continue; // We don't sync preferences the user cannot change. | |
152 InitPrefNodeAndAssociate(&trans, root, pref); | |
153 } | |
154 models_associated_ = true; | 163 models_associated_ = true; |
155 return true; | 164 return true; |
156 } | 165 } |
157 | 166 |
158 bool PrefModelAssociator::DisassociateModels() { | 167 void PrefModelAssociator::StopSyncing(syncable::ModelType type) { |
159 id_map_.clear(); | 168 DCHECK_EQ(type, PREFERENCES); |
160 id_map_inverse_.clear(); | |
161 models_associated_ = false; | 169 models_associated_ = false; |
162 return true; | 170 sync_processor_ = NULL; |
163 } | 171 untracked_pref_sync_data_.clear(); |
164 | |
165 bool PrefModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { | |
166 DCHECK(has_nodes); | |
167 *has_nodes = false; | |
168 int64 preferences_sync_id; | |
169 if (!GetSyncIdForTaggedNode(syncable::ModelTypeToRootTag(PREFERENCES), | |
170 &preferences_sync_id)) { | |
171 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
172 << "might be running against an out-of-date server."; | |
173 return false; | |
174 } | |
175 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
176 | |
177 sync_api::ReadNode preferences_node(&trans); | |
178 if (!preferences_node.InitByIdLookup(preferences_sync_id)) { | |
179 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
180 << "might be running against an out-of-date server."; | |
181 return false; | |
182 } | |
183 | |
184 // The sync model has user created nodes if the preferences folder has any | |
185 // children. | |
186 *has_nodes = sync_api::kInvalidId != preferences_node.GetFirstChildId(); | |
187 return true; | |
188 } | |
189 | |
190 int64 PrefModelAssociator::GetSyncIdFromChromeId( | |
191 const std::string& preference_name) { | |
192 PreferenceNameToSyncIdMap::const_iterator iter = | |
193 id_map_.find(preference_name); | |
194 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; | |
195 } | |
196 | |
197 void PrefModelAssociator::Associate( | |
198 const PrefService::Preference* preference, int64 sync_id) { | |
199 DCHECK(CalledOnValidThread()); | |
200 | |
201 std::string name = preference->name(); | |
202 DCHECK_NE(sync_api::kInvalidId, sync_id); | |
203 DCHECK_EQ(0U, id_map_.count(name)); | |
204 DCHECK_EQ(0U, id_map_inverse_.count(sync_id)); | |
205 id_map_[name] = sync_id; | |
206 id_map_inverse_[sync_id] = name; | |
207 } | |
208 | |
209 void PrefModelAssociator::Disassociate(int64 sync_id) { | |
210 DCHECK(CalledOnValidThread()); | |
211 SyncIdToPreferenceNameMap::iterator iter = id_map_inverse_.find(sync_id); | |
212 if (iter == id_map_inverse_.end()) | |
213 return; | |
214 id_map_.erase(iter->second); | |
215 id_map_inverse_.erase(iter); | |
216 } | |
217 | |
218 bool PrefModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, | |
219 int64* sync_id) { | |
220 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
221 sync_api::ReadNode sync_node(&trans); | |
222 if (!sync_node.InitByTagLookup(tag.c_str())) | |
223 return false; | |
224 *sync_id = sync_node.GetId(); | |
225 return true; | |
226 } | 172 } |
227 | 173 |
228 Value* PrefModelAssociator::MergePreference( | 174 Value* PrefModelAssociator::MergePreference( |
229 const PrefService::Preference& local_pref, | 175 const PrefService::Preference& local_pref, |
230 const Value& server_value) { | 176 const Value& server_value) { |
231 const std::string& name(local_pref.name()); | 177 const std::string& name(local_pref.name()); |
232 if (name == prefs::kURLsToRestoreOnStartup || | 178 if (name == prefs::kURLsToRestoreOnStartup || |
233 name == prefs::kDesktopNotificationAllowedOrigins || | 179 name == prefs::kDesktopNotificationAllowedOrigins || |
234 name == prefs::kDesktopNotificationDeniedOrigins) { | 180 name == prefs::kDesktopNotificationDeniedOrigins) { |
235 return MergeListValues(*local_pref.GetValue(), server_value); | 181 return MergeListValues(*local_pref.GetValue(), server_value); |
236 } | 182 } |
237 | 183 |
238 if (name == prefs::kContentSettingsPatterns || | 184 if (name == prefs::kContentSettingsPatterns || |
239 name == prefs::kGeolocationContentSettings) { | 185 name == prefs::kGeolocationContentSettings) { |
240 return MergeDictionaryValues(*local_pref.GetValue(), server_value); | 186 return MergeDictionaryValues(*local_pref.GetValue(), server_value); |
241 } | 187 } |
242 | 188 |
243 // If this is not a specially handled preference, server wins. | 189 // If this is not a specially handled preference, server wins. |
244 return server_value.DeepCopy(); | 190 return server_value.DeepCopy(); |
245 } | 191 } |
246 | 192 |
247 bool PrefModelAssociator::WritePreferenceToNode( | 193 bool PrefModelAssociator::CreatePrefSyncData( |
248 const std::string& name, | 194 const std::string& name, |
249 const Value& value, | 195 const Value& value, |
250 sync_api::WriteNode* node) { | 196 SyncData* sync_data) { |
251 std::string serialized; | 197 std::string serialized; |
| 198 // TODO(zea): consider JSONWriter::Write since you don't have to check |
| 199 // failures to deserialize. |
252 JSONStringValueSerializer json(&serialized); | 200 JSONStringValueSerializer json(&serialized); |
253 if (!json.Serialize(value)) { | 201 if (!json.Serialize(value)) { |
254 LOG(ERROR) << "Failed to serialize preference value."; | 202 LOG(ERROR) << "Failed to serialize preference value."; |
255 return false; | 203 return false; |
256 } | 204 } |
257 | 205 |
258 sync_pb::PreferenceSpecifics preference; | 206 sync_pb::EntitySpecifics specifics; |
259 preference.set_name(name); | 207 sync_pb::PreferenceSpecifics* pref_specifics = specifics.MutableExtension( |
260 preference.set_value(serialized); | 208 sync_pb::preference); |
261 node->SetPreferenceSpecifics(preference); | 209 pref_specifics->set_name(name); |
262 // TODO(viettrungluu): eliminate conversion (it's temporary) | 210 pref_specifics->set_value(serialized); |
263 node->SetTitle(UTF8ToWide(name)); | 211 *sync_data = SyncData::CreateLocalData(name, specifics); |
264 return true; | 212 return true; |
265 } | 213 } |
266 | 214 |
267 Value* PrefModelAssociator::MergeListValues(const Value& from_value, | 215 Value* PrefModelAssociator::MergeListValues(const Value& from_value, |
268 const Value& to_value) { | 216 const Value& to_value) { |
269 if (from_value.GetType() == Value::TYPE_NULL) | 217 if (from_value.GetType() == Value::TYPE_NULL) |
270 return to_value.DeepCopy(); | 218 return to_value.DeepCopy(); |
271 if (to_value.GetType() == Value::TYPE_NULL) | 219 if (to_value.GetType() == Value::TYPE_NULL) |
272 return from_value.DeepCopy(); | 220 return from_value.DeepCopy(); |
273 | 221 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 // The bookmark bar visibility preference requires a special | 275 // The bookmark bar visibility preference requires a special |
328 // notification to update the UI. | 276 // notification to update the UI. |
329 if (0 == pref_name.compare(prefs::kShowBookmarkBar)) { | 277 if (0 == pref_name.compare(prefs::kShowBookmarkBar)) { |
330 NotificationService::current()->Notify( | 278 NotificationService::current()->Notify( |
331 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, | 279 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, |
332 Source<PrefModelAssociator>(this), | 280 Source<PrefModelAssociator>(this), |
333 NotificationService::NoDetails()); | 281 NotificationService::NoDetails()); |
334 } | 282 } |
335 } | 283 } |
336 | 284 |
337 // Not implemented. | 285 // Note: This will build a model of all preferences registered as syncable |
338 void PrefModelAssociator::AbortAssociation() {} | 286 // with user controlled data. We do not track any information for preferences |
339 | 287 // not registered locally as syncable and do not inform the syncer of |
340 bool PrefModelAssociator::CryptoReadyIfNecessary() { | 288 // non-user controlled preferences. |
341 // We only access the cryptographer while holding a transaction. | 289 SyncDataList PrefModelAssociator::GetAllSyncData(syncable::ModelType type) |
342 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | 290 const { |
343 syncable::ModelTypeSet encrypted_types; | 291 DCHECK_EQ(PREFERENCES, type); |
344 sync_service_->GetEncryptedDataTypes(&encrypted_types); | 292 SyncDataList current_data; |
345 return encrypted_types.count(PREFERENCES) == 0 || | 293 for (PreferenceSet::const_iterator iter = synced_preferences_.begin(); |
346 sync_service_->IsCryptographerReady(&trans); | 294 iter != synced_preferences_.end(); |
| 295 ++iter) { |
| 296 std::string name = *iter; |
| 297 const PrefService::Preference* pref = |
| 298 pref_service_->FindPreference(name.c_str()); |
| 299 SyncData sync_data; |
| 300 if (!CreatePrefSyncData(name, *pref->GetValue(), &sync_data)) |
| 301 continue; |
| 302 current_data.push_back(sync_data); |
| 303 } |
| 304 for (SyncDataMap::const_iterator iter = untracked_pref_sync_data_.begin(); |
| 305 iter != untracked_pref_sync_data_.end(); |
| 306 ++iter) { |
| 307 current_data.push_back(iter->second); |
| 308 } |
| 309 return current_data; |
347 } | 310 } |
348 | 311 |
349 void PrefModelAssociator::SetupSync( | 312 void PrefModelAssociator::ProcessSyncChanges( |
350 ProfileSyncService* sync_service, | 313 const SyncChangeList& change_list) { |
351 browser_sync::GenericChangeProcessor* change_processor) { | |
352 sync_service_ = sync_service; | |
353 change_processor_ = change_processor; | |
354 } | |
355 | |
356 void PrefModelAssociator::ApplyChangesFromSync( | |
357 const sync_api::BaseTransaction* trans, | |
358 const sync_api::SyncManager::ChangeRecord* changes, | |
359 int change_count) { | |
360 if (!models_associated_) | 314 if (!models_associated_) |
361 return; | 315 return; |
362 AutoReset<bool> processing_changes(&processing_syncer_changes_, true); | 316 AutoReset<bool> processing_changes(&processing_syncer_changes_, true); |
363 for (int i = 0; i < change_count; ++i) { | 317 SyncChangeList::const_iterator iter; |
364 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == | 318 for (iter = change_list.begin(); iter != change_list.end(); ++iter) { |
365 changes[i].action) { | 319 DCHECK_EQ(PREFERENCES, iter->sync_data().GetDataType()); |
| 320 |
| 321 std::string name; |
| 322 sync_pb::PreferenceSpecifics pref_specifics = |
| 323 iter->sync_data().GetSpecifics().GetExtension(sync_pb::preference); |
| 324 scoped_ptr<Value> value(ReadPreferenceSpecifics(pref_specifics, |
| 325 &name)); |
| 326 |
| 327 if (iter->change_type() == SyncChange::ACTION_DELETE) { |
366 // We never delete preferences. | 328 // We never delete preferences. |
367 NOTREACHED(); | 329 NOTREACHED() << "Attempted to process sync delete change for " << name |
| 330 << ". Skipping."; |
| 331 continue; |
368 } | 332 } |
369 | 333 |
370 sync_api::ReadNode node(trans); | |
371 if (!node.InitByIdLookup(changes[i].id)) { | |
372 LOG(ERROR) << "Preference node lookup failed."; | |
373 return; | |
374 } | |
375 DCHECK(PREFERENCES == node.GetModelType()); | |
376 std::string name; | |
377 sync_pb::PreferenceSpecifics pref_specifics = | |
378 node.GetPreferenceSpecifics(); | |
379 scoped_ptr<Value> value(ReadPreferenceSpecifics(pref_specifics, | |
380 &name)); | |
381 // Skip values we can't deserialize. | 334 // Skip values we can't deserialize. |
| 335 // TODO(zea): consider taking some further action such as erasing the bad |
| 336 // data. |
382 if (!value.get()) | 337 if (!value.get()) |
383 continue; | 338 continue; |
384 | 339 |
385 // It is possible that we may receive a change to a preference we do not | 340 // It is possible that we may receive a change to a preference we do not |
386 // want to sync. For example if the user is syncing a Mac client and a | 341 // want to sync. For example if the user is syncing a Mac client and a |
387 // Windows client, the Windows client does not support | 342 // Windows client, the Windows client does not support |
388 // kConfirmToQuitEnabled. Ignore updates from these preferences. | 343 // kConfirmToQuitEnabled. Ignore updates from these preferences. |
389 const char* pref_name = name.c_str(); | 344 const char* pref_name = name.c_str(); |
390 if (!IsPrefRegistered(pref_name)) | 345 if (!IsPrefRegistered(pref_name)) |
391 continue; | 346 continue; |
392 | 347 |
393 const PrefService::Preference* pref = | 348 const PrefService::Preference* pref = |
394 pref_service_->FindPreference(pref_name); | 349 pref_service_->FindPreference(pref_name); |
395 DCHECK(pref); | 350 DCHECK(pref); |
396 if (!pref->IsUserModifiable()) { | 351 if (!pref->IsUserModifiable()) { |
| 352 // This preference is controlled by policy, ignore for now, but keep |
| 353 // the data around for possible later use. |
| 354 untracked_pref_sync_data_[name] = iter->sync_data(); |
397 continue; | 355 continue; |
398 } | 356 } |
399 | 357 |
400 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == | 358 pref_service_->Set(pref_name, *value); |
401 changes[i].action) { | |
402 pref_service_->ClearPref(pref_name); | |
403 } else { | |
404 pref_service_->Set(pref_name, *value); | |
405 | 359 |
406 // If this is a newly added node, associate. | 360 // If this is a newly added node, associate. |
407 if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == | 361 if (iter->change_type() == SyncChange::ACTION_ADD) { |
408 changes[i].action) { | 362 synced_preferences_.insert(name); |
409 Associate(pref, changes[i].id); | 363 } |
410 } | |
411 | 364 |
412 SendUpdateNotificationsIfNecessary(name); | 365 SendUpdateNotificationsIfNecessary(name); |
413 } | |
414 } | 366 } |
415 } | 367 } |
416 | 368 |
417 Value* PrefModelAssociator::ReadPreferenceSpecifics( | 369 Value* PrefModelAssociator::ReadPreferenceSpecifics( |
418 const sync_pb::PreferenceSpecifics& preference, | 370 const sync_pb::PreferenceSpecifics& preference, |
419 std::string* name) { | 371 std::string* name) { |
420 base::JSONReader reader; | 372 base::JSONReader reader; |
421 scoped_ptr<Value> value(reader.JsonToValue(preference.value(), false, false)); | 373 scoped_ptr<Value> value(reader.JsonToValue(preference.value(), false, false)); |
422 if (!value.get()) { | 374 if (!value.get()) { |
423 std::string err = "Failed to deserialize preference value: " + | 375 std::string err = "Failed to deserialize preference value: " + |
424 reader.GetErrorMessage(); | 376 reader.GetErrorMessage(); |
425 LOG(ERROR) << err; | 377 LOG(ERROR) << err; |
426 return NULL; | 378 return NULL; |
427 } | 379 } |
428 *name = preference.name(); | 380 *name = preference.name(); |
429 return value.release(); | 381 return value.release(); |
430 } | 382 } |
431 | 383 |
| 384 std::set<std::string> PrefModelAssociator::registered_preferences() const { |
| 385 return registered_preferences_; |
| 386 } |
432 | 387 |
433 std::set<std::string> PrefModelAssociator::synced_preferences() const { | 388 std::set<std::string> PrefModelAssociator::synced_preferences() const { |
434 return synced_preferences_; | 389 return synced_preferences_; |
435 } | 390 } |
436 | 391 |
437 void PrefModelAssociator::RegisterPref(const char* name) { | 392 void PrefModelAssociator::RegisterPref(const char* name) { |
438 DCHECK(!models_associated_ && synced_preferences_.count(name) == 0); | 393 DCHECK(!models_associated_ && registered_preferences_.count(name) == 0); |
439 synced_preferences_.insert(name); | 394 registered_preferences_.insert(name); |
440 } | 395 } |
441 | 396 |
442 bool PrefModelAssociator::IsPrefRegistered(const char* name) { | 397 bool PrefModelAssociator::IsPrefRegistered(const char* name) { |
443 return synced_preferences_.count(name) > 0; | 398 return registered_preferences_.count(name) > 0; |
444 } | 399 } |
445 | 400 |
446 void PrefModelAssociator::ProcessPrefChange(const std::string& name) { | 401 void PrefModelAssociator::ProcessPrefChange(const std::string& name) { |
447 if (processing_syncer_changes_) | 402 if (processing_syncer_changes_) |
448 return; // These are changes originating from us, ignore. | 403 return; // These are changes originating from us, ignore. |
449 | 404 |
450 // We only process changes if we've already associated models. | 405 // We only process changes if we've already associated models. |
451 if (!sync_service_ || !models_associated_) | 406 if (!models_associated_) |
452 return; | 407 return; |
453 | 408 |
454 const PrefService::Preference* preference = | 409 const PrefService::Preference* preference = |
455 pref_service_->FindPreference(name.c_str()); | 410 pref_service_->FindPreference(name.c_str()); |
456 if (!IsPrefRegistered(name.c_str())) | 411 if (!IsPrefRegistered(name.c_str())) |
457 return; // We are not syncing this preference. | 412 return; // We are not syncing this preference. |
458 | 413 |
459 // The preference does not have a node. This can happen if the preference | 414 SyncChangeList changes; |
460 // held the default value at association time. Create one and associate. | |
461 int64 root_id; | |
462 if (!GetSyncIdForTaggedNode(syncable::ModelTypeToRootTag(PREFERENCES), | |
463 &root_id)) { | |
464 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
465 << "might be running against an out-of-date server."; | |
466 return; | |
467 } | |
468 | 415 |
469 int64 sync_id = GetSyncIdFromChromeId(name); | |
470 if (!preference->IsUserModifiable()) { | 416 if (!preference->IsUserModifiable()) { |
471 // If the preference is not currently user modifiable, disassociate, so that | 417 // If the preference is not currently user modifiable, back up the |
472 // if it becomes user modifiable me pick up the server value. | 418 // previously synced value and remove it from our list of synced prefs. |
473 Disassociate(sync_id); | 419 if (synced_preferences_.count(name) > 0) { |
| 420 synced_preferences_.erase(name); |
| 421 SyncData sync_data; |
| 422 if (!CreatePrefSyncData(name, *preference->GetValue(), &sync_data)) { |
| 423 LOG(ERROR) << "Failed to update preference."; |
| 424 return; |
| 425 } |
| 426 untracked_pref_sync_data_[name] = sync_data; |
| 427 } |
474 return; | 428 return; |
475 } | 429 } |
476 | 430 |
477 AutoReset<bool> processing_changes(&processing_syncer_changes_, true); | 431 AutoReset<bool> processing_changes(&processing_syncer_changes_, true); |
478 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); | |
479 | 432 |
480 // Since we don't create sync nodes for preferences that are not under control | 433 if (synced_preferences_.count(name) == 0) { |
481 // of the user or still have their default value, this changed preference may | 434 // This is a preference that changed locally but we were not syncing. This |
482 // not have a sync node yet. If so, we create a node. Similarly, a preference | 435 // happens when a preference was previously not user modifiable but now is, |
483 // may become user-modifiable (e.g. due to laxer policy configuration), in | 436 // or if it had a default value but the user set a custom one. We now care |
484 // which case we also need to create a sync node and associate it. | 437 // about the preference and must inform the syncer, as well as update our |
485 if (sync_id == sync_api::kInvalidId) { | 438 // own internal tracking. |
486 sync_api::ReadNode root(&trans); | 439 if (untracked_pref_sync_data_.count(name) > 0) { |
487 if (!root.InitByIdLookup(root_id)) { | 440 // We have sync data for this preference, merge it (this only happens |
488 LOG(ERROR) << "Server did not create the top-level preferences node. We " | 441 // when we went from policy-controlled to user controlled. Default values |
489 << "might be running against an out-of-date server."; | 442 // are always overwritten by syncer values). |
| 443 InitPrefAndAssociate(untracked_pref_sync_data_[name], name, &changes); |
| 444 untracked_pref_sync_data_.erase(name); |
| 445 } else { |
| 446 InitPrefAndAssociate(SyncData(), name, &changes); |
| 447 } |
| 448 } else { |
| 449 // We're already syncing this preference, we just need to update the data. |
| 450 SyncData sync_data; |
| 451 if (!CreatePrefSyncData(name, *preference->GetValue(), &sync_data)) { |
| 452 LOG(ERROR) << "Failed to update preference."; |
490 return; | 453 return; |
491 } | 454 } |
492 InitPrefNodeAndAssociate(&trans, root, preference); | 455 changes.push_back(SyncChange(SyncChange::ACTION_UPDATE, sync_data)); |
493 } else { | |
494 sync_api::WriteNode node(&trans); | |
495 if (!node.InitByIdLookup(sync_id)) { | |
496 LOG(ERROR) << "Preference node lookup failed."; | |
497 return; | |
498 } | |
499 | |
500 if (!WritePreferenceToNode(name, *preference->GetValue(), &node)) { | |
501 LOG(ERROR) << "Failed to update preference node."; | |
502 return; | |
503 } | |
504 } | 456 } |
| 457 sync_processor_->ProcessSyncChanges(changes); |
505 } | 458 } |
OLD | NEW |