Home | History | Annotate | Download | only in storage
      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 
     15 namespace extensions {
     16 
     17 using content::BrowserThread;
     18 
     19 SyncableSettingsStorage::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 
     34 SyncableSettingsStorage::~SyncableSettingsStorage() {
     35   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     36 }
     37 
     38 size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) {
     39   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     40   return delegate_->GetBytesInUse(key);
     41 }
     42 
     43 size_t SyncableSettingsStorage::GetBytesInUse(
     44     const std::vector<std::string>& keys) {
     45   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     46   return delegate_->GetBytesInUse(keys);
     47 }
     48 
     49 size_t SyncableSettingsStorage::GetBytesInUse() {
     50   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     51   return delegate_->GetBytesInUse();
     52 }
     53 
     54 ValueStore::ReadResult SyncableSettingsStorage::Get(
     55     const std::string& key) {
     56   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     57   return delegate_->Get(key);
     58 }
     59 
     60 ValueStore::ReadResult SyncableSettingsStorage::Get(
     61     const std::vector<std::string>& keys) {
     62   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     63   return delegate_->Get(keys);
     64 }
     65 
     66 ValueStore::ReadResult SyncableSettingsStorage::Get() {
     67   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     68   return delegate_->Get();
     69 }
     70 
     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();
     77   }
     78   SyncResultIfEnabled(result);
     79   return result.Pass();
     80 }
     81 
     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();
     88   }
     89   SyncResultIfEnabled(result);
     90   return result.Pass();
     91 }
     92 
     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();
     99   }
    100   SyncResultIfEnabled(result);
    101   return result.Pass();
    102 }
    103 
    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();
    110   }
    111   SyncResultIfEnabled(result);
    112   return result.Pass();
    113 }
    114 
    115 ValueStore::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 
    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();
    134 }
    135 
    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);
    145 }
    146 
    147 void 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 
    166 syncer::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 
    191 syncer::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 
    210 syncer::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 
    265 void SyncableSettingsStorage::StopSyncing() {
    266   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    267   sync_processor_.reset();
    268 }
    269 
    270 syncer::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 
    369 syncer::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 
    387 syncer::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 
    407 syncer::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