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