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 using content::BrowserThread
;
17 namespace extensions
{
19 SyncableSettingsStorage::SyncableSettingsStorage(
20 const scoped_refptr
<base::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 scoped_ptr
<base::DictionaryValue
> sync_state
,
168 scoped_ptr
<SettingsSyncProcessor
> sync_processor
) {
169 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
171 DCHECK(!sync_processor_
.get());
173 sync_processor_
= sync_processor
.Pass();
174 sync_processor_
->Init(*sync_state
);
176 ReadResult maybe_settings
= delegate_
->Get();
177 if (maybe_settings
->HasError()) {
178 return syncer::SyncError(
179 FROM_HERE
, syncer::SyncError::DATATYPE_ERROR
,
180 base::StringPrintf("Failed to get settings: %s",
181 maybe_settings
->error().message
.c_str()),
182 sync_processor_
->type());
185 scoped_ptr
<base::DictionaryValue
> current_settings
=
186 maybe_settings
->PassSettings();
187 return sync_state
->empty() ? SendLocalSettingsToSync(current_settings
.Pass())
188 : OverwriteLocalSettingsWithSync(
189 sync_state
.Pass(), current_settings
.Pass());
192 syncer::SyncError
SyncableSettingsStorage::SendLocalSettingsToSync(
193 scoped_ptr
<base::DictionaryValue
> local_state
) {
194 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
196 if (local_state
->empty())
197 return syncer::SyncError();
199 // Transform the current settings into a list of sync changes.
200 ValueStoreChangeList changes
;
201 while (!local_state
->empty()) {
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()));
210 syncer::SyncError error
= sync_processor_
->SendChanges(changes
);
216 syncer::SyncError
SyncableSettingsStorage::OverwriteLocalSettingsWithSync(
217 scoped_ptr
<base::DictionaryValue
> sync_state
,
218 scoped_ptr
<base::DictionaryValue
> local_state
) {
219 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
220 // This is implemented by building up a list of sync changes then sending
221 // those to ProcessSyncChanges. This generates events like onStorageChanged.
222 scoped_ptr
<SettingSyncDataList
> changes(new SettingSyncDataList());
224 for (base::DictionaryValue::Iterator
it(*local_state
); !it
.IsAtEnd();
226 scoped_ptr
<base::Value
> sync_value
;
227 if (sync_state
->RemoveWithoutPathExpansion(it
.key(), &sync_value
)) {
228 if (sync_value
->Equals(&it
.value())) {
229 // Sync and local values are the same, no changes to send.
231 // Sync value is different, update local setting with new value.
233 new SettingSyncData(syncer::SyncChange::ACTION_UPDATE
,
234 extension_id_
, it
.key(), sync_value
.Pass()));
237 // Not synced, delete local setting.
238 changes
->push_back(new SettingSyncData(
239 syncer::SyncChange::ACTION_DELETE
, extension_id_
, it
.key(),
240 scoped_ptr
<base::Value
>(new base::DictionaryValue())));
244 // Add all new settings to local settings.
245 while (!sync_state
->empty()) {
246 // It's not possible to iterate over a DictionaryValue and modify it at the
247 // same time, so hack around that restriction.
248 std::string key
= base::DictionaryValue::Iterator(*sync_state
).key();
249 scoped_ptr
<base::Value
> value
;
250 CHECK(sync_state
->RemoveWithoutPathExpansion(key
, &value
));
251 changes
->push_back(new SettingSyncData(syncer::SyncChange::ACTION_ADD
,
252 extension_id_
, key
, value
.Pass()));
255 if (changes
->empty())
256 return syncer::SyncError();
257 return ProcessSyncChanges(changes
.Pass());
260 void SyncableSettingsStorage::StopSyncing() {
261 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
262 sync_processor_
.reset();
265 syncer::SyncError
SyncableSettingsStorage::ProcessSyncChanges(
266 scoped_ptr
<SettingSyncDataList
> sync_changes
) {
267 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
268 DCHECK(!sync_changes
->empty()) << "No sync changes for " << extension_id_
;
270 if (!sync_processor_
.get()) {
271 return syncer::SyncError(
273 syncer::SyncError::DATATYPE_ERROR
,
274 std::string("Sync is inactive for ") + extension_id_
,
275 syncer::UNSPECIFIED
);
278 std::vector
<syncer::SyncError
> errors
;
279 ValueStoreChangeList changes
;
281 for (SettingSyncDataList::iterator it
= sync_changes
->begin();
282 it
!= sync_changes
->end(); ++it
) {
283 DCHECK_EQ(extension_id_
, (*it
)->extension_id());
284 const std::string
& key
= (*it
)->key();
285 scoped_ptr
<base::Value
> change_value
= (*it
)->PassValue();
287 scoped_ptr
<base::Value
> current_value
;
289 ReadResult maybe_settings
= Get(key
);
290 if (maybe_settings
->HasError()) {
291 errors
.push_back(syncer::SyncError(
293 syncer::SyncError::DATATYPE_ERROR
,
294 base::StringPrintf("Error getting current sync state for %s/%s: %s",
295 extension_id_
.c_str(), key
.c_str(),
296 maybe_settings
->error().message
.c_str()),
297 sync_processor_
->type()));
300 maybe_settings
->settings().RemoveWithoutPathExpansion(key
,
304 syncer::SyncError error
;
306 switch ((*it
)->change_type()) {
307 case syncer::SyncChange::ACTION_ADD
:
308 if (!current_value
.get()) {
309 error
= OnSyncAdd(key
, change_value
.release(), &changes
);
311 // Already a value; hopefully a local change has beaten sync in a
312 // race and change's not a bug, so pretend change's an update.
313 LOG(WARNING
) << "Got add from sync for existing setting " <<
314 extension_id_
<< "/" << key
;
315 error
= OnSyncUpdate(key
, current_value
.release(),
316 change_value
.release(), &changes
);
320 case syncer::SyncChange::ACTION_UPDATE
:
321 if (current_value
.get()) {
322 error
= OnSyncUpdate(key
, current_value
.release(),
323 change_value
.release(), &changes
);
325 // Similarly, pretend change's an add.
326 LOG(WARNING
) << "Got update from sync for nonexistent setting" <<
327 extension_id_
<< "/" << key
;
328 error
= OnSyncAdd(key
, change_value
.release(), &changes
);
332 case syncer::SyncChange::ACTION_DELETE
:
333 if (current_value
.get()) {
334 error
= OnSyncDelete(key
, current_value
.release(), &changes
);
336 // Similarly, ignore change.
337 LOG(WARNING
) << "Got delete from sync for nonexistent setting " <<
338 extension_id_
<< "/" << key
;
347 errors
.push_back(error
);
351 sync_processor_
->NotifyChanges(changes
);
353 observers_
->Notify(FROM_HERE
, &SettingsObserver::OnSettingsChanged
,
354 extension_id_
, settings_namespace::SYNC
,
355 ValueStoreChange::ToJson(changes
));
357 // TODO(kalman): Something sensible with multiple errors.
358 return errors
.empty() ? syncer::SyncError() : errors
[0];
361 syncer::SyncError
SyncableSettingsStorage::OnSyncAdd(
362 const std::string
& key
,
363 base::Value
* new_value
,
364 ValueStoreChangeList
* changes
) {
366 WriteResult result
= delegate_
->Set(IGNORE_QUOTA
, key
, *new_value
);
367 if (result
->HasError()) {
368 return syncer::SyncError(
370 syncer::SyncError::DATATYPE_ERROR
,
371 base::StringPrintf("Error pushing sync add to local settings: %s",
372 result
->error().message
.c_str()),
373 sync_processor_
->type());
375 changes
->push_back(ValueStoreChange(key
, NULL
, new_value
));
376 return syncer::SyncError();
379 syncer::SyncError
SyncableSettingsStorage::OnSyncUpdate(
380 const std::string
& key
,
381 base::Value
* old_value
,
382 base::Value
* new_value
,
383 ValueStoreChangeList
* changes
) {
386 WriteResult result
= delegate_
->Set(IGNORE_QUOTA
, key
, *new_value
);
387 if (result
->HasError()) {
388 return syncer::SyncError(
390 syncer::SyncError::DATATYPE_ERROR
,
391 base::StringPrintf("Error pushing sync update to local settings: %s",
392 result
->error().message
.c_str()),
393 sync_processor_
->type());
395 changes
->push_back(ValueStoreChange(key
, old_value
, new_value
));
396 return syncer::SyncError();
399 syncer::SyncError
SyncableSettingsStorage::OnSyncDelete(
400 const std::string
& key
,
401 base::Value
* old_value
,
402 ValueStoreChangeList
* changes
) {
404 WriteResult result
= delegate_
->Remove(key
);
405 if (result
->HasError()) {
406 return syncer::SyncError(
408 syncer::SyncError::DATATYPE_ERROR
,
409 base::StringPrintf("Error pushing sync remove to local settings: %s",
410 result
->error().message
.c_str()),
411 sync_processor_
->type());
413 changes
->push_back(ValueStoreChange(key
, old_value
, NULL
));
414 return syncer::SyncError();
417 } // namespace extensions