Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / storage / syncable_settings_storage.cc
blobb30292e7eb6c0bc0eff06fda47508b6f45c4f530
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>>&
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 scoped_ptr<base::DictionaryValue> sync_state,
168 scoped_ptr<SettingsSyncProcessor> sync_processor) {
169 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
170 DCHECK(sync_state);
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);
211 if (error.IsSet())
212 StopSyncing();
213 return error;
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();
225 it.Advance()) {
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.
230 } else {
231 // Sync value is different, update local setting with new value.
232 changes->push_back(
233 new SettingSyncData(syncer::SyncChange::ACTION_UPDATE,
234 extension_id_, it.key(), sync_value.Pass()));
236 } else {
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(
272 FROM_HERE,
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(
292 FROM_HERE,
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()));
298 continue;
300 maybe_settings->settings().RemoveWithoutPathExpansion(key,
301 &current_value);
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);
310 } else {
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);
318 break;
320 case syncer::SyncChange::ACTION_UPDATE:
321 if (current_value.get()) {
322 error = OnSyncUpdate(key, current_value.release(),
323 change_value.release(), &changes);
324 } else {
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);
330 break;
332 case syncer::SyncChange::ACTION_DELETE:
333 if (current_value.get()) {
334 error = OnSyncDelete(key, current_value.release(), &changes);
335 } else {
336 // Similarly, ignore change.
337 LOG(WARNING) << "Got delete from sync for nonexistent setting " <<
338 extension_id_ << "/" << key;
340 break;
342 default:
343 NOTREACHED();
346 if (error.IsSet()) {
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) {
365 DCHECK(new_value);
366 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value);
367 if (result->HasError()) {
368 return syncer::SyncError(
369 FROM_HERE,
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) {
384 DCHECK(old_value);
385 DCHECK(new_value);
386 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value);
387 if (result->HasError()) {
388 return syncer::SyncError(
389 FROM_HERE,
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) {
403 DCHECK(old_value);
404 WriteResult result = delegate_->Remove(key);
405 if (result->HasError()) {
406 return syncer::SyncError(
407 FROM_HERE,
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