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
> >&
22 const std::string
& extension_id
,
24 syncer::ModelType sync_type
,
25 const syncer::SyncableService::StartSyncFlare
& flare
)
26 : observers_(observers
),
27 extension_id_(extension_id
),
29 sync_type_(sync_type
),
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()) {
78 SyncResultIfEnabled(result
);
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()) {
89 SyncResultIfEnabled(result
);
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()) {
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.
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.
144 return delegate_
->RestoreKey(key
);
147 void SyncableSettingsStorage::SyncResultIfEnabled(
148 const ValueStore::WriteResult
& result
) {
149 if (result
->changes().empty())
152 if (sync_processor_
.get()) {
153 syncer::SyncError error
= sync_processor_
->SendChanges(result
->changes());
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(
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()));
201 return syncer::SyncError();
203 syncer::SyncError error
= sync_processor_
->SendChanges(changes
);
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.
226 // Sync value is different, update local setting with new value.
229 syncer::SyncChange::ACTION_UPDATE
,
235 // Not synced, delete local setting.
238 syncer::SyncChange::ACTION_DELETE
,
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
));
253 syncer::SyncChange::ACTION_ADD
,
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(
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(
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()));
306 maybe_settings
->settings().RemoveWithoutPathExpansion(key
,
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
);
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
);
326 case syncer::SyncChange::ACTION_UPDATE
:
327 if (current_value
.get()) {
328 error
= OnSyncUpdate(
329 key
, current_value
.release(), value
.DeepCopy(), &changes
);
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
);
338 case syncer::SyncChange::ACTION_DELETE
:
339 if (current_value
.get()) {
340 error
= OnSyncDelete(key
, current_value
.release(), &changes
);
342 // Similarly, ignore it.
343 LOG(WARNING
) << "Got delete from sync for nonexistent setting " <<
344 extension_id_
<< "/" << key
;
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
) {
372 WriteResult result
= delegate_
->Set(IGNORE_QUOTA
, key
, *new_value
);
373 if (result
->HasError()) {
374 return syncer::SyncError(
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
) {
392 WriteResult result
= delegate_
->Set(IGNORE_QUOTA
, key
, *new_value
);
393 if (result
->HasError()) {
394 return syncer::SyncError(
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
) {
410 WriteResult result
= delegate_
->Remove(key
);
411 if (result
->HasError()) {
412 return syncer::SyncError(
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