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