Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / api / storage / syncable_settings_storage.cc
blobd25bd170c3ef1e61dd9bbf92d4c92c86d927c456
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.
5 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h"
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"
15 namespace extensions {
17 using content::BrowserThread;
19 SyncableSettingsStorage::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);
34 SyncableSettingsStorage::~SyncableSettingsStorage() {
35 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
38 size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) {
39 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
40 return delegate_->GetBytesInUse(key);
43 size_t SyncableSettingsStorage::GetBytesInUse(
44 const std::vector<std::string>& keys) {
45 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
46 return delegate_->GetBytesInUse(keys);
49 size_t SyncableSettingsStorage::GetBytesInUse() {
50 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
51 return delegate_->GetBytesInUse();
54 ValueStore::ReadResult SyncableSettingsStorage::Get(
55 const std::string& key) {
56 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
57 return delegate_->Get(key);
60 ValueStore::ReadResult SyncableSettingsStorage::Get(
61 const std::vector<std::string>& keys) {
62 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
63 return delegate_->Get(keys);
66 ValueStore::ReadResult SyncableSettingsStorage::Get() {
67 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
68 return delegate_->Get();
71 ValueStore::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();
78 SyncResultIfEnabled(result);
79 return result.Pass();
82 ValueStore::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();
89 SyncResultIfEnabled(result);
90 return result.Pass();
93 ValueStore::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();
100 SyncResultIfEnabled(result);
101 return result.Pass();
104 ValueStore::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();
111 SyncResultIfEnabled(result);
112 return result.Pass();
115 ValueStore::WriteResult SyncableSettingsStorage::Clear() {
116 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
117 WriteResult result = delegate_->Clear();
118 if (result->HasError()) {
119 return result.Pass();
121 SyncResultIfEnabled(result);
122 return result.Pass();
125 bool 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();
136 bool 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);
147 void SyncableSettingsStorage::SyncResultIfEnabled(
148 const ValueStore::WriteResult& result) {
149 if (result->changes().empty())
150 return;
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_);
164 // Sync-related methods.
166 syncer::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());
172 sync_processor_ = sync_processor.Pass();
173 sync_processor_->Init(sync_state);
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());
185 const base::DictionaryValue& settings = maybe_settings->settings();
186 return sync_state.empty() ?
187 SendLocalSettingsToSync(settings) :
188 OverwriteLocalSettingsWithSync(sync_state, settings);
191 syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync(
192 const base::DictionaryValue& settings) {
193 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
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()));
200 if (changes.empty())
201 return syncer::SyncError();
203 syncer::SyncError error = sync_processor_->SendChanges(changes);
204 if (error.IsSet())
205 StopSyncing();
207 return error;
210 syncer::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());
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()));
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())));
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()));
259 if (changes.empty())
260 return syncer::SyncError();
262 return ProcessSyncChanges(changes);
265 void SyncableSettingsStorage::StopSyncing() {
266 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
267 sync_processor_.reset();
270 syncer::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_;
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);
283 std::vector<syncer::SyncError> errors;
284 ValueStoreChangeList changes;
286 for (SettingSyncDataList::const_iterator it = sync_changes.begin();
287 it != sync_changes.end(); ++it) {
288 DCHECK_EQ(extension_id_, it->extension_id());
290 const std::string& key = it->key();
291 const base::Value& value = it->value();
293 scoped_ptr<base::Value> current_value;
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;
306 maybe_settings->settings().RemoveWithoutPathExpansion(key,
307 &current_value);
310 syncer::SyncError error;
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);
324 break;
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);
336 break;
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;
346 break;
348 default:
349 NOTREACHED();
352 if (error.IsSet()) {
353 errors.push_back(error);
357 sync_processor_->NotifyChanges(changes);
359 observers_->Notify(FROM_HERE, &SettingsObserver::OnSettingsChanged,
360 extension_id_, settings_namespace::SYNC,
361 ValueStoreChange::ToJson(changes));
363 // TODO(kalman): Something sensible with multiple errors.
364 return errors.empty() ? syncer::SyncError() : errors[0];
367 syncer::SyncError SyncableSettingsStorage::OnSyncAdd(
368 const std::string& key,
369 base::Value* new_value,
370 ValueStoreChangeList* changes) {
371 DCHECK(new_value);
372 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value);
373 if (result->HasError()) {
374 return syncer::SyncError(
375 FROM_HERE,
376 syncer::SyncError::DATATYPE_ERROR,
377 base::StringPrintf("Error pushing sync add to local settings: %s",
378 result->error().message.c_str()),
379 sync_processor_->type());
381 changes->push_back(ValueStoreChange(key, NULL, new_value));
382 return syncer::SyncError();
385 syncer::SyncError SyncableSettingsStorage::OnSyncUpdate(
386 const std::string& key,
387 base::Value* old_value,
388 base::Value* new_value,
389 ValueStoreChangeList* changes) {
390 DCHECK(old_value);
391 DCHECK(new_value);
392 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value);
393 if (result->HasError()) {
394 return syncer::SyncError(
395 FROM_HERE,
396 syncer::SyncError::DATATYPE_ERROR,
397 base::StringPrintf("Error pushing sync update to local settings: %s",
398 result->error().message.c_str()),
399 sync_processor_->type());
401 changes->push_back(ValueStoreChange(key, old_value, new_value));
402 return syncer::SyncError();
405 syncer::SyncError SyncableSettingsStorage::OnSyncDelete(
406 const std::string& key,
407 base::Value* old_value,
408 ValueStoreChangeList* changes) {
409 DCHECK(old_value);
410 WriteResult result = delegate_->Remove(key);
411 if (result->HasError()) {
412 return syncer::SyncError(
413 FROM_HERE,
414 syncer::SyncError::DATATYPE_ERROR,
415 base::StringPrintf("Error pushing sync remove to local settings: %s",
416 result->error().message.c_str()),
417 sync_processor_->type());
419 changes->push_back(ValueStoreChange(key, old_value, NULL));
420 return syncer::SyncError();
423 } // namespace extensions