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.
4
5 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h"
6
7 #include <utility>
8
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/extensions/api/storage/settings_sync_processor.h"
11 #include "chrome/browser/extensions/api/storage/settings_sync_util.h"
12 #include "components/sync/model/model_error.h"
13 #include "components/sync/model/sync_data.h"
14 #include "components/sync/model/sync_error.h"
15 #include "components/sync/protocol/extension_setting_specifics.pb.h"
16 #include "extensions/browser/api/storage/backend_task_runner.h"
17 #include "extensions/browser/api/storage/settings_namespace.h"
18
19 namespace extensions {
20
SyncableSettingsStorage(scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>> observers,const std::string & extension_id,ValueStore * delegate,syncer::ModelType sync_type,const syncer::SyncableService::StartSyncFlare & flare)21 SyncableSettingsStorage::SyncableSettingsStorage(
22 scoped_refptr<base::ObserverListThreadSafe<SettingsObserver>> observers,
23 const std::string& extension_id,
24 ValueStore* delegate,
25 syncer::ModelType sync_type,
26 const syncer::SyncableService::StartSyncFlare& flare)
27 : observers_(std::move(observers)),
28 extension_id_(extension_id),
29 delegate_(delegate),
30 sync_type_(sync_type),
31 flare_(flare) {
32 DCHECK(IsOnBackendSequence());
33 }
34
~SyncableSettingsStorage()35 SyncableSettingsStorage::~SyncableSettingsStorage() {
36 DCHECK(IsOnBackendSequence());
37 }
38
GetBytesInUse(const std::string & key)39 size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) {
40 DCHECK(IsOnBackendSequence());
41 return delegate_->GetBytesInUse(key);
42 }
43
GetBytesInUse(const std::vector<std::string> & keys)44 size_t SyncableSettingsStorage::GetBytesInUse(
45 const std::vector<std::string>& keys) {
46 DCHECK(IsOnBackendSequence());
47 return delegate_->GetBytesInUse(keys);
48 }
49
GetBytesInUse()50 size_t SyncableSettingsStorage::GetBytesInUse() {
51 DCHECK(IsOnBackendSequence());
52 return delegate_->GetBytesInUse();
53 }
54
55 template <class T>
HandleResult(T result)56 T SyncableSettingsStorage::HandleResult(T result) {
57 if (result.status().restore_status != RESTORE_NONE) {
58 // If we're syncing, stop - we don't want to push the deletion of any data.
59 // At next startup, when we start up the sync service, we'll get back any
60 // data which was stored intact on Sync.
61 // TODO(devlin): Investigate if there's a way we can trigger
62 // MergeDataAndStartSyncing() to immediately get back any data we can, and
63 // continue syncing.
64 StopSyncing();
65 }
66 return result;
67 }
68
Get(const std::string & key)69 ValueStore::ReadResult SyncableSettingsStorage::Get(
70 const std::string& key) {
71 DCHECK(IsOnBackendSequence());
72 return HandleResult(delegate_->Get(key));
73 }
74
Get(const std::vector<std::string> & keys)75 ValueStore::ReadResult SyncableSettingsStorage::Get(
76 const std::vector<std::string>& keys) {
77 DCHECK(IsOnBackendSequence());
78 return HandleResult(delegate_->Get(keys));
79 }
80
Get()81 ValueStore::ReadResult SyncableSettingsStorage::Get() {
82 DCHECK(IsOnBackendSequence());
83 return HandleResult(delegate_->Get());
84 }
85
Set(WriteOptions options,const std::string & key,const base::Value & value)86 ValueStore::WriteResult SyncableSettingsStorage::Set(
87 WriteOptions options, const std::string& key, const base::Value& value) {
88 DCHECK(IsOnBackendSequence());
89 WriteResult result = HandleResult(delegate_->Set(options, key, value));
90 if (!result.status().ok())
91 return result;
92 SyncResultIfEnabled(result);
93 return result;
94 }
95
Set(WriteOptions options,const base::DictionaryValue & values)96 ValueStore::WriteResult SyncableSettingsStorage::Set(
97 WriteOptions options, const base::DictionaryValue& values) {
98 DCHECK(IsOnBackendSequence());
99 WriteResult result = HandleResult(delegate_->Set(options, values));
100 if (!result.status().ok())
101 return result;
102 SyncResultIfEnabled(result);
103 return result;
104 }
105
Remove(const std::string & key)106 ValueStore::WriteResult SyncableSettingsStorage::Remove(
107 const std::string& key) {
108 DCHECK(IsOnBackendSequence());
109 WriteResult result = HandleResult(delegate_->Remove(key));
110 if (!result.status().ok())
111 return result;
112 SyncResultIfEnabled(result);
113 return result;
114 }
115
Remove(const std::vector<std::string> & keys)116 ValueStore::WriteResult SyncableSettingsStorage::Remove(
117 const std::vector<std::string>& keys) {
118 DCHECK(IsOnBackendSequence());
119 WriteResult result = HandleResult(delegate_->Remove(keys));
120 if (!result.status().ok())
121 return result;
122 SyncResultIfEnabled(result);
123 return result;
124 }
125
Clear()126 ValueStore::WriteResult SyncableSettingsStorage::Clear() {
127 DCHECK(IsOnBackendSequence());
128 WriteResult result = HandleResult(delegate_->Clear());
129 if (!result.status().ok())
130 return result;
131 SyncResultIfEnabled(result);
132 return result;
133 }
134
SyncResultIfEnabled(const ValueStore::WriteResult & result)135 void SyncableSettingsStorage::SyncResultIfEnabled(
136 const ValueStore::WriteResult& result) {
137 if (result.changes().empty())
138 return;
139
140 if (sync_processor_.get()) {
141 base::Optional<syncer::ModelError> error =
142 sync_processor_->SendChanges(result.changes());
143 if (error.has_value())
144 StopSyncing();
145 } else {
146 // Tell sync to try and start soon, because syncable changes to sync_type_
147 // have started happening. This will cause sync to call us back
148 // asynchronously via StartSyncing(...) as soon as possible.
149 flare_.Run(sync_type_);
150 }
151 }
152
153 // Sync-related methods.
154
StartSyncing(std::unique_ptr<base::DictionaryValue> sync_state,std::unique_ptr<SettingsSyncProcessor> sync_processor)155 base::Optional<syncer::ModelError> SyncableSettingsStorage::StartSyncing(
156 std::unique_ptr<base::DictionaryValue> sync_state,
157 std::unique_ptr<SettingsSyncProcessor> sync_processor) {
158 DCHECK(IsOnBackendSequence());
159 DCHECK(sync_state);
160 DCHECK(!sync_processor_.get());
161
162 sync_processor_ = std::move(sync_processor);
163 sync_processor_->Init(*sync_state);
164
165 ReadResult maybe_settings = delegate_->Get();
166 if (!maybe_settings.status().ok()) {
167 return syncer::ModelError(
168 FROM_HERE, base::StringPrintf("Failed to get settings: %s",
169 maybe_settings.status().message.c_str()));
170 }
171
172 std::unique_ptr<base::DictionaryValue> current_settings =
173 maybe_settings.PassSettings();
174 return sync_state->empty()
175 ? SendLocalSettingsToSync(std::move(current_settings))
176 : OverwriteLocalSettingsWithSync(std::move(sync_state),
177 std::move(current_settings));
178 }
179
180 base::Optional<syncer::ModelError>
SendLocalSettingsToSync(std::unique_ptr<base::DictionaryValue> local_state)181 SyncableSettingsStorage::SendLocalSettingsToSync(
182 std::unique_ptr<base::DictionaryValue> local_state) {
183 DCHECK(IsOnBackendSequence());
184
185 if (local_state->empty())
186 return base::nullopt;
187
188 // Transform the current settings into a list of sync changes.
189 ValueStoreChangeList changes;
190 while (!local_state->empty()) {
191 // It's not possible to iterate over a DictionaryValue and modify it at the
192 // same time, so hack around that restriction.
193 std::string key = base::DictionaryValue::Iterator(*local_state).key();
194 std::unique_ptr<base::Value> value;
195 local_state->RemoveWithoutPathExpansion(key, &value);
196 changes.push_back(ValueStoreChange(key, base::nullopt, std::move(*value)));
197 }
198
199 base::Optional<syncer::ModelError> error =
200 sync_processor_->SendChanges(changes);
201 if (error.has_value())
202 StopSyncing();
203 return error;
204 }
205
206 base::Optional<syncer::ModelError>
OverwriteLocalSettingsWithSync(std::unique_ptr<base::DictionaryValue> sync_state,std::unique_ptr<base::DictionaryValue> local_state)207 SyncableSettingsStorage::OverwriteLocalSettingsWithSync(
208 std::unique_ptr<base::DictionaryValue> sync_state,
209 std::unique_ptr<base::DictionaryValue> local_state) {
210 DCHECK(IsOnBackendSequence());
211 // This is implemented by building up a list of sync changes then sending
212 // those to ProcessSyncChanges. This generates events like onStorageChanged.
213 std::unique_ptr<SettingSyncDataList> changes(new SettingSyncDataList());
214
215 for (base::DictionaryValue::Iterator it(*local_state); !it.IsAtEnd();
216 it.Advance()) {
217 std::unique_ptr<base::Value> sync_value;
218 if (sync_state->RemoveWithoutPathExpansion(it.key(), &sync_value)) {
219 if (sync_value->Equals(&it.value())) {
220 // Sync and local values are the same, no changes to send.
221 } else {
222 // Sync value is different, update local setting with new value.
223 changes->push_back(std::make_unique<SettingSyncData>(
224 syncer::SyncChange::ACTION_UPDATE, extension_id_, it.key(),
225 std::move(sync_value)));
226 }
227 } else {
228 // Not synced, delete local setting.
229 changes->push_back(std::make_unique<SettingSyncData>(
230 syncer::SyncChange::ACTION_DELETE, extension_id_, it.key(),
231 std::unique_ptr<base::Value>(new base::DictionaryValue())));
232 }
233 }
234
235 // Add all new settings to local settings.
236 while (!sync_state->empty()) {
237 // It's not possible to iterate over a DictionaryValue and modify it at the
238 // same time, so hack around that restriction.
239 std::string key = base::DictionaryValue::Iterator(*sync_state).key();
240 std::unique_ptr<base::Value> value;
241 CHECK(sync_state->RemoveWithoutPathExpansion(key, &value));
242 changes->push_back(std::make_unique<SettingSyncData>(
243 syncer::SyncChange::ACTION_ADD, extension_id_, key, std::move(value)));
244 }
245
246 if (changes->empty())
247 return base::nullopt;
248 return ProcessSyncChanges(std::move(changes));
249 }
250
StopSyncing()251 void SyncableSettingsStorage::StopSyncing() {
252 DCHECK(IsOnBackendSequence());
253 sync_processor_.reset();
254 }
255
ProcessSyncChanges(std::unique_ptr<SettingSyncDataList> sync_changes)256 base::Optional<syncer::ModelError> SyncableSettingsStorage::ProcessSyncChanges(
257 std::unique_ptr<SettingSyncDataList> sync_changes) {
258 DCHECK(IsOnBackendSequence());
259 DCHECK(!sync_changes->empty()) << "No sync changes for " << extension_id_;
260
261 if (!sync_processor_.get()) {
262 return syncer::ModelError(
263 FROM_HERE, std::string("Sync is inactive for ") + extension_id_);
264 }
265
266 std::vector<syncer::SyncError> errors;
267 ValueStoreChangeList changes;
268
269 for (const std::unique_ptr<SettingSyncData>& sync_change : *sync_changes) {
270 DCHECK_EQ(extension_id_, sync_change->extension_id());
271 const std::string& key = sync_change->key();
272 std::unique_ptr<base::Value> change_value = sync_change->PassValue();
273
274 std::unique_ptr<base::Value> current_value;
275 {
276 ReadResult maybe_settings = Get(key);
277 if (!maybe_settings.status().ok()) {
278 errors.push_back(syncer::SyncError(
279 FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
280 base::StringPrintf("Error getting current sync state for %s/%s: %s",
281 extension_id_.c_str(), key.c_str(),
282 maybe_settings.status().message.c_str()),
283 sync_processor_->type()));
284 continue;
285 }
286 maybe_settings.settings().RemoveWithoutPathExpansion(key, ¤t_value);
287 }
288
289 syncer::SyncError error;
290
291 switch (sync_change->change_type()) {
292 case syncer::SyncChange::ACTION_ADD:
293 if (!current_value.get()) {
294 error = OnSyncAdd(key, std::move(change_value), &changes);
295 } else {
296 // Already a value; hopefully a local change has beaten sync in a
297 // race and change's not a bug, so pretend change's an update.
298 LOG(WARNING) << "Got add from sync for existing setting " <<
299 extension_id_ << "/" << key;
300 error = OnSyncUpdate(key, std::move(current_value),
301 std::move(change_value), &changes);
302 }
303 break;
304
305 case syncer::SyncChange::ACTION_UPDATE:
306 if (current_value.get()) {
307 error = OnSyncUpdate(key, std::move(current_value),
308 std::move(change_value), &changes);
309 } else {
310 // Similarly, pretend change's an add.
311 LOG(WARNING) << "Got update from sync for nonexistent setting" <<
312 extension_id_ << "/" << key;
313 error = OnSyncAdd(key, std::move(change_value), &changes);
314 }
315 break;
316
317 case syncer::SyncChange::ACTION_DELETE:
318 if (current_value.get()) {
319 error = OnSyncDelete(key, std::move(current_value), &changes);
320 } else {
321 // Similarly, ignore change.
322 LOG(WARNING) << "Got delete from sync for nonexistent setting " <<
323 extension_id_ << "/" << key;
324 }
325 break;
326
327 default:
328 NOTREACHED();
329 }
330
331 if (error.IsSet()) {
332 errors.push_back(error);
333 }
334 }
335
336 sync_processor_->NotifyChanges(changes);
337
338 observers_->Notify(FROM_HERE, &SettingsObserver::OnSettingsChanged,
339 extension_id_, settings_namespace::SYNC,
340 ValueStoreChange::ToJson(changes));
341
342 // TODO(kalman): Something sensible with multiple errors.
343 if (errors.empty())
344 return base::nullopt;
345 return syncer::ConvertToModelError(errors[0]);
346 }
347
OnSyncAdd(const std::string & key,std::unique_ptr<base::Value> new_value,ValueStoreChangeList * changes)348 syncer::SyncError SyncableSettingsStorage::OnSyncAdd(
349 const std::string& key,
350 std::unique_ptr<base::Value> new_value,
351 ValueStoreChangeList* changes) {
352 DCHECK(new_value);
353 WriteResult result =
354 HandleResult(delegate_->Set(IGNORE_QUOTA, key, *new_value));
355 if (!result.status().ok()) {
356 return syncer::SyncError(
357 FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
358 base::StringPrintf("Error pushing sync add to local settings: %s",
359 result.status().message.c_str()),
360 sync_processor_->type());
361 }
362 changes->push_back(
363 ValueStoreChange(key, base::nullopt, std::move(*new_value)));
364 return syncer::SyncError();
365 }
366
OnSyncUpdate(const std::string & key,std::unique_ptr<base::Value> old_value,std::unique_ptr<base::Value> new_value,ValueStoreChangeList * changes)367 syncer::SyncError SyncableSettingsStorage::OnSyncUpdate(
368 const std::string& key,
369 std::unique_ptr<base::Value> old_value,
370 std::unique_ptr<base::Value> new_value,
371 ValueStoreChangeList* changes) {
372 DCHECK(old_value);
373 DCHECK(new_value);
374 WriteResult result =
375 HandleResult(delegate_->Set(IGNORE_QUOTA, key, *new_value));
376 if (!result.status().ok()) {
377 return syncer::SyncError(
378 FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
379 base::StringPrintf("Error pushing sync update to local settings: %s",
380 result.status().message.c_str()),
381 sync_processor_->type());
382 }
383 changes->push_back(
384 ValueStoreChange(key, std::move(*old_value), std::move(*new_value)));
385 return syncer::SyncError();
386 }
387
OnSyncDelete(const std::string & key,std::unique_ptr<base::Value> old_value,ValueStoreChangeList * changes)388 syncer::SyncError SyncableSettingsStorage::OnSyncDelete(
389 const std::string& key,
390 std::unique_ptr<base::Value> old_value,
391 ValueStoreChangeList* changes) {
392 DCHECK(old_value);
393 WriteResult result = HandleResult(delegate_->Remove(key));
394 if (!result.status().ok()) {
395 return syncer::SyncError(
396 FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
397 base::StringPrintf("Error pushing sync remove to local settings: %s",
398 result.status().message.c_str()),
399 sync_processor_->type());
400 }
401 changes->push_back(
402 ValueStoreChange(key, std::move(*old_value), base::nullopt));
403 return syncer::SyncError();
404 }
405
406 } // namespace extensions
407