preference_model_associator.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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/values.h" 10#include "base/utf_string_conversions.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/json_value_serializer.h" 18#include "chrome/common/notification_service.h" 19#include "chrome/common/pref_names.h" 20#include "content/browser/browser_thread.h" 21 22namespace browser_sync { 23 24PreferenceModelAssociator::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 41PreferenceModelAssociator::~PreferenceModelAssociator() { 42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 43} 44 45bool 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 114bool 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 InitPrefNodeAndAssociate(&trans, root, pref); 138 } 139 return true; 140} 141 142bool PreferenceModelAssociator::DisassociateModels() { 143 id_map_.clear(); 144 id_map_inverse_.clear(); 145 return true; 146} 147 148bool PreferenceModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 149 DCHECK(has_nodes); 150 *has_nodes = false; 151 int64 preferences_sync_id; 152 if (!GetSyncIdForTaggedNode(kPreferencesTag, &preferences_sync_id)) { 153 LOG(ERROR) << "Server did not create the top-level preferences node. We " 154 << "might be running against an out-of-date server."; 155 return false; 156 } 157 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); 158 159 sync_api::ReadNode preferences_node(&trans); 160 if (!preferences_node.InitByIdLookup(preferences_sync_id)) { 161 LOG(ERROR) << "Server did not create the top-level preferences node. We " 162 << "might be running against an out-of-date server."; 163 return false; 164 } 165 166 // The sync model has user created nodes if the preferences folder has any 167 // children. 168 *has_nodes = sync_api::kInvalidId != preferences_node.GetFirstChildId(); 169 return true; 170} 171 172const PrefService::Preference* 173PreferenceModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) { 174 return NULL; 175} 176 177bool PreferenceModelAssociator::InitSyncNodeFromChromeId( 178 const std::string& node_id, 179 sync_api::BaseNode* sync_node) { 180 return false; 181} 182 183int64 PreferenceModelAssociator::GetSyncIdFromChromeId( 184 const std::string& preference_name) { 185 PreferenceNameToSyncIdMap::const_iterator iter = 186 id_map_.find(preference_name); 187 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; 188} 189 190void PreferenceModelAssociator::Associate( 191 const PrefService::Preference* preference, int64 sync_id) { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 193 DCHECK_NE(sync_api::kInvalidId, sync_id); 194 DCHECK(id_map_.find(preference->name()) == id_map_.end()); 195 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); 196 id_map_[preference->name()] = sync_id; 197 id_map_inverse_[sync_id] = preference->name(); 198} 199 200void PreferenceModelAssociator::Disassociate(int64 sync_id) { 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 202 SyncIdToPreferenceNameMap::iterator iter = id_map_inverse_.find(sync_id); 203 if (iter == id_map_inverse_.end()) 204 return; 205 id_map_.erase(iter->second); 206 id_map_inverse_.erase(iter); 207} 208 209bool PreferenceModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, 210 int64* sync_id) { 211 sync_api::ReadTransaction trans(sync_service_->GetUserShare()); 212 sync_api::ReadNode sync_node(&trans); 213 if (!sync_node.InitByTagLookup(tag.c_str())) 214 return false; 215 *sync_id = sync_node.GetId(); 216 return true; 217} 218 219Value* PreferenceModelAssociator::MergePreference( 220 const PrefService::Preference& local_pref, 221 const Value& server_value) { 222 const std::string& name(local_pref.name()); 223 if (name == prefs::kURLsToRestoreOnStartup || 224 name == prefs::kDesktopNotificationAllowedOrigins || 225 name == prefs::kDesktopNotificationDeniedOrigins) { 226 return MergeListValues(*local_pref.GetValue(), server_value); 227 } 228 229 if (name == prefs::kContentSettingsPatterns || 230 name == prefs::kGeolocationContentSettings) { 231 return MergeDictionaryValues(*local_pref.GetValue(), server_value); 232 } 233 234 // If this is not a specially handled preference, server wins. 235 return server_value.DeepCopy(); 236} 237 238bool PreferenceModelAssociator::WritePreferenceToNode( 239 const std::string& name, 240 const Value& value, 241 sync_api::WriteNode* node) { 242 std::string serialized; 243 JSONStringValueSerializer json(&serialized); 244 if (!json.Serialize(value)) { 245 LOG(ERROR) << "Failed to serialize preference value."; 246 return false; 247 } 248 249 sync_pb::PreferenceSpecifics preference; 250 preference.set_name(name); 251 preference.set_value(serialized); 252 node->SetPreferenceSpecifics(preference); 253 // TODO(viettrungluu): eliminate conversion (it's temporary) 254 node->SetTitle(UTF8ToWide(name)); 255 return true; 256} 257 258Value* PreferenceModelAssociator::MergeListValues(const Value& from_value, 259 const Value& to_value) { 260 if (from_value.GetType() == Value::TYPE_NULL) 261 return to_value.DeepCopy(); 262 if (to_value.GetType() == Value::TYPE_NULL) 263 return from_value.DeepCopy(); 264 265 DCHECK(from_value.GetType() == Value::TYPE_LIST); 266 DCHECK(to_value.GetType() == Value::TYPE_LIST); 267 const ListValue& from_list_value = static_cast<const ListValue&>(from_value); 268 const ListValue& to_list_value = static_cast<const ListValue&>(to_value); 269 ListValue* result = to_list_value.DeepCopy(); 270 271 for (ListValue::const_iterator i = from_list_value.begin(); 272 i != from_list_value.end(); ++i) { 273 Value* value = (*i)->DeepCopy(); 274 if (!result->AppendIfNotPresent(value)) 275 delete value; 276 } 277 return result; 278} 279 280Value* 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 317void 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} // namespace browser_sync 330