1// Copyright (c) 2012 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/extensions/api/storage/syncable_settings_storage.h" 6 7#include "base/strings/stringprintf.h" 8#include "chrome/browser/extensions/api/storage/settings_sync_processor.h" 9#include "chrome/browser/extensions/api/storage/settings_sync_util.h" 10#include "content/public/browser/browser_thread.h" 11#include "extensions/browser/api/storage/settings_namespace.h" 12#include "sync/api/sync_data.h" 13#include "sync/protocol/extension_setting_specifics.pb.h" 14 15namespace extensions { 16 17using content::BrowserThread; 18 19SyncableSettingsStorage::SyncableSettingsStorage( 20 const scoped_refptr<ObserverListThreadSafe<SettingsObserver> >& 21 observers, 22 const std::string& extension_id, 23 ValueStore* delegate, 24 syncer::ModelType sync_type, 25 const syncer::SyncableService::StartSyncFlare& flare) 26 : observers_(observers), 27 extension_id_(extension_id), 28 delegate_(delegate), 29 sync_type_(sync_type), 30 flare_(flare) { 31 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 32} 33 34SyncableSettingsStorage::~SyncableSettingsStorage() { 35 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 36} 37 38size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) { 39 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 40 return delegate_->GetBytesInUse(key); 41} 42 43size_t SyncableSettingsStorage::GetBytesInUse( 44 const std::vector<std::string>& keys) { 45 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 46 return delegate_->GetBytesInUse(keys); 47} 48 49size_t SyncableSettingsStorage::GetBytesInUse() { 50 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 51 return delegate_->GetBytesInUse(); 52} 53 54ValueStore::ReadResult SyncableSettingsStorage::Get( 55 const std::string& key) { 56 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 57 return delegate_->Get(key); 58} 59 60ValueStore::ReadResult SyncableSettingsStorage::Get( 61 const std::vector<std::string>& keys) { 62 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 63 return delegate_->Get(keys); 64} 65 66ValueStore::ReadResult SyncableSettingsStorage::Get() { 67 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 68 return delegate_->Get(); 69} 70 71ValueStore::WriteResult SyncableSettingsStorage::Set( 72 WriteOptions options, const std::string& key, const base::Value& value) { 73 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 74 WriteResult result = delegate_->Set(options, key, value); 75 if (result->HasError()) { 76 return result.Pass(); 77 } 78 SyncResultIfEnabled(result); 79 return result.Pass(); 80} 81 82ValueStore::WriteResult SyncableSettingsStorage::Set( 83 WriteOptions options, const base::DictionaryValue& values) { 84 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 85 WriteResult result = delegate_->Set(options, values); 86 if (result->HasError()) { 87 return result.Pass(); 88 } 89 SyncResultIfEnabled(result); 90 return result.Pass(); 91} 92 93ValueStore::WriteResult SyncableSettingsStorage::Remove( 94 const std::string& key) { 95 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 96 WriteResult result = delegate_->Remove(key); 97 if (result->HasError()) { 98 return result.Pass(); 99 } 100 SyncResultIfEnabled(result); 101 return result.Pass(); 102} 103 104ValueStore::WriteResult SyncableSettingsStorage::Remove( 105 const std::vector<std::string>& keys) { 106 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 107 WriteResult result = delegate_->Remove(keys); 108 if (result->HasError()) { 109 return result.Pass(); 110 } 111 SyncResultIfEnabled(result); 112 return result.Pass(); 113} 114 115ValueStore::WriteResult SyncableSettingsStorage::Clear() { 116 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 117 WriteResult result = delegate_->Clear(); 118 if (result->HasError()) { 119 return result.Pass(); 120 } 121 SyncResultIfEnabled(result); 122 return result.Pass(); 123} 124 125bool SyncableSettingsStorage::Restore() { 126 // If we're syncing, stop - we don't want to push the deletion of any data. 127 // At next startup, when we start up the sync service, we'll get back any 128 // data which was stored intact on Sync. 129 // TODO (rdevlin.cronin): Investigate if there's a way we can trigger 130 // MergeDataAndStartSyncing() to immediately get back any data we can, 131 // and continue syncing. 132 StopSyncing(); 133 return delegate_->Restore(); 134} 135 136bool SyncableSettingsStorage::RestoreKey(const std::string& key) { 137 // If we're syncing, stop - we don't want to push the deletion of any data. 138 // At next startup, when we start up the sync service, we'll get back any 139 // data which was stored intact on Sync. 140 // TODO (rdevlin.cronin): Investigate if there's a way we can trigger 141 // MergeDataAndStartSyncing() to immediately get back any data we can, 142 // and continue syncing. 143 StopSyncing(); 144 return delegate_->RestoreKey(key); 145} 146 147void SyncableSettingsStorage::SyncResultIfEnabled( 148 const ValueStore::WriteResult& result) { 149 if (result->changes().empty()) 150 return; 151 152 if (sync_processor_.get()) { 153 syncer::SyncError error = sync_processor_->SendChanges(result->changes()); 154 if (error.IsSet()) 155 StopSyncing(); 156 } else { 157 // Tell sync to try and start soon, because syncable changes to sync_type_ 158 // have started happening. This will cause sync to call us back 159 // asynchronously via StartSyncing(...) as soon as possible. 160 flare_.Run(sync_type_); 161 } 162} 163 164// Sync-related methods. 165 166syncer::SyncError SyncableSettingsStorage::StartSyncing( 167 const base::DictionaryValue& sync_state, 168 scoped_ptr<SettingsSyncProcessor> sync_processor) { 169 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 170 DCHECK(!sync_processor_.get()); 171 172 sync_processor_ = sync_processor.Pass(); 173 sync_processor_->Init(sync_state); 174 175 ReadResult maybe_settings = delegate_->Get(); 176 if (maybe_settings->HasError()) { 177 return syncer::SyncError( 178 FROM_HERE, 179 syncer::SyncError::DATATYPE_ERROR, 180 base::StringPrintf("Failed to get settings: %s", 181 maybe_settings->error().message.c_str()), 182 sync_processor_->type()); 183 } 184 185 const base::DictionaryValue& settings = maybe_settings->settings(); 186 return sync_state.empty() ? 187 SendLocalSettingsToSync(settings) : 188 OverwriteLocalSettingsWithSync(sync_state, settings); 189} 190 191syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync( 192 const base::DictionaryValue& settings) { 193 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 194 195 ValueStoreChangeList changes; 196 for (base::DictionaryValue::Iterator i(settings); !i.IsAtEnd(); i.Advance()) { 197 changes.push_back(ValueStoreChange(i.key(), NULL, i.value().DeepCopy())); 198 } 199 200 if (changes.empty()) 201 return syncer::SyncError(); 202 203 syncer::SyncError error = sync_processor_->SendChanges(changes); 204 if (error.IsSet()) 205 StopSyncing(); 206 207 return error; 208} 209 210syncer::SyncError SyncableSettingsStorage::OverwriteLocalSettingsWithSync( 211 const base::DictionaryValue& sync_state, 212 const base::DictionaryValue& settings) { 213 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 214 // Treat this as a list of changes to sync and use ProcessSyncChanges. 215 // This gives notifications etc for free. 216 scoped_ptr<base::DictionaryValue> new_sync_state(sync_state.DeepCopy()); 217 218 SettingSyncDataList changes; 219 for (base::DictionaryValue::Iterator it(settings); 220 !it.IsAtEnd(); it.Advance()) { 221 scoped_ptr<base::Value> sync_value; 222 if (new_sync_state->RemoveWithoutPathExpansion(it.key(), &sync_value)) { 223 if (sync_value->Equals(&it.value())) { 224 // Sync and local values are the same, no changes to send. 225 } else { 226 // Sync value is different, update local setting with new value. 227 changes.push_back( 228 SettingSyncData( 229 syncer::SyncChange::ACTION_UPDATE, 230 extension_id_, 231 it.key(), 232 sync_value.Pass())); 233 } 234 } else { 235 // Not synced, delete local setting. 236 changes.push_back( 237 SettingSyncData( 238 syncer::SyncChange::ACTION_DELETE, 239 extension_id_, 240 it.key(), 241 scoped_ptr<base::Value>(new base::DictionaryValue()))); 242 } 243 } 244 245 // Add all new settings to local settings. 246 while (!new_sync_state->empty()) { 247 base::DictionaryValue::Iterator first_entry(*new_sync_state); 248 std::string key = first_entry.key(); 249 scoped_ptr<base::Value> value; 250 CHECK(new_sync_state->RemoveWithoutPathExpansion(key, &value)); 251 changes.push_back( 252 SettingSyncData( 253 syncer::SyncChange::ACTION_ADD, 254 extension_id_, 255 key, 256 value.Pass())); 257 } 258 259 if (changes.empty()) 260 return syncer::SyncError(); 261 262 return ProcessSyncChanges(changes); 263} 264 265void SyncableSettingsStorage::StopSyncing() { 266 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 267 sync_processor_.reset(); 268} 269 270syncer::SyncError SyncableSettingsStorage::ProcessSyncChanges( 271 const SettingSyncDataList& sync_changes) { 272 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 273 DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_; 274 275 if (!sync_processor_.get()) { 276 return syncer::SyncError( 277 FROM_HERE, 278 syncer::SyncError::DATATYPE_ERROR, 279 std::string("Sync is inactive for ") + extension_id_, 280 syncer::UNSPECIFIED); 281 } 282 283 std::vector<syncer::SyncError> errors; 284 ValueStoreChangeList changes; 285 286 for (SettingSyncDataList::const_iterator it = sync_changes.begin(); 287 it != sync_changes.end(); ++it) { 288 DCHECK_EQ(extension_id_, it->extension_id()); 289 290 const std::string& key = it->key(); 291 const base::Value& value = it->value(); 292 293 scoped_ptr<base::Value> current_value; 294 { 295 ReadResult maybe_settings = Get(it->key()); 296 if (maybe_settings->HasError()) { 297 errors.push_back(syncer::SyncError( 298 FROM_HERE, 299 syncer::SyncError::DATATYPE_ERROR, 300 base::StringPrintf("Error getting current sync state for %s/%s: %s", 301 extension_id_.c_str(), key.c_str(), 302 maybe_settings->error().message.c_str()), 303 sync_processor_->type())); 304 continue; 305 } 306 maybe_settings->settings().RemoveWithoutPathExpansion(key, 307 ¤t_value); 308 } 309 310 syncer::SyncError error; 311 312 switch (it->change_type()) { 313 case syncer::SyncChange::ACTION_ADD: 314 if (!current_value.get()) { 315 error = OnSyncAdd(key, value.DeepCopy(), &changes); 316 } else { 317 // Already a value; hopefully a local change has beaten sync in a 318 // race and it's not a bug, so pretend it's an update. 319 LOG(WARNING) << "Got add from sync for existing setting " << 320 extension_id_ << "/" << key; 321 error = OnSyncUpdate( 322 key, current_value.release(), value.DeepCopy(), &changes); 323 } 324 break; 325 326 case syncer::SyncChange::ACTION_UPDATE: 327 if (current_value.get()) { 328 error = OnSyncUpdate( 329 key, current_value.release(), value.DeepCopy(), &changes); 330 } else { 331 // Similarly, pretend it's an add. 332 LOG(WARNING) << "Got update from sync for nonexistent setting" << 333 extension_id_ << "/" << key; 334 error = OnSyncAdd(key, value.DeepCopy(), &changes); 335 } 336 break; 337 338 case syncer::SyncChange::ACTION_DELETE: 339 if (current_value.get()) { 340 error = OnSyncDelete(key, current_value.release(), &changes); 341 } else { 342 // Similarly, ignore it. 343 LOG(WARNING) << "Got delete from sync for nonexistent setting " << 344 extension_id_ << "/" << key; 345 } 346 break; 347 348 default: 349 NOTREACHED(); 350 } 351 352 if (error.IsSet()) { 353 errors.push_back(error); 354 } 355 } 356 357 sync_processor_->NotifyChanges(changes); 358 359 observers_->Notify( 360 &SettingsObserver::OnSettingsChanged, 361 extension_id_, 362 settings_namespace::SYNC, 363 ValueStoreChange::ToJson(changes)); 364 365 // TODO(kalman): Something sensible with multiple errors. 366 return errors.empty() ? syncer::SyncError() : errors[0]; 367} 368 369syncer::SyncError SyncableSettingsStorage::OnSyncAdd( 370 const std::string& key, 371 base::Value* new_value, 372 ValueStoreChangeList* changes) { 373 DCHECK(new_value); 374 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); 375 if (result->HasError()) { 376 return syncer::SyncError( 377 FROM_HERE, 378 syncer::SyncError::DATATYPE_ERROR, 379 base::StringPrintf("Error pushing sync add to local settings: %s", 380 result->error().message.c_str()), 381 sync_processor_->type()); 382 } 383 changes->push_back(ValueStoreChange(key, NULL, new_value)); 384 return syncer::SyncError(); 385} 386 387syncer::SyncError SyncableSettingsStorage::OnSyncUpdate( 388 const std::string& key, 389 base::Value* old_value, 390 base::Value* new_value, 391 ValueStoreChangeList* changes) { 392 DCHECK(old_value); 393 DCHECK(new_value); 394 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); 395 if (result->HasError()) { 396 return syncer::SyncError( 397 FROM_HERE, 398 syncer::SyncError::DATATYPE_ERROR, 399 base::StringPrintf("Error pushing sync update to local settings: %s", 400 result->error().message.c_str()), 401 sync_processor_->type()); 402 } 403 changes->push_back(ValueStoreChange(key, old_value, new_value)); 404 return syncer::SyncError(); 405} 406 407syncer::SyncError SyncableSettingsStorage::OnSyncDelete( 408 const std::string& key, 409 base::Value* old_value, 410 ValueStoreChangeList* changes) { 411 DCHECK(old_value); 412 WriteResult result = delegate_->Remove(key); 413 if (result->HasError()) { 414 return syncer::SyncError( 415 FROM_HERE, 416 syncer::SyncError::DATATYPE_ERROR, 417 base::StringPrintf("Error pushing sync remove to local settings: %s", 418 result->error().message.c_str()), 419 sync_processor_->type()); 420 } 421 changes->push_back(ValueStoreChange(key, old_value, NULL)); 422 return syncer::SyncError(); 423} 424 425} // namespace extensions 426