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 23namespace browser_sync { 24 25PreferenceChangeProcessor::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 37PreferenceChangeProcessor::~PreferenceChangeProcessor() { 38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 39} 40 41void 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 108void 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 179Value* 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 196void PreferenceChangeProcessor::StartImpl(Profile* profile) { 197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 198 pref_service_ = profile->GetPrefs(); 199 registrar_.Init(pref_service_); 200 StartObserving(); 201} 202 203void PreferenceChangeProcessor::StopImpl() { 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 205 StopObserving(); 206 pref_service_ = NULL; 207} 208 209 210void 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 219void 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