• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PreferenceModelAssociator(ProfileSyncService * sync_service)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 
~PreferenceModelAssociator()41 PreferenceModelAssociator::~PreferenceModelAssociator() {
42   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
43 }
44 
InitPrefNodeAndAssociate(sync_api::WriteTransaction * trans,const sync_api::BaseNode & root,const PrefService::Preference * pref)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 
AssociateModels()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 
DisassociateModels()143 bool PreferenceModelAssociator::DisassociateModels() {
144   id_map_.clear();
145   id_map_inverse_.clear();
146   return true;
147 }
148 
SyncModelHasUserCreatedNodes(bool * has_nodes)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*
GetChromeNodeFromSyncId(int64 sync_id)174 PreferenceModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) {
175   return NULL;
176 }
177 
InitSyncNodeFromChromeId(const std::string & node_id,sync_api::BaseNode * sync_node)178 bool PreferenceModelAssociator::InitSyncNodeFromChromeId(
179     const std::string& node_id,
180     sync_api::BaseNode* sync_node) {
181   return false;
182 }
183 
GetSyncIdFromChromeId(const std::string & preference_name)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 
Associate(const PrefService::Preference * preference,int64 sync_id)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 
Disassociate(int64 sync_id)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 
GetSyncIdForTaggedNode(const std::string & tag,int64 * sync_id)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 
MergePreference(const PrefService::Preference & local_pref,const Value & server_value)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 
WritePreferenceToNode(const std::string & name,const Value & value,sync_api::WriteNode * node)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 
MergeListValues(const Value & from_value,const Value & to_value)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 
MergeDictionaryValues(const Value & from_value,const Value & to_value)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 
AfterUpdateOperations(const std::string & pref_name)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 
CryptoReadyIfNecessary()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