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 "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"
14
15namespace extensions {
16
17using content::BrowserThread;
18
19SyncableSettingsStorage::SyncableSettingsStorage(
20    const scoped_refptr<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);
32}
33
34SyncableSettingsStorage::~SyncableSettingsStorage() {
35  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
36}
37
38size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) {
39  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
40  return delegate_->GetBytesInUse(key);
41}
42
43size_t SyncableSettingsStorage::GetBytesInUse(
44    const std::vector<std::string>& keys) {
45  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
46  return delegate_->GetBytesInUse(keys);
47}
48
49size_t SyncableSettingsStorage::GetBytesInUse() {
50  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
51  return delegate_->GetBytesInUse();
52}
53
54ValueStore::ReadResult SyncableSettingsStorage::Get(
55    const std::string& key) {
56  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
57  return delegate_->Get(key);
58}
59
60ValueStore::ReadResult SyncableSettingsStorage::Get(
61    const std::vector<std::string>& keys) {
62  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
63  return delegate_->Get(keys);
64}
65
66ValueStore::ReadResult SyncableSettingsStorage::Get() {
67  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
68  return delegate_->Get();
69}
70
71ValueStore::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();
77  }
78  SyncResultIfEnabled(result);
79  return result.Pass();
80}
81
82ValueStore::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();
88  }
89  SyncResultIfEnabled(result);
90  return result.Pass();
91}
92
93ValueStore::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();
99  }
100  SyncResultIfEnabled(result);
101  return result.Pass();
102}
103
104ValueStore::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();
110  }
111  SyncResultIfEnabled(result);
112  return result.Pass();
113}
114
115ValueStore::WriteResult SyncableSettingsStorage::Clear() {
116  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
117  WriteResult result = delegate_->Clear();
118  if (result->HasError()) {
119    return result.Pass();
120  }
121  SyncResultIfEnabled(result);
122  return result.Pass();
123}
124
125bool 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();
134}
135
136bool 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);
145}
146
147void SyncableSettingsStorage::SyncResultIfEnabled(
148    const ValueStore::WriteResult& result) {
149  if (result->changes().empty())
150    return;
151
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_);
161  }
162}
163
164// Sync-related methods.
165
166syncer::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());
171
172  sync_processor_ = sync_processor.Pass();
173  sync_processor_->Init(sync_state);
174
175  ReadResult maybe_settings = delegate_->Get();
176  if (maybe_settings->HasError()) {
177    return syncer::SyncError(
178        FROM_HERE,
179        syncer::SyncError::DATATYPE_ERROR,
180        base::StringPrintf("Failed to get settings: %s",
181            maybe_settings->error().message.c_str()),
182        sync_processor_->type());
183  }
184
185  const base::DictionaryValue& settings = maybe_settings->settings();
186  return sync_state.empty() ?
187      SendLocalSettingsToSync(settings) :
188      OverwriteLocalSettingsWithSync(sync_state, settings);
189}
190
191syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync(
192    const base::DictionaryValue& settings) {
193  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
194
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()));
198  }
199
200  if (changes.empty())
201    return syncer::SyncError();
202
203  syncer::SyncError error = sync_processor_->SendChanges(changes);
204  if (error.IsSet())
205    StopSyncing();
206
207  return error;
208}
209
210syncer::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());
217
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.
225      } else {
226        // Sync value is different, update local setting with new value.
227        changes.push_back(
228            SettingSyncData(
229                syncer::SyncChange::ACTION_UPDATE,
230                extension_id_,
231                it.key(),
232                sync_value.Pass()));
233      }
234    } else {
235      // Not synced, delete local setting.
236      changes.push_back(
237          SettingSyncData(
238              syncer::SyncChange::ACTION_DELETE,
239              extension_id_,
240              it.key(),
241              scoped_ptr<base::Value>(new base::DictionaryValue())));
242    }
243  }
244
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));
251    changes.push_back(
252        SettingSyncData(
253            syncer::SyncChange::ACTION_ADD,
254            extension_id_,
255            key,
256            value.Pass()));
257  }
258
259  if (changes.empty())
260    return syncer::SyncError();
261
262  return ProcessSyncChanges(changes);
263}
264
265void SyncableSettingsStorage::StopSyncing() {
266  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
267  sync_processor_.reset();
268}
269
270syncer::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_;
274
275  if (!sync_processor_.get()) {
276    return syncer::SyncError(
277        FROM_HERE,
278        syncer::SyncError::DATATYPE_ERROR,
279        std::string("Sync is inactive for ") + extension_id_,
280        syncer::UNSPECIFIED);
281  }
282
283  std::vector<syncer::SyncError> errors;
284  ValueStoreChangeList changes;
285
286  for (SettingSyncDataList::const_iterator it = sync_changes.begin();
287      it != sync_changes.end(); ++it) {
288    DCHECK_EQ(extension_id_, it->extension_id());
289
290    const std::string& key = it->key();
291    const base::Value& value = it->value();
292
293    scoped_ptr<base::Value> current_value;
294    {
295      ReadResult maybe_settings = Get(it->key());
296      if (maybe_settings->HasError()) {
297        errors.push_back(syncer::SyncError(
298            FROM_HERE,
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()));
304        continue;
305      }
306      maybe_settings->settings().RemoveWithoutPathExpansion(key,
307                                                            &current_value);
308    }
309
310    syncer::SyncError error;
311
312    switch (it->change_type()) {
313      case syncer::SyncChange::ACTION_ADD:
314        if (!current_value.get()) {
315          error = OnSyncAdd(key, value.DeepCopy(), &changes);
316        } else {
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);
323        }
324        break;
325
326      case syncer::SyncChange::ACTION_UPDATE:
327        if (current_value.get()) {
328          error = OnSyncUpdate(
329              key, current_value.release(), value.DeepCopy(), &changes);
330        } else {
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);
335        }
336        break;
337
338      case syncer::SyncChange::ACTION_DELETE:
339        if (current_value.get()) {
340          error = OnSyncDelete(key, current_value.release(), &changes);
341        } else {
342          // Similarly, ignore it.
343          LOG(WARNING) << "Got delete from sync for nonexistent setting " <<
344              extension_id_ << "/" << key;
345        }
346        break;
347
348      default:
349        NOTREACHED();
350    }
351
352    if (error.IsSet()) {
353      errors.push_back(error);
354    }
355  }
356
357  sync_processor_->NotifyChanges(changes);
358
359  observers_->Notify(
360      &SettingsObserver::OnSettingsChanged,
361      extension_id_,
362      settings_namespace::SYNC,
363      ValueStoreChange::ToJson(changes));
364
365  // TODO(kalman): Something sensible with multiple errors.
366  return errors.empty() ? syncer::SyncError() : errors[0];
367}
368
369syncer::SyncError SyncableSettingsStorage::OnSyncAdd(
370    const std::string& key,
371    base::Value* new_value,
372    ValueStoreChangeList* changes) {
373  DCHECK(new_value);
374  WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value);
375  if (result->HasError()) {
376    return syncer::SyncError(
377        FROM_HERE,
378        syncer::SyncError::DATATYPE_ERROR,
379        base::StringPrintf("Error pushing sync add to local settings: %s",
380            result->error().message.c_str()),
381        sync_processor_->type());
382  }
383  changes->push_back(ValueStoreChange(key, NULL, new_value));
384  return syncer::SyncError();
385}
386
387syncer::SyncError SyncableSettingsStorage::OnSyncUpdate(
388    const std::string& key,
389    base::Value* old_value,
390    base::Value* new_value,
391    ValueStoreChangeList* changes) {
392  DCHECK(old_value);
393  DCHECK(new_value);
394  WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value);
395  if (result->HasError()) {
396    return syncer::SyncError(
397        FROM_HERE,
398        syncer::SyncError::DATATYPE_ERROR,
399        base::StringPrintf("Error pushing sync update to local settings: %s",
400            result->error().message.c_str()),
401        sync_processor_->type());
402  }
403  changes->push_back(ValueStoreChange(key, old_value, new_value));
404  return syncer::SyncError();
405}
406
407syncer::SyncError SyncableSettingsStorage::OnSyncDelete(
408    const std::string& key,
409    base::Value* old_value,
410    ValueStoreChangeList* changes) {
411  DCHECK(old_value);
412  WriteResult result = delegate_->Remove(key);
413  if (result->HasError()) {
414    return syncer::SyncError(
415        FROM_HERE,
416        syncer::SyncError::DATATYPE_ERROR,
417        base::StringPrintf("Error pushing sync remove to local settings: %s",
418            result->error().message.c_str()),
419        sync_processor_->type());
420  }
421  changes->push_back(ValueStoreChange(key, old_value, NULL));
422  return syncer::SyncError();
423}
424
425}  // namespace extensions
426