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 339