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