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, &current_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