Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h" | 5 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h" |
| 6 | 6 |
| 7 #include "base/strings/stringprintf.h" | 7 #include "base/strings/stringprintf.h" |
| 8 #include "chrome/browser/extensions/api/storage/settings_sync_processor.h" | 8 #include "chrome/browser/extensions/api/storage/settings_sync_processor.h" |
| 9 #include "chrome/browser/extensions/api/storage/settings_sync_util.h" | 9 #include "chrome/browser/extensions/api/storage/settings_sync_util.h" |
| 10 #include "content/public/browser/browser_thread.h" | 10 #include "content/public/browser/browser_thread.h" |
| 11 #include "extensions/browser/api/storage/settings_namespace.h" | 11 #include "extensions/browser/api/storage/settings_namespace.h" |
| 12 #include "sync/api/sync_data.h" | 12 #include "sync/api/sync_data.h" |
| 13 #include "sync/protocol/extension_setting_specifics.pb.h" | 13 #include "sync/protocol/extension_setting_specifics.pb.h" |
| 14 | 14 |
| 15 using content::BrowserThread; | |
| 16 | |
| 15 namespace extensions { | 17 namespace extensions { |
| 16 | 18 |
| 17 using content::BrowserThread; | |
| 18 | |
| 19 SyncableSettingsStorage::SyncableSettingsStorage( | 19 SyncableSettingsStorage::SyncableSettingsStorage( |
| 20 const scoped_refptr<ObserverListThreadSafe<SettingsObserver> >& | 20 const scoped_refptr<ObserverListThreadSafe<SettingsObserver> >& |
| 21 observers, | 21 observers, |
| 22 const std::string& extension_id, | 22 const std::string& extension_id, |
| 23 ValueStore* delegate, | 23 ValueStore* delegate, |
| 24 syncer::ModelType sync_type, | 24 syncer::ModelType sync_type, |
| 25 const syncer::SyncableService::StartSyncFlare& flare) | 25 const syncer::SyncableService::StartSyncFlare& flare) |
| 26 : observers_(observers), | 26 : observers_(observers), |
| 27 extension_id_(extension_id), | 27 extension_id_(extension_id), |
| 28 delegate_(delegate), | 28 delegate_(delegate), |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 // Tell sync to try and start soon, because syncable changes to sync_type_ | 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 | 158 // have started happening. This will cause sync to call us back |
| 159 // asynchronously via StartSyncing(...) as soon as possible. | 159 // asynchronously via StartSyncing(...) as soon as possible. |
| 160 flare_.Run(sync_type_); | 160 flare_.Run(sync_type_); |
| 161 } | 161 } |
| 162 } | 162 } |
| 163 | 163 |
| 164 // Sync-related methods. | 164 // Sync-related methods. |
| 165 | 165 |
| 166 syncer::SyncError SyncableSettingsStorage::StartSyncing( | 166 syncer::SyncError SyncableSettingsStorage::StartSyncing( |
| 167 const base::DictionaryValue& sync_state, | 167 scoped_ptr<base::DictionaryValue> sync_state, |
| 168 scoped_ptr<SettingsSyncProcessor> sync_processor) { | 168 scoped_ptr<SettingsSyncProcessor> sync_processor) { |
| 169 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 169 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 170 DCHECK(sync_state); | |
| 170 DCHECK(!sync_processor_.get()); | 171 DCHECK(!sync_processor_.get()); |
| 171 | 172 |
| 172 sync_processor_ = sync_processor.Pass(); | 173 sync_processor_ = sync_processor.Pass(); |
| 173 sync_processor_->Init(sync_state); | 174 sync_processor_->Init(*sync_state); |
| 174 | 175 |
| 175 ReadResult maybe_settings = delegate_->Get(); | 176 ReadResult maybe_settings = delegate_->Get(); |
| 176 if (maybe_settings->HasError()) { | 177 if (maybe_settings->HasError()) { |
| 177 return syncer::SyncError( | 178 return syncer::SyncError( |
| 178 FROM_HERE, | 179 FROM_HERE, syncer::SyncError::DATATYPE_ERROR, |
| 179 syncer::SyncError::DATATYPE_ERROR, | |
| 180 base::StringPrintf("Failed to get settings: %s", | 180 base::StringPrintf("Failed to get settings: %s", |
| 181 maybe_settings->error().message.c_str()), | 181 maybe_settings->error().message.c_str()), |
| 182 sync_processor_->type()); | 182 sync_processor_->type()); |
| 183 } | 183 } |
| 184 | 184 |
| 185 const base::DictionaryValue& settings = maybe_settings->settings(); | 185 scoped_ptr<base::DictionaryValue> current_settings = |
| 186 return sync_state.empty() ? | 186 maybe_settings->PassSettings(); |
| 187 SendLocalSettingsToSync(settings) : | 187 return sync_state->empty() ? SendLocalSettingsToSync(current_settings.Pass()) |
| 188 OverwriteLocalSettingsWithSync(sync_state, settings); | 188 : OverwriteLocalSettingsWithSync( |
| 189 sync_state.Pass(), current_settings.Pass()); | |
| 189 } | 190 } |
| 190 | 191 |
| 191 syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync( | 192 syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync( |
| 192 const base::DictionaryValue& settings) { | 193 scoped_ptr<base::DictionaryValue> local_state) { |
| 193 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 194 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 194 | 195 |
| 196 if (local_state->empty()) | |
| 197 return syncer::SyncError(); | |
| 198 | |
| 199 // Transform the current settings into a list of sync changes. | |
| 195 ValueStoreChangeList changes; | 200 ValueStoreChangeList changes; |
| 196 for (base::DictionaryValue::Iterator i(settings); !i.IsAtEnd(); i.Advance()) { | 201 while (!local_state->empty()) { |
| 197 changes.push_back(ValueStoreChange(i.key(), NULL, i.value().DeepCopy())); | 202 // It's not possible to iterate over a DictionaryValue and modify it at the |
| 203 // same time, so hack around that restriction. | |
| 204 std::string key = base::DictionaryValue::Iterator(*local_state).key(); | |
| 205 scoped_ptr<base::Value> value; | |
| 206 local_state->RemoveWithoutPathExpansion(key, &value); | |
| 207 changes.push_back(ValueStoreChange(key, nullptr, value.release())); | |
| 198 } | 208 } |
| 199 | 209 |
| 200 if (changes.empty()) | |
| 201 return syncer::SyncError(); | |
| 202 | |
| 203 syncer::SyncError error = sync_processor_->SendChanges(changes); | 210 syncer::SyncError error = sync_processor_->SendChanges(changes); |
| 204 if (error.IsSet()) | 211 if (error.IsSet()) |
| 205 StopSyncing(); | 212 StopSyncing(); |
| 206 | |
| 207 return error; | 213 return error; |
| 208 } | 214 } |
| 209 | 215 |
| 210 syncer::SyncError SyncableSettingsStorage::OverwriteLocalSettingsWithSync( | 216 syncer::SyncError SyncableSettingsStorage::OverwriteLocalSettingsWithSync( |
| 211 const base::DictionaryValue& sync_state, | 217 scoped_ptr<base::DictionaryValue> sync_state, |
| 212 const base::DictionaryValue& settings) { | 218 scoped_ptr<base::DictionaryValue> local_state) { |
| 213 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 219 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 214 // Treat this as a list of changes to sync and use ProcessSyncChanges. | 220 // This is implemented by building up a list of sync changes then sending |
| 215 // This gives notifications etc for free. | 221 // those to ProcessSyncChanges. This generates events like onStorageChanged. |
| 216 scoped_ptr<base::DictionaryValue> new_sync_state(sync_state.DeepCopy()); | 222 scoped_ptr<SettingSyncDataList> changes(new SettingSyncDataList()); |
| 217 | 223 |
| 218 SettingSyncDataList changes; | 224 for (base::DictionaryValue::Iterator it(*local_state); !it.IsAtEnd(); |
| 219 for (base::DictionaryValue::Iterator it(settings); | 225 it.Advance()) { |
| 220 !it.IsAtEnd(); it.Advance()) { | |
| 221 scoped_ptr<base::Value> sync_value; | 226 scoped_ptr<base::Value> sync_value; |
| 222 if (new_sync_state->RemoveWithoutPathExpansion(it.key(), &sync_value)) { | 227 if (sync_state->RemoveWithoutPathExpansion(it.key(), &sync_value)) { |
| 223 if (sync_value->Equals(&it.value())) { | 228 if (sync_value->Equals(&it.value())) { |
| 224 // Sync and local values are the same, no changes to send. | 229 // Sync and local values are the same, no changes to send. |
|
Devlin
2015/05/15 21:59:07
nit: Empty if statement? Weird. Wanna take it ou
not at google - send to devlin
2015/05/15 23:28:30
I like it in there for documentation.
| |
| 225 } else { | 230 } else { |
| 226 // Sync value is different, update local setting with new value. | 231 // Sync value is different, update local setting with new value. |
| 227 changes.push_back( | 232 changes->push_back(SettingSyncData(syncer::SyncChange::ACTION_UPDATE, |
| 228 SettingSyncData( | 233 extension_id_, it.key(), |
| 229 syncer::SyncChange::ACTION_UPDATE, | 234 sync_value.Pass())); |
| 230 extension_id_, | |
| 231 it.key(), | |
| 232 sync_value.Pass())); | |
| 233 } | 235 } |
| 234 } else { | 236 } else { |
| 235 // Not synced, delete local setting. | 237 // Not synced, delete local setting. |
| 236 changes.push_back( | 238 changes->push_back(SettingSyncData( |
| 237 SettingSyncData( | 239 syncer::SyncChange::ACTION_DELETE, extension_id_, it.key(), |
| 238 syncer::SyncChange::ACTION_DELETE, | 240 scoped_ptr<base::Value>(new base::DictionaryValue()))); |
| 239 extension_id_, | |
| 240 it.key(), | |
| 241 scoped_ptr<base::Value>(new base::DictionaryValue()))); | |
| 242 } | 241 } |
| 243 } | 242 } |
| 244 | 243 |
| 245 // Add all new settings to local settings. | 244 // Add all new settings to local settings. |
| 246 while (!new_sync_state->empty()) { | 245 while (!sync_state->empty()) { |
| 247 base::DictionaryValue::Iterator first_entry(*new_sync_state); | 246 // It's not possible to iterate over a DictionaryValue and modify it at the |
| 248 std::string key = first_entry.key(); | 247 // same time, so hack around that restriction. |
| 248 std::string key = base::DictionaryValue::Iterator(*sync_state).key(); | |
| 249 scoped_ptr<base::Value> value; | 249 scoped_ptr<base::Value> value; |
| 250 CHECK(new_sync_state->RemoveWithoutPathExpansion(key, &value)); | 250 CHECK(sync_state->RemoveWithoutPathExpansion(key, &value)); |
| 251 changes.push_back( | 251 changes->push_back(SettingSyncData(syncer::SyncChange::ACTION_ADD, |
| 252 SettingSyncData( | 252 extension_id_, key, value.Pass())); |
| 253 syncer::SyncChange::ACTION_ADD, | |
| 254 extension_id_, | |
| 255 key, | |
| 256 value.Pass())); | |
| 257 } | 253 } |
| 258 | 254 |
| 259 if (changes.empty()) | 255 if (changes->empty()) |
| 260 return syncer::SyncError(); | 256 return syncer::SyncError(); |
| 261 | 257 return ProcessSyncChanges(changes.Pass()); |
| 262 return ProcessSyncChanges(changes); | |
| 263 } | 258 } |
| 264 | 259 |
| 265 void SyncableSettingsStorage::StopSyncing() { | 260 void SyncableSettingsStorage::StopSyncing() { |
| 266 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 261 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 267 sync_processor_.reset(); | 262 sync_processor_.reset(); |
| 268 } | 263 } |
| 269 | 264 |
| 270 syncer::SyncError SyncableSettingsStorage::ProcessSyncChanges( | 265 syncer::SyncError SyncableSettingsStorage::ProcessSyncChanges( |
| 271 const SettingSyncDataList& sync_changes) { | 266 scoped_ptr<SettingSyncDataList> sync_changes) { |
| 272 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 267 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 273 DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_; | 268 DCHECK(!sync_changes->empty()) << "No sync changes for " << extension_id_; |
| 274 | 269 |
| 275 if (!sync_processor_.get()) { | 270 if (!sync_processor_.get()) { |
| 276 return syncer::SyncError( | 271 return syncer::SyncError( |
| 277 FROM_HERE, | 272 FROM_HERE, |
| 278 syncer::SyncError::DATATYPE_ERROR, | 273 syncer::SyncError::DATATYPE_ERROR, |
| 279 std::string("Sync is inactive for ") + extension_id_, | 274 std::string("Sync is inactive for ") + extension_id_, |
| 280 syncer::UNSPECIFIED); | 275 syncer::UNSPECIFIED); |
| 281 } | 276 } |
| 282 | 277 |
| 283 std::vector<syncer::SyncError> errors; | 278 std::vector<syncer::SyncError> errors; |
| 284 ValueStoreChangeList changes; | 279 ValueStoreChangeList changes; |
| 285 | 280 |
| 286 for (SettingSyncDataList::const_iterator it = sync_changes.begin(); | 281 for (SettingSyncData& change : *sync_changes) { |
| 287 it != sync_changes.end(); ++it) { | 282 DCHECK_EQ(extension_id_, change.extension_id()); |
| 288 DCHECK_EQ(extension_id_, it->extension_id()); | 283 const std::string& key = change.key(); |
| 289 | 284 scoped_ptr<base::Value> change_value = change.PassValue(); |
| 290 const std::string& key = it->key(); | |
| 291 const base::Value& value = it->value(); | |
| 292 | 285 |
| 293 scoped_ptr<base::Value> current_value; | 286 scoped_ptr<base::Value> current_value; |
| 294 { | 287 { |
| 295 ReadResult maybe_settings = Get(it->key()); | 288 ReadResult maybe_settings = Get(key); |
| 296 if (maybe_settings->HasError()) { | 289 if (maybe_settings->HasError()) { |
| 297 errors.push_back(syncer::SyncError( | 290 errors.push_back(syncer::SyncError( |
| 298 FROM_HERE, | 291 FROM_HERE, |
| 299 syncer::SyncError::DATATYPE_ERROR, | 292 syncer::SyncError::DATATYPE_ERROR, |
| 300 base::StringPrintf("Error getting current sync state for %s/%s: %s", | 293 base::StringPrintf("Error getting current sync state for %s/%s: %s", |
| 301 extension_id_.c_str(), key.c_str(), | 294 extension_id_.c_str(), key.c_str(), |
| 302 maybe_settings->error().message.c_str()), | 295 maybe_settings->error().message.c_str()), |
| 303 sync_processor_->type())); | 296 sync_processor_->type())); |
| 304 continue; | 297 continue; |
| 305 } | 298 } |
| 306 maybe_settings->settings().RemoveWithoutPathExpansion(key, | 299 maybe_settings->settings().RemoveWithoutPathExpansion(key, |
| 307 ¤t_value); | 300 ¤t_value); |
| 308 } | 301 } |
| 309 | 302 |
| 310 syncer::SyncError error; | 303 syncer::SyncError error; |
| 311 | 304 |
| 312 switch (it->change_type()) { | 305 switch (change.change_type()) { |
| 313 case syncer::SyncChange::ACTION_ADD: | 306 case syncer::SyncChange::ACTION_ADD: |
| 314 if (!current_value.get()) { | 307 if (!current_value.get()) { |
| 315 error = OnSyncAdd(key, value.DeepCopy(), &changes); | 308 error = OnSyncAdd(key, change_value.release(), &changes); |
| 316 } else { | 309 } else { |
| 317 // Already a value; hopefully a local change has beaten sync in a | 310 // 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. | 311 // race and change's not a bug, so pretend change's an update. |
| 319 LOG(WARNING) << "Got add from sync for existing setting " << | 312 LOG(WARNING) << "Got add from sync for existing setting " << |
| 320 extension_id_ << "/" << key; | 313 extension_id_ << "/" << key; |
| 321 error = OnSyncUpdate( | 314 error = OnSyncUpdate(key, current_value.release(), |
| 322 key, current_value.release(), value.DeepCopy(), &changes); | 315 change_value.release(), &changes); |
| 323 } | 316 } |
| 324 break; | 317 break; |
| 325 | 318 |
| 326 case syncer::SyncChange::ACTION_UPDATE: | 319 case syncer::SyncChange::ACTION_UPDATE: |
| 327 if (current_value.get()) { | 320 if (current_value.get()) { |
| 328 error = OnSyncUpdate( | 321 error = OnSyncUpdate(key, current_value.release(), |
| 329 key, current_value.release(), value.DeepCopy(), &changes); | 322 change_value.release(), &changes); |
| 330 } else { | 323 } else { |
| 331 // Similarly, pretend it's an add. | 324 // Similarly, pretend change's an add. |
| 332 LOG(WARNING) << "Got update from sync for nonexistent setting" << | 325 LOG(WARNING) << "Got update from sync for nonexistent setting" << |
| 333 extension_id_ << "/" << key; | 326 extension_id_ << "/" << key; |
| 334 error = OnSyncAdd(key, value.DeepCopy(), &changes); | 327 error = OnSyncAdd(key, change_value.release(), &changes); |
| 335 } | 328 } |
| 336 break; | 329 break; |
| 337 | 330 |
| 338 case syncer::SyncChange::ACTION_DELETE: | 331 case syncer::SyncChange::ACTION_DELETE: |
| 339 if (current_value.get()) { | 332 if (current_value.get()) { |
| 340 error = OnSyncDelete(key, current_value.release(), &changes); | 333 error = OnSyncDelete(key, current_value.release(), &changes); |
| 341 } else { | 334 } else { |
| 342 // Similarly, ignore it. | 335 // Similarly, ignore change. |
| 343 LOG(WARNING) << "Got delete from sync for nonexistent setting " << | 336 LOG(WARNING) << "Got delete from sync for nonexistent setting " << |
| 344 extension_id_ << "/" << key; | 337 extension_id_ << "/" << key; |
| 345 } | 338 } |
| 346 break; | 339 break; |
| 347 | 340 |
| 348 default: | 341 default: |
| 349 NOTREACHED(); | 342 NOTREACHED(); |
| 350 } | 343 } |
| 351 | 344 |
| 352 if (error.IsSet()) { | 345 if (error.IsSet()) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 414 syncer::SyncError::DATATYPE_ERROR, | 407 syncer::SyncError::DATATYPE_ERROR, |
| 415 base::StringPrintf("Error pushing sync remove to local settings: %s", | 408 base::StringPrintf("Error pushing sync remove to local settings: %s", |
| 416 result->error().message.c_str()), | 409 result->error().message.c_str()), |
| 417 sync_processor_->type()); | 410 sync_processor_->type()); |
| 418 } | 411 } |
| 419 changes->push_back(ValueStoreChange(key, old_value, NULL)); | 412 changes->push_back(ValueStoreChange(key, old_value, NULL)); |
| 420 return syncer::SyncError(); | 413 return syncer::SyncError(); |
| 421 } | 414 } |
| 422 | 415 |
| 423 } // namespace extensions | 416 } // namespace extensions |
| OLD | NEW |