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