Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/prefs/pref_model_associator.h" | |
| 6 | |
| 7 #include "base/json/json_reader.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/utf_string_conversions.h" | |
| 10 #include "base/values.h" | |
| 11 #include "chrome/browser/profiles/profile.h" | |
| 12 #include "chrome/browser/sync/engine/syncapi.h" | |
| 13 #include "chrome/browser/sync/glue/generic_change_processor.h" | |
| 14 #include "chrome/browser/sync/profile_sync_service.h" | |
| 15 #include "chrome/browser/sync/protocol/preference_specifics.pb.h" | |
| 16 #include "chrome/common/pref_names.h" | |
| 17 #include "content/browser/browser_thread.h" | |
| 18 #include "content/common/json_value_serializer.h" | |
| 19 #include "content/common/notification_service.h" | |
| 20 | |
| 21 PrefModelAssociator::PrefModelAssociator() | |
| 22 : pref_service_(NULL), | |
| 23 sync_service_(NULL), | |
| 24 preferences_node_id_(sync_api::kInvalidId), | |
| 25 models_associated_(false), | |
| 26 processing_syncer_changes_(false), | |
| 27 processing_syncapi_changes_(false), | |
|
tim (not reviewing)
2011/04/28 21:51:44
could we combine these into a single "processing_c
Nicolas Zea
2011/05/03 18:59:36
I don't think so. They get reset in different plac
| |
| 28 change_processor_(NULL) { | |
| 29 } | |
| 30 | |
| 31 PrefModelAssociator::PrefModelAssociator( | |
| 32 PrefService* pref_service) | |
| 33 : pref_service_(pref_service), | |
| 34 sync_service_(NULL), | |
| 35 preferences_node_id_(sync_api::kInvalidId), | |
| 36 models_associated_(false), | |
| 37 processing_syncer_changes_(false), | |
| 38 processing_syncapi_changes_(false), | |
| 39 change_processor_(NULL) { | |
| 40 DCHECK(CalledOnValidThread()); | |
| 41 registrar_.Add(this, | |
| 42 NotificationType::PROFILE_SYNC_SERVICE_LOADING, | |
|
tim (not reviewing)
2011/04/28 21:51:44
Are we sure we always get this? That there is no r
Nicolas Zea
2011/05/03 18:59:36
Not used anymore.
| |
| 43 NotificationService::AllSources()); | |
| 44 } | |
| 45 | |
| 46 PrefModelAssociator::~PrefModelAssociator() { | |
| 47 DCHECK(CalledOnValidThread()); | |
| 48 change_processor_ = NULL; | |
| 49 sync_service_ = NULL; | |
| 50 pref_service_ = NULL; | |
| 51 } | |
| 52 void PrefModelAssociator::Observe(NotificationType type, | |
| 53 const NotificationSource& source, | |
| 54 const NotificationDetails& details) { | |
| 55 DCHECK(CalledOnValidThread()); | |
| 56 DCHECK_EQ(NotificationType::PROFILE_SYNC_SERVICE_LOADING, type.value); | |
| 57 ProfileSyncService* sync_service_source = | |
| 58 Source<ProfileSyncService>(source).ptr(); | |
| 59 DCHECK(sync_service_source); | |
| 60 if (sync_service_source->profile()->GetPrefs() != pref_service_) | |
| 61 return; // This is a different profile; | |
| 62 DCHECK(!sync_service_); | |
| 63 sync_service_ = sync_service_source; | |
| 64 sync_service_->RegisterModelAssociator(this); | |
|
tim (not reviewing)
2011/04/28 21:51:44
Could we registrar_.RevokeAll at this point?
Nicolas Zea
2011/05/03 18:59:36
n/a.
| |
| 65 } | |
| 66 | |
| 67 bool PrefModelAssociator::InitPrefNodeAndAssociate( | |
| 68 sync_api::WriteTransaction* trans, | |
| 69 const sync_api::BaseNode& root, | |
| 70 const PrefService::Preference* pref) { | |
| 71 DCHECK(pref); | |
| 72 | |
| 73 base::JSONReader reader; | |
| 74 std::string tag = pref->name(); | |
| 75 sync_api::WriteNode node(trans); | |
| 76 if (node.InitByClientTagLookup(syncable::PREFERENCES, tag)) { | |
| 77 // The server has a value for the preference. | |
| 78 const sync_pb::PreferenceSpecifics& preference( | |
| 79 node.GetPreferenceSpecifics()); | |
| 80 DCHECK_EQ(tag, preference.name()); | |
| 81 | |
| 82 if (pref->IsUserModifiable()) { | |
|
tim (not reviewing)
2011/04/28 21:51:44
It might be a bit cleaner to do
if (!IsUserModifia
Nicolas Zea
2011/05/03 18:59:36
Done.
| |
| 83 scoped_ptr<Value> value( | |
| 84 reader.JsonToValue(preference.value(), false, false)); | |
| 85 std::string pref_name = preference.name(); | |
| 86 if (!value.get()) { | |
| 87 LOG(ERROR) << "Failed to deserialize preference value: " | |
| 88 << reader.GetErrorMessage(); | |
| 89 return false; | |
| 90 } | |
| 91 | |
| 92 // Merge the server value of this preference with the local value. | |
| 93 scoped_ptr<Value> new_value(MergePreference(*pref, *value)); | |
| 94 | |
| 95 // Update the local preference based on what we got from the | |
| 96 // sync server. | |
| 97 if (new_value->IsType(Value::TYPE_NULL)) { | |
| 98 pref_service_->ClearPref(pref_name.c_str()); | |
| 99 } else if (!new_value->IsType(pref->GetType())) { | |
| 100 LOG(WARNING) << "Synced value for " << preference.name() | |
| 101 << " is of type " << new_value->GetType() | |
| 102 << " which doesn't match pref type " << pref->GetType(); | |
| 103 } else if (!pref->GetValue()->Equals(new_value.get())) { | |
| 104 pref_service_->Set(pref_name.c_str(), *new_value); | |
| 105 } | |
| 106 | |
| 107 AfterUpdateOperations(pref_name); | |
| 108 | |
| 109 // If the merge resulted in an updated value, write it back to | |
| 110 // the sync node. | |
| 111 if (!value->Equals(new_value.get()) && | |
| 112 !WritePreferenceToNode(pref->name(), *new_value, &node)) | |
| 113 return false; | |
| 114 } | |
| 115 Associate(pref, node.GetId()); | |
| 116 } else if (pref->IsUserControlled()) { | |
| 117 // The server doesn't have a value, but we have a user-controlled value, | |
| 118 // so we push it to the server. | |
| 119 sync_api::WriteNode write_node(trans); | |
| 120 if (!write_node.InitUniqueByCreation(syncable::PREFERENCES, root, tag)) { | |
| 121 LOG(ERROR) << "Failed to create preference sync node."; | |
| 122 return false; | |
| 123 } | |
| 124 | |
| 125 // Update the sync node with the local value for this preference. | |
| 126 if (!WritePreferenceToNode(pref->name(), *pref->GetValue(), &write_node)) | |
| 127 return false; | |
| 128 | |
| 129 Associate(pref, write_node.GetId()); | |
| 130 } | |
|
tim (not reviewing)
2011/04/28 21:51:44
maybe a comment for the implicit 'else' case here?
Nicolas Zea
2011/05/03 18:59:36
Done.
| |
| 131 | |
| 132 return true; | |
| 133 } | |
| 134 | |
| 135 bool PrefModelAssociator::AssociateModels() { | |
| 136 DCHECK(CalledOnValidThread()); | |
| 137 | |
| 138 int64 root_id; | |
| 139 if (!GetSyncIdForTaggedNode(kPreferencesTag, &root_id)) { | |
| 140 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 141 << "might be running against an out-of-date server."; | |
| 142 return false; | |
| 143 } | |
| 144 | |
| 145 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); | |
| 146 sync_api::ReadNode root(&trans); | |
| 147 if (!root.InitByIdLookup(root_id)) { | |
| 148 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 149 << "might be running against an out-of-date server."; | |
| 150 return false; | |
| 151 } | |
| 152 | |
| 153 for (std::set<std::string>::iterator it = synced_preferences_.begin(); | |
| 154 it != synced_preferences_.end(); ++it) { | |
| 155 std::string name = *it; | |
| 156 const PrefService::Preference* pref = | |
| 157 pref_service_->FindPreference(name.c_str()); | |
| 158 VLOG(1) << "Associating preference " << name; | |
| 159 DCHECK(pref); | |
| 160 if (!pref->IsUserModifiable()) | |
| 161 continue; // We don't sync preferences the user cannot change. | |
| 162 InitPrefNodeAndAssociate(&trans, root, pref); | |
| 163 } | |
| 164 models_associated_ = true; | |
| 165 return true; | |
| 166 } | |
| 167 | |
| 168 bool PrefModelAssociator::DisassociateModels() { | |
| 169 id_map_.clear(); | |
| 170 id_map_inverse_.clear(); | |
| 171 models_associated_ = false; | |
|
tim (not reviewing)
2011/04/28 21:51:44
does it make sense to clear / reset anything else
Nicolas Zea
2011/05/03 18:59:36
Rest should be remain, as they're set before Assoc
| |
| 172 return true; | |
| 173 } | |
| 174 | |
| 175 bool PrefModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { | |
| 176 DCHECK(has_nodes); | |
| 177 *has_nodes = false; | |
| 178 int64 preferences_sync_id; | |
| 179 if (!GetSyncIdForTaggedNode(kPreferencesTag, &preferences_sync_id)) { | |
| 180 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 181 << "might be running against an out-of-date server."; | |
| 182 return false; | |
| 183 } | |
| 184 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
| 185 | |
| 186 sync_api::ReadNode preferences_node(&trans); | |
| 187 if (!preferences_node.InitByIdLookup(preferences_sync_id)) { | |
| 188 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 189 << "might be running against an out-of-date server."; | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 // The sync model has user created nodes if the preferences folder has any | |
| 194 // children. | |
| 195 *has_nodes = sync_api::kInvalidId != preferences_node.GetFirstChildId(); | |
| 196 return true; | |
| 197 } | |
| 198 | |
| 199 int64 PrefModelAssociator::GetSyncIdFromChromeId( | |
| 200 const std::string& preference_name) { | |
| 201 PreferenceNameToSyncIdMap::const_iterator iter = | |
| 202 id_map_.find(preference_name); | |
| 203 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; | |
| 204 } | |
| 205 | |
| 206 void PrefModelAssociator::Associate( | |
| 207 const PrefService::Preference* preference, int64 sync_id) { | |
| 208 DCHECK(CalledOnValidThread()); | |
| 209 | |
| 210 std::string name = preference->name(); | |
| 211 DCHECK_NE(sync_api::kInvalidId, sync_id); | |
| 212 DCHECK_EQ(0U, id_map_.count(name)); | |
| 213 DCHECK_EQ(0U, id_map_inverse_.count(sync_id)); | |
| 214 id_map_[name] = sync_id; | |
| 215 id_map_inverse_[sync_id] = name; | |
| 216 } | |
| 217 | |
| 218 void PrefModelAssociator::Disassociate(int64 sync_id) { | |
| 219 DCHECK(CalledOnValidThread()); | |
| 220 SyncIdToPreferenceNameMap::iterator iter = id_map_inverse_.find(sync_id); | |
|
tim (not reviewing)
2011/04/28 21:51:44
do we actually use id_map_inverse_ anywhere?
Nicolas Zea
2011/05/03 18:59:36
Aside from here, no. There's a higher level todo t
| |
| 221 if (iter == id_map_inverse_.end()) | |
| 222 return; | |
| 223 id_map_.erase(iter->second); | |
| 224 id_map_inverse_.erase(iter); | |
| 225 } | |
| 226 | |
| 227 bool PrefModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, | |
| 228 int64* sync_id) { | |
| 229 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
| 230 sync_api::ReadNode sync_node(&trans); | |
| 231 if (!sync_node.InitByTagLookup(tag.c_str())) | |
| 232 return false; | |
| 233 *sync_id = sync_node.GetId(); | |
| 234 return true; | |
| 235 } | |
| 236 | |
| 237 Value* PrefModelAssociator::MergePreference( | |
| 238 const PrefService::Preference& local_pref, | |
| 239 const Value& server_value) { | |
| 240 const std::string& name(local_pref.name()); | |
| 241 if (name == prefs::kURLsToRestoreOnStartup || | |
|
tim (not reviewing)
2011/04/28 21:51:44
Instead of doing this, could we not do local_pref-
Nicolas Zea
2011/05/03 18:59:36
I'm not sure how that would solve this? GetType te
| |
| 242 name == prefs::kDesktopNotificationAllowedOrigins || | |
| 243 name == prefs::kDesktopNotificationDeniedOrigins) { | |
| 244 return MergeListValues(*local_pref.GetValue(), server_value); | |
| 245 } | |
| 246 | |
| 247 if (name == prefs::kContentSettingsPatterns || | |
| 248 name == prefs::kGeolocationContentSettings) { | |
| 249 return MergeDictionaryValues(*local_pref.GetValue(), server_value); | |
| 250 } | |
| 251 | |
| 252 // If this is not a specially handled preference, server wins. | |
| 253 return server_value.DeepCopy(); | |
| 254 } | |
| 255 | |
| 256 bool PrefModelAssociator::WritePreferenceToNode( | |
| 257 const std::string& name, | |
| 258 const Value& value, | |
| 259 sync_api::WriteNode* node) { | |
| 260 std::string serialized; | |
| 261 JSONStringValueSerializer json(&serialized); | |
| 262 if (!json.Serialize(value)) { | |
| 263 LOG(ERROR) << "Failed to serialize preference value."; | |
| 264 return false; | |
| 265 } | |
| 266 | |
| 267 sync_pb::PreferenceSpecifics preference; | |
| 268 preference.set_name(name); | |
| 269 preference.set_value(serialized); | |
| 270 node->SetPreferenceSpecifics(preference); | |
| 271 // TODO(viettrungluu): eliminate conversion (it's temporary) | |
| 272 node->SetTitle(UTF8ToWide(name)); | |
| 273 return true; | |
| 274 } | |
| 275 | |
| 276 Value* PrefModelAssociator::MergeListValues(const Value& from_value, | |
| 277 const Value& to_value) { | |
|
tim (not reviewing)
2011/04/28 21:51:44
nit - indent
Nicolas Zea
2011/05/03 18:59:36
Done.
| |
| 278 if (from_value.GetType() == Value::TYPE_NULL) | |
| 279 return to_value.DeepCopy(); | |
| 280 if (to_value.GetType() == Value::TYPE_NULL) | |
| 281 return from_value.DeepCopy(); | |
| 282 | |
| 283 DCHECK(from_value.GetType() == Value::TYPE_LIST); | |
| 284 DCHECK(to_value.GetType() == Value::TYPE_LIST); | |
| 285 const ListValue& from_list_value = static_cast<const ListValue&>(from_value); | |
| 286 const ListValue& to_list_value = static_cast<const ListValue&>(to_value); | |
| 287 ListValue* result = to_list_value.DeepCopy(); | |
| 288 | |
| 289 for (ListValue::const_iterator i = from_list_value.begin(); | |
| 290 i != from_list_value.end(); ++i) { | |
| 291 Value* value = (*i)->DeepCopy(); | |
| 292 result->AppendIfNotPresent(value); | |
| 293 } | |
| 294 return result; | |
| 295 } | |
| 296 | |
| 297 Value* PrefModelAssociator::MergeDictionaryValues( | |
| 298 const Value& from_value, | |
| 299 const Value& to_value) { | |
| 300 if (from_value.GetType() == Value::TYPE_NULL) | |
| 301 return to_value.DeepCopy(); | |
| 302 if (to_value.GetType() == Value::TYPE_NULL) | |
| 303 return from_value.DeepCopy(); | |
| 304 | |
| 305 DCHECK(from_value.GetType() == Value::TYPE_DICTIONARY); | |
|
tim (not reviewing)
2011/04/28 21:51:44
DCHECK_EQ
Nicolas Zea
2011/05/03 18:59:36
Done.
| |
| 306 DCHECK(to_value.GetType() == Value::TYPE_DICTIONARY); | |
| 307 const DictionaryValue& from_dict_value = | |
| 308 static_cast<const DictionaryValue&>(from_value); | |
| 309 const DictionaryValue& to_dict_value = | |
| 310 static_cast<const DictionaryValue&>(to_value); | |
| 311 DictionaryValue* result = to_dict_value.DeepCopy(); | |
| 312 | |
| 313 for (DictionaryValue::key_iterator key = from_dict_value.begin_keys(); | |
| 314 key != from_dict_value.end_keys(); ++key) { | |
| 315 Value* from_value; | |
| 316 bool success = from_dict_value.GetWithoutPathExpansion(*key, &from_value); | |
| 317 DCHECK(success); | |
| 318 | |
| 319 Value* to_key_value; | |
| 320 if (result->GetWithoutPathExpansion(*key, &to_key_value)) { | |
| 321 if (to_key_value->GetType() == Value::TYPE_DICTIONARY) { | |
| 322 Value* merged_value = MergeDictionaryValues(*from_value, *to_key_value); | |
| 323 result->SetWithoutPathExpansion(*key, merged_value); | |
| 324 } | |
| 325 // Note that for all other types we want to preserve the "to" | |
| 326 // values so we do nothing here. | |
| 327 } else { | |
| 328 result->SetWithoutPathExpansion(*key, from_value->DeepCopy()); | |
| 329 } | |
| 330 } | |
| 331 return result; | |
| 332 } | |
| 333 | |
| 334 void PrefModelAssociator::AfterUpdateOperations( | |
|
tim (not reviewing)
2011/04/28 21:51:44
SendUpdateNotificationsIfNecessary?
Nicolas Zea
2011/05/03 18:59:36
Done.
| |
| 335 const std::string& pref_name) { | |
| 336 // The bookmark bar visibility preference requires a special | |
| 337 // notification to update the UI. | |
| 338 if (0 == pref_name.compare(prefs::kShowBookmarkBar)) { | |
| 339 NotificationService::current()->Notify( | |
| 340 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, | |
| 341 Source<PrefModelAssociator>(this), | |
| 342 NotificationService::NoDetails()); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 // Not implemented; | |
|
tim (not reviewing)
2011/04/28 21:51:44
nit - .
Nicolas Zea
2011/05/03 18:59:36
Done.
| |
| 347 void PrefModelAssociator::AbortAssociation() {} | |
| 348 | |
| 349 bool PrefModelAssociator::CryptoReadyIfNecessary() { | |
| 350 // We only access the cryptographer while holding a transaction. | |
| 351 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
| 352 syncable::ModelTypeSet encrypted_types; | |
| 353 sync_service_->GetEncryptedDataTypes(&encrypted_types); | |
| 354 return encrypted_types.count(syncable::PREFERENCES) == 0 || | |
| 355 sync_service_->IsCryptographerReady(&trans); | |
| 356 } | |
| 357 | |
| 358 syncable::ModelType PrefModelAssociator::model_type() const { | |
| 359 return syncable::PREFERENCES; | |
| 360 } | |
| 361 | |
| 362 // Not implemented. | |
| 363 void PrefModelAssociator::set_change_processor( | |
| 364 browser_sync::ChangeProcessor* processor) {} | |
| 365 | |
| 366 browser_sync::ChangeProcessor* PrefModelAssociator::change_processor() { | |
| 367 if (!change_processor_) { | |
| 368 change_processor_ = | |
| 369 new browser_sync::GenericChangeProcessor(sync_service_, this); | |
| 370 } | |
| 371 return change_processor_; | |
| 372 } | |
| 373 | |
| 374 void PrefModelAssociator::ApplyChangesFromSync( | |
| 375 const sync_api::BaseTransaction* trans, | |
| 376 const sync_api::SyncManager::ChangeRecord* changes, | |
| 377 int change_count) { | |
| 378 if (!models_associated_) | |
| 379 return; | |
| 380 processing_syncer_changes_ = true; | |
| 381 for (int i = 0; i < change_count; ++i) { | |
| 382 // TODO(ncarter): Can't look up the name for deletions: lookup of | |
|
tim (not reviewing)
2011/04/28 21:51:44
is this still true? we have the entire specifics
Nicolas Zea
2011/05/03 18:59:36
True, although we never delete preferences (due to
| |
| 383 // deleted items fails at the syncapi layer. However, the node should | |
| 384 // generally still exist in the syncable database; we just need to | |
| 385 // plumb the syncapi so that it succeeds. | |
| 386 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == | |
| 387 changes[i].action) { | |
| 388 // Until the above is fixed, we have no choice but to ignore deletions. | |
| 389 LOG(ERROR) << "No way to handle pref deletion"; | |
| 390 continue; | |
| 391 } | |
| 392 | |
| 393 sync_api::ReadNode node(trans); | |
| 394 if (!node.InitByIdLookup(changes[i].id)) { | |
| 395 LOG(ERROR) << "Preference node lookup failed."; | |
| 396 processing_syncer_changes_ = false; | |
| 397 return; | |
| 398 } | |
| 399 DCHECK(syncable::PREFERENCES == node.GetModelType()); | |
| 400 std::string name; | |
| 401 sync_pb::PreferenceSpecifics pref_specifics = | |
| 402 node.GetPreferenceSpecifics(); | |
| 403 scoped_ptr<Value> value(ReadPreferenceSpecifics(pref_specifics, | |
| 404 &name)); | |
| 405 // Skip values we can't deserialize. | |
| 406 if (!value.get()) | |
| 407 continue; | |
| 408 | |
| 409 // It is possible that we may receive a change to a preference we do not | |
| 410 // want to sync. For example if the user is syncing a Mac client and a | |
| 411 // Windows client, the Windows client does not support | |
| 412 // kConfirmToQuitEnabled. Ignore updates from these preferences. | |
| 413 const char* pref_name = name.c_str(); | |
| 414 if (!IsPrefRegistered(pref_name)) | |
| 415 continue; | |
| 416 | |
| 417 const PrefService::Preference* pref = | |
| 418 pref_service_->FindPreference(pref_name); | |
| 419 DCHECK(pref); | |
| 420 if (!pref->IsUserModifiable()) { | |
| 421 continue; | |
| 422 } | |
| 423 | |
| 424 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == | |
| 425 changes[i].action) { | |
| 426 pref_service_->ClearPref(pref_name); | |
| 427 } else { | |
| 428 pref_service_->Set(pref_name, *value); | |
| 429 | |
| 430 // If this is a newly added node, associate. | |
| 431 if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == | |
| 432 changes[i].action) { | |
| 433 Associate(pref, changes[i].id); | |
| 434 } | |
| 435 | |
| 436 AfterUpdateOperations(name); | |
| 437 } | |
| 438 } | |
| 439 processing_syncer_changes_ = false; | |
| 440 } | |
| 441 | |
| 442 Value* PrefModelAssociator::ReadPreferenceSpecifics( | |
| 443 const sync_pb::PreferenceSpecifics& preference, | |
| 444 std::string* name) { | |
| 445 base::JSONReader reader; | |
| 446 scoped_ptr<Value> value(reader.JsonToValue(preference.value(), false, false)); | |
| 447 if (!value.get()) { | |
| 448 std::string err = "Failed to deserialize preference value: " + | |
| 449 reader.GetErrorMessage(); | |
| 450 LOG(ERROR) << err; | |
| 451 return NULL; | |
| 452 } | |
| 453 *name = preference.name(); | |
| 454 return value.release(); | |
| 455 } | |
| 456 | |
| 457 | |
| 458 std::set<std::string> PrefModelAssociator::synced_preferences() { | |
| 459 return synced_preferences_; | |
| 460 } | |
| 461 | |
| 462 void PrefModelAssociator::RegisterPref(const char* name) { | |
| 463 DCHECK(!models_associated_ && synced_preferences_.count(name) == 0); | |
| 464 synced_preferences_.insert(name); | |
| 465 } | |
| 466 | |
| 467 bool PrefModelAssociator::IsPrefRegistered(const char* name) { | |
| 468 return synced_preferences_.count(name) > 0; | |
| 469 } | |
| 470 | |
| 471 void PrefModelAssociator::ProcessPrefChange(const std::string& name) { | |
| 472 if (processing_syncer_changes_ || processing_syncapi_changes_) | |
| 473 return; // These are changes originating from us, ignore. | |
| 474 | |
| 475 // We only process changes if we've already associated models. | |
| 476 if (!sync_service_ || !models_associated_) | |
| 477 return; | |
| 478 | |
| 479 const PrefService::Preference* preference = | |
| 480 pref_service_->FindPreference(name.c_str()); | |
| 481 if (!IsPrefRegistered(name.c_str())) | |
| 482 return; // We are not syncing this preference. | |
| 483 | |
| 484 // The preference does not have a node. This can happen if the preference | |
| 485 // held the default value at association time. Create one and associate. | |
| 486 int64 root_id; | |
| 487 if (!GetSyncIdForTaggedNode(kPreferencesTag, &root_id)) { | |
| 488 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 489 << "might be running against an out-of-date server."; | |
| 490 return; | |
| 491 } | |
| 492 | |
| 493 int64 sync_id = GetSyncIdFromChromeId(name); | |
| 494 if (!preference->IsUserModifiable()) { | |
| 495 // If the preference is not currently user modifiable, disassociate, so that | |
| 496 // if it becomes user modifiable me pick up the server value. | |
| 497 Disassociate(sync_id); | |
| 498 return; | |
| 499 } | |
| 500 | |
| 501 processing_syncapi_changes_ = true; | |
| 502 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); | |
| 503 | |
| 504 // Since we don't create sync nodes for preferences that are not under control | |
| 505 // of the user or still have their default value, this changed preference may | |
| 506 // not have a sync node yet. If so, we create a node. Similarly, a preference | |
| 507 // may become user-modifiable (e.g. due to laxer policy configuration), in | |
| 508 // which case we also need to create a sync node and associate it. | |
| 509 if (sync_id == sync_api::kInvalidId) { | |
| 510 sync_api::ReadNode root(&trans); | |
| 511 if (!root.InitByIdLookup(root_id)) { | |
| 512 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 513 << "might be running against an out-of-date server."; | |
| 514 processing_syncapi_changes_ = false; | |
|
tim (not reviewing)
2011/04/28 21:51:44
Check out base::AutoReset (auto_reset.h)
Nicolas Zea
2011/05/03 18:59:36
Nice, was wondering if something like that existed
| |
| 515 return; | |
| 516 } | |
| 517 InitPrefNodeAndAssociate(&trans, root, preference); | |
| 518 } else { | |
| 519 sync_api::WriteNode node(&trans); | |
| 520 if (!node.InitByIdLookup(sync_id)) { | |
| 521 LOG(ERROR) << "Preference node lookup failed."; | |
| 522 processing_syncapi_changes_ = false; | |
| 523 return; | |
| 524 } | |
| 525 | |
| 526 if (!WritePreferenceToNode(name, *preference->GetValue(), &node)) { | |
| 527 LOG(ERROR) << "Failed to update preference node."; | |
| 528 processing_syncapi_changes_ = false; | |
| 529 return; | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 processing_syncapi_changes_ = false; | |
| 534 } | |
| OLD | NEW |