| 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/sync/glue/preference_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/prefs/pref_service.h" | |
| 12 #include "chrome/browser/profiles/profile.h" | |
| 13 #include "chrome/browser/sync/engine/syncapi.h" | |
| 14 #include "chrome/browser/sync/glue/synchronized_preferences.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 namespace browser_sync { | |
| 23 | |
| 24 PreferenceModelAssociator::PreferenceModelAssociator( | |
| 25 ProfileSyncService* sync_service) | |
| 26 : sync_service_(sync_service), | |
| 27 preferences_node_id_(sync_api::kInvalidId) { | |
| 28 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 29 DCHECK(sync_service_); | |
| 30 | |
| 31 // Add the list of kSynchronizedPreferences to our local | |
| 32 // synced_preferences set, taking care to filter out any preferences | |
| 33 // that are not registered. | |
| 34 PrefService* pref_service = sync_service_->profile()->GetPrefs(); | |
| 35 for (size_t i = 0; i < arraysize(kSynchronizedPreferences); ++i) { | |
| 36 if (pref_service->FindPreference(kSynchronizedPreferences[i])) | |
| 37 synced_preferences_.insert(kSynchronizedPreferences[i]); | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 PreferenceModelAssociator::~PreferenceModelAssociator() { | |
| 42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 43 } | |
| 44 | |
| 45 bool PreferenceModelAssociator::InitPrefNodeAndAssociate( | |
| 46 sync_api::WriteTransaction* trans, | |
| 47 const sync_api::BaseNode& root, | |
| 48 const PrefService::Preference* pref) { | |
| 49 DCHECK(pref); | |
| 50 | |
| 51 PrefService* pref_service = sync_service_->profile()->GetPrefs(); | |
| 52 base::JSONReader reader; | |
| 53 std::string tag = pref->name(); | |
| 54 sync_api::WriteNode node(trans); | |
| 55 if (node.InitByClientTagLookup(syncable::PREFERENCES, tag)) { | |
| 56 // The server has a value for the preference. | |
| 57 const sync_pb::PreferenceSpecifics& preference( | |
| 58 node.GetPreferenceSpecifics()); | |
| 59 DCHECK_EQ(tag, preference.name()); | |
| 60 | |
| 61 if (pref->IsUserModifiable()) { | |
| 62 scoped_ptr<Value> value( | |
| 63 reader.JsonToValue(preference.value(), false, false)); | |
| 64 std::string pref_name = preference.name(); | |
| 65 if (!value.get()) { | |
| 66 LOG(ERROR) << "Failed to deserialize preference value: " | |
| 67 << reader.GetErrorMessage(); | |
| 68 return false; | |
| 69 } | |
| 70 | |
| 71 // Merge the server value of this preference with the local value. | |
| 72 scoped_ptr<Value> new_value(MergePreference(*pref, *value)); | |
| 73 | |
| 74 // Update the local preference based on what we got from the | |
| 75 // sync server. | |
| 76 if (new_value->IsType(Value::TYPE_NULL)) { | |
| 77 pref_service->ClearPref(pref_name.c_str()); | |
| 78 } else if (!new_value->IsType(pref->GetType())) { | |
| 79 LOG(WARNING) << "Synced value for " << preference.name() | |
| 80 << " is of type " << new_value->GetType() | |
| 81 << " which doesn't match pref type " << pref->GetType(); | |
| 82 } else if (!pref->GetValue()->Equals(new_value.get())) { | |
| 83 pref_service->Set(pref_name.c_str(), *new_value); | |
| 84 } | |
| 85 | |
| 86 AfterUpdateOperations(pref_name); | |
| 87 | |
| 88 // If the merge resulted in an updated value, write it back to | |
| 89 // the sync node. | |
| 90 if (!value->Equals(new_value.get()) && | |
| 91 !WritePreferenceToNode(pref->name(), *new_value, &node)) | |
| 92 return false; | |
| 93 } | |
| 94 Associate(pref, node.GetId()); | |
| 95 } else if (pref->IsUserControlled()) { | |
| 96 // The server doesn't have a value, but we have a user-controlled value, | |
| 97 // so we push it to the server. | |
| 98 sync_api::WriteNode write_node(trans); | |
| 99 if (!write_node.InitUniqueByCreation(syncable::PREFERENCES, root, tag)) { | |
| 100 LOG(ERROR) << "Failed to create preference sync node."; | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 104 // Update the sync node with the local value for this preference. | |
| 105 if (!WritePreferenceToNode(pref->name(), *pref->GetValue(), &write_node)) | |
| 106 return false; | |
| 107 | |
| 108 Associate(pref, write_node.GetId()); | |
| 109 } | |
| 110 | |
| 111 return true; | |
| 112 } | |
| 113 | |
| 114 bool PreferenceModelAssociator::AssociateModels() { | |
| 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 116 PrefService* pref_service = sync_service_->profile()->GetPrefs(); | |
| 117 | |
| 118 int64 root_id; | |
| 119 if (!GetSyncIdForTaggedNode(kPreferencesTag, &root_id)) { | |
| 120 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 121 << "might be running against an out-of-date server."; | |
| 122 return false; | |
| 123 } | |
| 124 | |
| 125 sync_api::WriteTransaction trans(sync_service_->GetUserShare()); | |
| 126 sync_api::ReadNode root(&trans); | |
| 127 if (!root.InitByIdLookup(root_id)) { | |
| 128 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 129 << "might be running against an out-of-date server."; | |
| 130 return false; | |
| 131 } | |
| 132 | |
| 133 for (std::set<std::string>::iterator it = synced_preferences_.begin(); | |
| 134 it != synced_preferences_.end(); ++it) { | |
| 135 const PrefService::Preference* pref = | |
| 136 pref_service->FindPreference((*it).c_str()); | |
| 137 DCHECK(pref); | |
| 138 InitPrefNodeAndAssociate(&trans, root, pref); | |
| 139 } | |
| 140 return true; | |
| 141 } | |
| 142 | |
| 143 bool PreferenceModelAssociator::DisassociateModels() { | |
| 144 id_map_.clear(); | |
| 145 id_map_inverse_.clear(); | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 149 bool PreferenceModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { | |
| 150 DCHECK(has_nodes); | |
| 151 *has_nodes = false; | |
| 152 int64 preferences_sync_id; | |
| 153 if (!GetSyncIdForTaggedNode(kPreferencesTag, &preferences_sync_id)) { | |
| 154 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 155 << "might be running against an out-of-date server."; | |
| 156 return false; | |
| 157 } | |
| 158 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
| 159 | |
| 160 sync_api::ReadNode preferences_node(&trans); | |
| 161 if (!preferences_node.InitByIdLookup(preferences_sync_id)) { | |
| 162 LOG(ERROR) << "Server did not create the top-level preferences node. We " | |
| 163 << "might be running against an out-of-date server."; | |
| 164 return false; | |
| 165 } | |
| 166 | |
| 167 // The sync model has user created nodes if the preferences folder has any | |
| 168 // children. | |
| 169 *has_nodes = sync_api::kInvalidId != preferences_node.GetFirstChildId(); | |
| 170 return true; | |
| 171 } | |
| 172 | |
| 173 const PrefService::Preference* | |
| 174 PreferenceModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) { | |
| 175 return NULL; | |
| 176 } | |
| 177 | |
| 178 bool PreferenceModelAssociator::InitSyncNodeFromChromeId( | |
| 179 const std::string& node_id, | |
| 180 sync_api::BaseNode* sync_node) { | |
| 181 return false; | |
| 182 } | |
| 183 | |
| 184 int64 PreferenceModelAssociator::GetSyncIdFromChromeId( | |
| 185 const std::string& preference_name) { | |
| 186 PreferenceNameToSyncIdMap::const_iterator iter = | |
| 187 id_map_.find(preference_name); | |
| 188 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; | |
| 189 } | |
| 190 | |
| 191 void PreferenceModelAssociator::Associate( | |
| 192 const PrefService::Preference* preference, int64 sync_id) { | |
| 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 194 DCHECK_NE(sync_api::kInvalidId, sync_id); | |
| 195 DCHECK(id_map_.find(preference->name()) == id_map_.end()); | |
| 196 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); | |
| 197 id_map_[preference->name()] = sync_id; | |
| 198 id_map_inverse_[sync_id] = preference->name(); | |
| 199 } | |
| 200 | |
| 201 void PreferenceModelAssociator::Disassociate(int64 sync_id) { | |
| 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 203 SyncIdToPreferenceNameMap::iterator iter = id_map_inverse_.find(sync_id); | |
| 204 if (iter == id_map_inverse_.end()) | |
| 205 return; | |
| 206 id_map_.erase(iter->second); | |
| 207 id_map_inverse_.erase(iter); | |
| 208 } | |
| 209 | |
| 210 bool PreferenceModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, | |
| 211 int64* sync_id) { | |
| 212 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
| 213 sync_api::ReadNode sync_node(&trans); | |
| 214 if (!sync_node.InitByTagLookup(tag.c_str())) | |
| 215 return false; | |
| 216 *sync_id = sync_node.GetId(); | |
| 217 return true; | |
| 218 } | |
| 219 | |
| 220 Value* PreferenceModelAssociator::MergePreference( | |
| 221 const PrefService::Preference& local_pref, | |
| 222 const Value& server_value) { | |
| 223 const std::string& name(local_pref.name()); | |
| 224 if (name == prefs::kURLsToRestoreOnStartup || | |
| 225 name == prefs::kDesktopNotificationAllowedOrigins || | |
| 226 name == prefs::kDesktopNotificationDeniedOrigins) { | |
| 227 return MergeListValues(*local_pref.GetValue(), server_value); | |
| 228 } | |
| 229 | |
| 230 if (name == prefs::kContentSettingsPatterns || | |
| 231 name == prefs::kGeolocationContentSettings) { | |
| 232 return MergeDictionaryValues(*local_pref.GetValue(), server_value); | |
| 233 } | |
| 234 | |
| 235 // If this is not a specially handled preference, server wins. | |
| 236 return server_value.DeepCopy(); | |
| 237 } | |
| 238 | |
| 239 bool PreferenceModelAssociator::WritePreferenceToNode( | |
| 240 const std::string& name, | |
| 241 const Value& value, | |
| 242 sync_api::WriteNode* node) { | |
| 243 std::string serialized; | |
| 244 JSONStringValueSerializer json(&serialized); | |
| 245 if (!json.Serialize(value)) { | |
| 246 LOG(ERROR) << "Failed to serialize preference value."; | |
| 247 return false; | |
| 248 } | |
| 249 | |
| 250 sync_pb::PreferenceSpecifics preference; | |
| 251 preference.set_name(name); | |
| 252 preference.set_value(serialized); | |
| 253 node->SetPreferenceSpecifics(preference); | |
| 254 // TODO(viettrungluu): eliminate conversion (it's temporary) | |
| 255 node->SetTitle(UTF8ToWide(name)); | |
| 256 return true; | |
| 257 } | |
| 258 | |
| 259 Value* PreferenceModelAssociator::MergeListValues(const Value& from_value, | |
| 260 const Value& to_value) { | |
| 261 if (from_value.GetType() == Value::TYPE_NULL) | |
| 262 return to_value.DeepCopy(); | |
| 263 if (to_value.GetType() == Value::TYPE_NULL) | |
| 264 return from_value.DeepCopy(); | |
| 265 | |
| 266 DCHECK(from_value.GetType() == Value::TYPE_LIST); | |
| 267 DCHECK(to_value.GetType() == Value::TYPE_LIST); | |
| 268 const ListValue& from_list_value = static_cast<const ListValue&>(from_value); | |
| 269 const ListValue& to_list_value = static_cast<const ListValue&>(to_value); | |
| 270 ListValue* result = to_list_value.DeepCopy(); | |
| 271 | |
| 272 for (ListValue::const_iterator i = from_list_value.begin(); | |
| 273 i != from_list_value.end(); ++i) { | |
| 274 Value* value = (*i)->DeepCopy(); | |
| 275 result->AppendIfNotPresent(value); | |
| 276 } | |
| 277 return result; | |
| 278 } | |
| 279 | |
| 280 Value* PreferenceModelAssociator::MergeDictionaryValues( | |
| 281 const Value& from_value, | |
| 282 const Value& to_value) { | |
| 283 if (from_value.GetType() == Value::TYPE_NULL) | |
| 284 return to_value.DeepCopy(); | |
| 285 if (to_value.GetType() == Value::TYPE_NULL) | |
| 286 return from_value.DeepCopy(); | |
| 287 | |
| 288 DCHECK(from_value.GetType() == Value::TYPE_DICTIONARY); | |
| 289 DCHECK(to_value.GetType() == Value::TYPE_DICTIONARY); | |
| 290 const DictionaryValue& from_dict_value = | |
| 291 static_cast<const DictionaryValue&>(from_value); | |
| 292 const DictionaryValue& to_dict_value = | |
| 293 static_cast<const DictionaryValue&>(to_value); | |
| 294 DictionaryValue* result = to_dict_value.DeepCopy(); | |
| 295 | |
| 296 for (DictionaryValue::key_iterator key = from_dict_value.begin_keys(); | |
| 297 key != from_dict_value.end_keys(); ++key) { | |
| 298 Value* from_value; | |
| 299 bool success = from_dict_value.GetWithoutPathExpansion(*key, &from_value); | |
| 300 DCHECK(success); | |
| 301 | |
| 302 Value* to_key_value; | |
| 303 if (result->GetWithoutPathExpansion(*key, &to_key_value)) { | |
| 304 if (to_key_value->GetType() == Value::TYPE_DICTIONARY) { | |
| 305 Value* merged_value = MergeDictionaryValues(*from_value, *to_key_value); | |
| 306 result->SetWithoutPathExpansion(*key, merged_value); | |
| 307 } | |
| 308 // Note that for all other types we want to preserve the "to" | |
| 309 // values so we do nothing here. | |
| 310 } else { | |
| 311 result->SetWithoutPathExpansion(*key, from_value->DeepCopy()); | |
| 312 } | |
| 313 } | |
| 314 return result; | |
| 315 } | |
| 316 | |
| 317 void PreferenceModelAssociator::AfterUpdateOperations( | |
| 318 const std::string& pref_name) { | |
| 319 // The bookmark bar visibility preference requires a special | |
| 320 // notification to update the UI. | |
| 321 if (0 == pref_name.compare(prefs::kShowBookmarkBar)) { | |
| 322 NotificationService::current()->Notify( | |
| 323 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, | |
| 324 Source<PreferenceModelAssociator>(this), | |
| 325 NotificationService::NoDetails()); | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 bool PreferenceModelAssociator::CryptoReadyIfNecessary() { | |
| 330 // We only access the cryptographer while holding a transaction. | |
| 331 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); | |
| 332 syncable::ModelTypeSet encrypted_types; | |
| 333 sync_service_->GetEncryptedDataTypes(&encrypted_types); | |
| 334 return encrypted_types.count(syncable::PREFERENCES) == 0 || | |
| 335 sync_service_->IsCryptographerReady(&trans); | |
| 336 } | |
| 337 | |
| 338 } // namespace browser_sync | |
| OLD | NEW |