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 15using content::BrowserThread; 16 17namespace extensions { 18 19SettingsBackend::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 37SettingsBackend::~SettingsBackend() { 38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 39} 40 41ValueStore* 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 48SyncableSettingsStorage* 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 89void 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 107std::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 134static 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 145syncer::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 173syncer::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 237syncer::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 265void 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 282scoped_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