Home | History | Annotate | Download | only in storage
      1 // Copyright 2014 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/sync_storage_backend.h"
      6 
      7 #include "base/files/file_enumerator.h"
      8 #include "base/logging.h"
      9 #include "chrome/browser/extensions/api/storage/settings_sync_processor.h"
     10 #include "chrome/browser/extensions/api/storage/settings_sync_util.h"
     11 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "sync/api/sync_error_factory.h"
     14 
     15 using content::BrowserThread;
     16 
     17 namespace extensions {
     18 
     19 namespace {
     20 
     21 void AddAllSyncData(const std::string& extension_id,
     22                     const base::DictionaryValue& src,
     23                     syncer::ModelType type,
     24                     syncer::SyncDataList* dst) {
     25   for (base::DictionaryValue::Iterator it(src); !it.IsAtEnd(); it.Advance()) {
     26     dst->push_back(settings_sync_util::CreateData(
     27         extension_id, it.key(), it.value(), type));
     28   }
     29 }
     30 
     31 }  // namespace
     32 
     33 SyncStorageBackend::SyncStorageBackend(
     34     const scoped_refptr<SettingsStorageFactory>& storage_factory,
     35     const base::FilePath& base_path,
     36     const SettingsStorageQuotaEnforcer::Limits& quota,
     37     const scoped_refptr<SettingsObserverList>& observers,
     38     syncer::ModelType sync_type,
     39     const syncer::SyncableService::StartSyncFlare& flare)
     40     : storage_factory_(storage_factory),
     41       base_path_(base_path),
     42       quota_(quota),
     43       observers_(observers),
     44       sync_type_(sync_type),
     45       flare_(flare) {
     46   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     47   DCHECK(sync_type_ == syncer::EXTENSION_SETTINGS ||
     48          sync_type_ == syncer::APP_SETTINGS);
     49 }
     50 
     51 SyncStorageBackend::~SyncStorageBackend() {}
     52 
     53 ValueStore* SyncStorageBackend::GetStorage(const std::string& extension_id) {
     54   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     55   base::DictionaryValue empty;
     56   return GetOrCreateStorageWithSyncData(extension_id, empty);
     57 }
     58 
     59 SyncableSettingsStorage* SyncStorageBackend::GetOrCreateStorageWithSyncData(
     60     const std::string& extension_id,
     61     const base::DictionaryValue& sync_data) const {
     62   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     63 
     64   StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
     65   if (maybe_storage != storage_objs_.end()) {
     66     return maybe_storage->second.get();
     67   }
     68 
     69   scoped_ptr<SettingsStorageQuotaEnforcer> storage(
     70       new SettingsStorageQuotaEnforcer(
     71           quota_, storage_factory_->Create(base_path_, extension_id)));
     72 
     73   // It's fine to create the quota enforcer underneath the sync layer, since
     74   // sync will only go ahead if each underlying storage operation succeeds.
     75   linked_ptr<SyncableSettingsStorage> syncable_storage(
     76       new SyncableSettingsStorage(
     77           observers_, extension_id, storage.release(), sync_type_, flare_));
     78   storage_objs_[extension_id] = syncable_storage;
     79 
     80   if (sync_processor_.get()) {
     81     syncer::SyncError error = syncable_storage->StartSyncing(
     82         sync_data, CreateSettingsSyncProcessor(extension_id).Pass());
     83     if (error.IsSet())
     84       syncable_storage.get()->StopSyncing();
     85   }
     86   return syncable_storage.get();
     87 }
     88 
     89 void SyncStorageBackend::DeleteStorage(const std::string& extension_id) {
     90   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
     91 
     92   // Clear settings when the extension is uninstalled.  Leveldb implementations
     93   // will also delete the database from disk when the object is destroyed as a
     94   // result of being removed from |storage_objs_|.
     95   //
     96   // TODO(kalman): always GetStorage here (rather than only clearing if it
     97   // exists) since the storage area may have been unloaded, but we still want
     98   // to clear the data from disk.
     99   // However, this triggers http://crbug.com/111072.
    100   StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
    101   if (maybe_storage == storage_objs_.end())
    102     return;
    103   maybe_storage->second->Clear();
    104   storage_objs_.erase(extension_id);
    105 }
    106 
    107 std::set<std::string> SyncStorageBackend::GetKnownExtensionIDs() const {
    108   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    109   std::set<std::string> result;
    110 
    111   // Storage areas can be in-memory as well as on disk. |storage_objs_| will
    112   // contain all that are in-memory.
    113   for (StorageObjMap::iterator it = storage_objs_.begin();
    114        it != storage_objs_.end();
    115        ++it) {
    116     result.insert(it->first);
    117   }
    118 
    119   // Leveldb databases are directories inside |base_path_|.
    120   base::FileEnumerator extension_dirs(
    121       base_path_, false, base::FileEnumerator::DIRECTORIES);
    122   while (!extension_dirs.Next().empty()) {
    123     base::FilePath extension_dir = extension_dirs.GetInfo().GetName();
    124     DCHECK(!extension_dir.IsAbsolute());
    125     // Extension IDs are created as std::strings so they *should* be ASCII.
    126     std::string maybe_as_ascii(extension_dir.MaybeAsASCII());
    127     if (!maybe_as_ascii.empty()) {
    128       result.insert(maybe_as_ascii);
    129     }
    130   }
    131 
    132   return result;
    133 }
    134 
    135 syncer::SyncDataList SyncStorageBackend::GetAllSyncData(syncer::ModelType type)
    136     const {
    137   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    138   // Ignore the type, it's just for sanity checking; assume that whatever base
    139   // path we're constructed with is correct for the sync type.
    140   DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS);
    141 
    142   // For all extensions, get all their settings.  This has the effect
    143   // of bringing in the entire state of extension settings in memory; sad.
    144   syncer::SyncDataList all_sync_data;
    145   std::set<std::string> known_extension_ids(GetKnownExtensionIDs());
    146 
    147   for (std::set<std::string>::const_iterator it = known_extension_ids.begin();
    148        it != known_extension_ids.end();
    149        ++it) {
    150     ValueStore::ReadResult maybe_settings =
    151         GetOrCreateStorageWithSyncData(*it, base::DictionaryValue())->Get();
    152     if (maybe_settings->HasError()) {
    153       LOG(WARNING) << "Failed to get settings for " << *it << ": "
    154                    << maybe_settings->error().message;
    155       continue;
    156     }
    157     AddAllSyncData(*it, maybe_settings->settings(), type, &all_sync_data);
    158   }
    159 
    160   return all_sync_data;
    161 }
    162 
    163 syncer::SyncMergeResult SyncStorageBackend::MergeDataAndStartSyncing(
    164     syncer::ModelType type,
    165     const syncer::SyncDataList& initial_sync_data,
    166     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
    167     scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
    168   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    169   DCHECK_EQ(sync_type_, type);
    170   DCHECK(!sync_processor_.get());
    171   DCHECK(sync_processor.get());
    172   DCHECK(sync_error_factory.get());
    173 
    174   sync_processor_ = sync_processor.Pass();
    175   sync_error_factory_ = sync_error_factory.Pass();
    176 
    177   // Group the initial sync data by extension id.
    178   std::map<std::string, linked_ptr<base::DictionaryValue> > grouped_sync_data;
    179   for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
    180        it != initial_sync_data.end();
    181        ++it) {
    182     SettingSyncData data(*it);
    183     linked_ptr<base::DictionaryValue> sync_data =
    184         grouped_sync_data[data.extension_id()];
    185     if (!sync_data.get()) {
    186       sync_data =
    187           linked_ptr<base::DictionaryValue>(new base::DictionaryValue());
    188       grouped_sync_data[data.extension_id()] = sync_data;
    189     }
    190     DCHECK(!sync_data->HasKey(data.key())) << "Duplicate settings for "
    191                                            << data.extension_id() << "/"
    192                                            << data.key();
    193     sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy());
    194   }
    195 
    196   // Start syncing all existing storage areas.  Any storage areas created in
    197   // the future will start being synced as part of the creation process.
    198   for (StorageObjMap::iterator it = storage_objs_.begin();
    199        it != storage_objs_.end();
    200        ++it) {
    201     std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator
    202         maybe_sync_data = grouped_sync_data.find(it->first);
    203     syncer::SyncError error;
    204     if (maybe_sync_data != grouped_sync_data.end()) {
    205       error = it->second->StartSyncing(
    206           *maybe_sync_data->second,
    207           CreateSettingsSyncProcessor(it->first).Pass());
    208       grouped_sync_data.erase(it->first);
    209     } else {
    210       base::DictionaryValue empty;
    211       error = it->second->StartSyncing(
    212           empty, CreateSettingsSyncProcessor(it->first).Pass());
    213     }
    214     if (error.IsSet())
    215       it->second->StopSyncing();
    216   }
    217 
    218   // Eagerly create and init the rest of the storage areas that have sync data.
    219   // Under normal circumstances (i.e. not first-time sync) this will be all of
    220   // them.
    221   for (std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator it =
    222            grouped_sync_data.begin();
    223        it != grouped_sync_data.end();
    224        ++it) {
    225     GetOrCreateStorageWithSyncData(it->first, *it->second);
    226   }
    227 
    228   return syncer::SyncMergeResult(type);
    229 }
    230 
    231 syncer::SyncError SyncStorageBackend::ProcessSyncChanges(
    232     const tracked_objects::Location& from_here,
    233     const syncer::SyncChangeList& sync_changes) {
    234   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    235   DCHECK(sync_processor_.get());
    236 
    237   // Group changes by extension, to pass all changes in a single method call.
    238   std::map<std::string, SettingSyncDataList> grouped_sync_data;
    239   for (syncer::SyncChangeList::const_iterator it = sync_changes.begin();
    240        it != sync_changes.end();
    241        ++it) {
    242     SettingSyncData data(*it);
    243     grouped_sync_data[data.extension_id()].push_back(data);
    244   }
    245 
    246   // Create any storage areas that don't exist yet but have sync data.
    247   base::DictionaryValue empty;
    248   for (std::map<std::string, SettingSyncDataList>::iterator it =
    249            grouped_sync_data.begin();
    250        it != grouped_sync_data.end();
    251        ++it) {
    252     SyncableSettingsStorage* storage =
    253         GetOrCreateStorageWithSyncData(it->first, empty);
    254     syncer::SyncError error = storage->ProcessSyncChanges(it->second);
    255     if (error.IsSet())
    256       storage->StopSyncing();
    257   }
    258 
    259   return syncer::SyncError();
    260 }
    261 
    262 void SyncStorageBackend::StopSyncing(syncer::ModelType type) {
    263   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    264   DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS);
    265   DCHECK_EQ(sync_type_, type);
    266 
    267   for (StorageObjMap::iterator it = storage_objs_.begin();
    268        it != storage_objs_.end();
    269        ++it) {
    270     // Some storage areas may have already stopped syncing if they had areas
    271     // and syncing was disabled, but StopSyncing is safe to call multiple times.
    272     it->second->StopSyncing();
    273   }
    274 
    275   sync_processor_.reset();
    276   sync_error_factory_.reset();
    277 }
    278 
    279 scoped_ptr<SettingsSyncProcessor>
    280 SyncStorageBackend::CreateSettingsSyncProcessor(const std::string& extension_id)
    281     const {
    282   CHECK(sync_processor_.get());
    283   return scoped_ptr<SettingsSyncProcessor>(new SettingsSyncProcessor(
    284       extension_id, sync_type_, sync_processor_.get()));
    285 }
    286 
    287 }  // namespace extensions
    288