• 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_change_processor.h"
6 
7 #include <set>
8 #include <string>
9 
10 #include "base/auto_reset.h"
11 #include "base/json/json_reader.h"
12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/sync/glue/preference_model_associator.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_details.h"
21 #include "content/common/notification_source.h"
22 
23 namespace browser_sync {
24 
PreferenceChangeProcessor(PreferenceModelAssociator * model_associator,UnrecoverableErrorHandler * error_handler)25 PreferenceChangeProcessor::PreferenceChangeProcessor(
26     PreferenceModelAssociator* model_associator,
27     UnrecoverableErrorHandler* error_handler)
28     : ChangeProcessor(error_handler),
29       pref_service_(NULL),
30       model_associator_(model_associator),
31       processing_pref_change_(false) {
32   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
33   DCHECK(model_associator);
34   DCHECK(error_handler);
35 }
36 
~PreferenceChangeProcessor()37 PreferenceChangeProcessor::~PreferenceChangeProcessor() {
38   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
39 }
40 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)41 void PreferenceChangeProcessor::Observe(NotificationType type,
42                                         const NotificationSource& source,
43                                         const NotificationDetails& details) {
44   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
45   DCHECK(running());
46   DCHECK(NotificationType::PREF_CHANGED == type);
47   DCHECK_EQ(pref_service_, Source<PrefService>(source).ptr());
48 
49   // Avoid recursion.
50   if (processing_pref_change_)
51     return;
52 
53   AutoReset<bool> guard(&processing_pref_change_, true);
54   std::string* name = Details<std::string>(details).ptr();
55   const PrefService::Preference* preference =
56       pref_service_->FindPreference((*name).c_str());
57   DCHECK(preference);
58   int64 sync_id = model_associator_->GetSyncIdFromChromeId(*name);
59   bool user_modifiable = preference->IsUserModifiable();
60   if (!user_modifiable) {
61     // We do not track preferences the user cannot change.
62     model_associator_->Disassociate(sync_id);
63     return;
64   }
65 
66   sync_api::WriteTransaction trans(share_handle());
67   sync_api::WriteNode node(&trans);
68 
69   // Since we don't create sync nodes for preferences that are not under control
70   // of the user or still have their default value, this changed preference may
71   // not have a sync node yet. If so, we create a node. Similarly, a preference
72   // may become user-modifiable (e.g. due to laxer policy configuration), in
73   // which case we also need to create a sync node and associate it.
74   if (sync_api::kInvalidId == sync_id) {
75     sync_api::ReadNode root(&trans);
76     if (!root.InitByTagLookup(browser_sync::kPreferencesTag)) {
77       error_handler()->OnUnrecoverableError(FROM_HERE, "Can't find root.");
78       return;
79     }
80 
81     // InitPrefNodeAndAssociate takes care of writing the value to the sync
82     // node if appropriate.
83     if (!model_associator_->InitPrefNodeAndAssociate(&trans,
84                                                      root,
85                                                      preference)) {
86       error_handler()->OnUnrecoverableError(FROM_HERE,
87                                             "Can't create sync node.");
88     }
89     return;
90   }
91 
92   if (!node.InitByIdLookup(sync_id)) {
93     error_handler()->OnUnrecoverableError(FROM_HERE,
94                                           "Preference node lookup failed.");
95     return;
96   }
97 
98   if (!PreferenceModelAssociator::WritePreferenceToNode(
99           preference->name(),
100           *preference->GetValue(),
101           &node)) {
102     error_handler()->OnUnrecoverableError(FROM_HERE,
103                                           "Failed to update preference node.");
104     return;
105   }
106 }
107 
ApplyChangesFromSyncModel(const sync_api::BaseTransaction * trans,const sync_api::SyncManager::ChangeRecord * changes,int change_count)108 void PreferenceChangeProcessor::ApplyChangesFromSyncModel(
109     const sync_api::BaseTransaction* trans,
110     const sync_api::SyncManager::ChangeRecord* changes,
111     int change_count) {
112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113   if (!running())
114     return;
115   StopObserving();
116 
117   for (int i = 0; i < change_count; ++i) {
118     sync_api::ReadNode node(trans);
119     // TODO(ncarter): Can't look up the name for deletions: lookup of
120     // deleted items fails at the syncapi layer.  However, the node should
121     // generally still exist in the syncable database; we just need to
122     // plumb the syncapi so that it succeeds.
123     if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
124         changes[i].action) {
125       // Until the above is fixed, we have no choice but to ignore deletions.
126       LOG(ERROR) << "No way to handle pref deletion";
127       continue;
128     }
129 
130     if (!node.InitByIdLookup(changes[i].id)) {
131       error_handler()->OnUnrecoverableError(FROM_HERE,
132                                             "Preference node lookup failed.");
133       return;
134     }
135     DCHECK(syncable::PREFERENCES == node.GetModelType());
136 
137     std::string name;
138     scoped_ptr<Value> value(ReadPreference(&node, &name));
139     // Skip values we can't deserialize.
140     if (!value.get())
141       continue;
142 
143     // It is possible that we may receive a change to a preference we
144     // do not want to sync.  For example if the user is syncing a Mac
145     // client and a Windows client, the Windows client does not
146     // support kConfirmToQuitEnabled.  Ignore updates from these
147     // preferences.
148     const char* pref_name = name.c_str();
149     if (model_associator_->synced_preferences().count(pref_name) == 0)
150       continue;
151 
152     // Don't try to overwrite preferences not controllable by the user.
153     const PrefService::Preference* pref =
154         pref_service_->FindPreference(pref_name);
155     DCHECK(pref);
156     if (!pref->IsUserModifiable())
157       continue;
158 
159     if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
160         changes[i].action) {
161       pref_service_->ClearPref(pref_name);
162     } else {
163       pref_service_->Set(pref_name, *value);
164 
165       // If this is a newly added node, associate.
166       if (sync_api::SyncManager::ChangeRecord::ACTION_ADD ==
167           changes[i].action) {
168         const PrefService::Preference* preference =
169             pref_service_->FindPreference(name.c_str());
170         model_associator_->Associate(preference, changes[i].id);
171       }
172 
173       model_associator_->AfterUpdateOperations(name);
174     }
175   }
176   StartObserving();
177 }
178 
ReadPreference(sync_api::ReadNode * node,std::string * name)179 Value* PreferenceChangeProcessor::ReadPreference(
180     sync_api::ReadNode* node,
181     std::string* name) {
182   const sync_pb::PreferenceSpecifics& preference(
183       node->GetPreferenceSpecifics());
184   base::JSONReader reader;
185   scoped_ptr<Value> value(reader.JsonToValue(preference.value(), false, false));
186   if (!value.get()) {
187     std::string err = "Failed to deserialize preference value: " +
188         reader.GetErrorMessage();
189     error_handler()->OnUnrecoverableError(FROM_HERE, err);
190     return NULL;
191   }
192   *name = preference.name();
193   return value.release();
194 }
195 
StartImpl(Profile * profile)196 void PreferenceChangeProcessor::StartImpl(Profile* profile) {
197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198   pref_service_ = profile->GetPrefs();
199   registrar_.Init(pref_service_);
200   StartObserving();
201 }
202 
StopImpl()203 void PreferenceChangeProcessor::StopImpl() {
204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205   StopObserving();
206   pref_service_ = NULL;
207 }
208 
209 
StartObserving()210 void PreferenceChangeProcessor::StartObserving() {
211   DCHECK(pref_service_);
212   for (std::set<std::string>::const_iterator it =
213       model_associator_->synced_preferences().begin();
214       it != model_associator_->synced_preferences().end(); ++it) {
215     registrar_.Add((*it).c_str(), this);
216   }
217 }
218 
StopObserving()219 void PreferenceChangeProcessor::StopObserving() {
220   DCHECK(pref_service_);
221   for (std::set<std::string>::const_iterator it =
222       model_associator_->synced_preferences().begin();
223       it != model_associator_->synced_preferences().end(); ++it) {
224     registrar_.Remove((*it).c_str(), this);
225   }
226 }
227 
228 }  // namespace browser_sync
229