Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/extension_settings.h" | 5 #include "chrome/browser/extensions/extension_settings.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
| 9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "content/browser/browser_thread.h" | 12 #include "content/browser/browser_thread.h" |
| 13 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" | 13 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" |
| 14 #include "chrome/browser/extensions/extension_settings_noop_storage.h" | 14 #include "chrome/browser/extensions/extension_settings_noop_storage.h" |
| 15 #include "chrome/browser/extensions/extension_settings_storage_cache.h" | 15 #include "chrome/browser/extensions/extension_settings_storage_cache.h" |
| 16 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | 16 #include "chrome/browser/extensions/syncable_extension_settings_storage.h" |
| 17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
| 18 | 17 |
| 19 ExtensionSettings::ExtensionSettings(const FilePath& base_path) | 18 ExtensionSettings::ExtensionSettings(const FilePath& base_path) |
| 20 : base_path_(base_path) { | 19 : base_path_(base_path), sync_processor_(NULL) { |
| 21 } | 20 } |
| 22 | 21 |
| 23 ExtensionSettings::~ExtensionSettings() { | 22 ExtensionSettings::~ExtensionSettings() { |
| 24 std::map<std::string, ExtensionSettingsStorage*>::iterator it; | 23 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; |
| 25 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | 24 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { |
| 26 it->second->DeleteSoon(); | 25 it->second->DeleteSoon(); |
| 27 } | 26 } |
| 28 } | 27 } |
| 29 | 28 |
| 30 void ExtensionSettings::GetStorage( | 29 void ExtensionSettings::GetStorage( |
| 31 const std::string& extension_id, | 30 const std::string& extension_id, |
| 32 const Callback& callback) { | 31 const Callback& callback) { |
| 33 if (!GetExistingStorage(extension_id, callback)) { | 32 if (!GetExistingStorage(extension_id, callback)) { |
| 34 StartCreationOfStorage( | 33 StartCreationOfStorage( |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 45 bool cached, | 44 bool cached, |
| 46 const std::string& extension_id, | 45 const std::string& extension_id, |
| 47 const Callback& callback) { | 46 const Callback& callback) { |
| 48 if (!GetExistingStorage(extension_id, callback)) { | 47 if (!GetExistingStorage(extension_id, callback)) { |
| 49 StartCreationOfStorage(extension_id, type, type, cached, callback); | 48 StartCreationOfStorage(extension_id, type, type, cached, callback); |
| 50 } | 49 } |
| 51 } | 50 } |
| 52 | 51 |
| 53 bool ExtensionSettings::GetExistingStorage( | 52 bool ExtensionSettings::GetExistingStorage( |
| 54 const std::string& extension_id, const Callback& callback) { | 53 const std::string& extension_id, const Callback& callback) { |
| 55 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | 54 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing = |
| 56 storage_objs_.find(extension_id); | 55 storage_objs_.find(extension_id); |
| 57 if (existing == storage_objs_.end()) { | 56 if (existing == storage_objs_.end()) { |
| 58 // No existing storage. | 57 // No existing storage. |
| 59 return false; | 58 return false; |
| 60 } | 59 } |
| 61 // Existing storage. Reply with that. | 60 // Existing storage. Reply with that. |
| 62 ExtensionSettingsStorage* storage = existing->second; | 61 SyncableExtensionSettingsStorage* storage = existing->second; |
| 63 DCHECK(storage != NULL); | 62 DCHECK(storage != NULL); |
| 64 MessageLoop::current()->PostTask( | 63 MessageLoop::current()->PostTask( |
| 65 FROM_HERE, | 64 FROM_HERE, |
| 66 base::Bind( | 65 base::Bind( |
| 67 &ExtensionSettings::RunWithStorage, | 66 &ExtensionSettings::RunWithStorage, |
| 68 this, | 67 this, |
| 69 new Callback(callback), | 68 new Callback(callback), |
| 70 storage)); | 69 storage)); |
| 71 return true; | 70 return true; |
| 72 } | 71 } |
| 73 | 72 |
| 74 void ExtensionSettings::RunWithStorage( | 73 void ExtensionSettings::RunWithStorage( |
| 75 Callback* callback, ExtensionSettingsStorage* storage) { | 74 Callback* callback, SyncableExtensionSettingsStorage* storage) { |
| 76 callback->Run(storage); | 75 callback->Run(storage); |
| 77 delete callback; | 76 delete callback; |
| 78 } | 77 } |
| 79 | 78 |
| 80 void ExtensionSettings::StartCreationOfStorage( | 79 void ExtensionSettings::StartCreationOfStorage( |
| 81 const std::string& extension_id, | 80 const std::string& extension_id, |
| 82 ExtensionSettingsStorage::Type type, | 81 ExtensionSettingsStorage::Type type, |
| 83 ExtensionSettingsStorage::Type fallback_type, | 82 ExtensionSettingsStorage::Type fallback_type, |
| 84 bool cached, | 83 bool cached, |
| 85 const Callback& callback) { | 84 const Callback& callback) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 96 callback)); | 95 callback)); |
| 97 } | 96 } |
| 98 | 97 |
| 99 void ExtensionSettings::CreateStorageOnFileThread( | 98 void ExtensionSettings::CreateStorageOnFileThread( |
| 100 const std::string& extension_id, | 99 const std::string& extension_id, |
| 101 ExtensionSettingsStorage::Type type, | 100 ExtensionSettingsStorage::Type type, |
| 102 ExtensionSettingsStorage::Type fallback_type, | 101 ExtensionSettingsStorage::Type fallback_type, |
| 103 bool cached, | 102 bool cached, |
| 104 const Callback& callback) { | 103 const Callback& callback) { |
| 105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 106 ExtensionSettingsStorage* storage = CreateStorage(extension_id, type, cached); | 105 SyncableExtensionSettingsStorage* storage = |
| 106 CreateStorage(extension_id, type, cached); | |
| 107 if (storage == NULL && fallback_type != type) { | 107 if (storage == NULL && fallback_type != type) { |
| 108 storage = CreateStorage(extension_id, fallback_type, cached); | 108 storage = CreateStorage(extension_id, fallback_type, cached); |
| 109 } | 109 } |
| 110 DCHECK(storage != NULL); | 110 DCHECK(storage != NULL); |
| 111 BrowserThread::PostTask( | 111 BrowserThread::PostTask( |
| 112 BrowserThread::UI, | 112 BrowserThread::UI, |
| 113 FROM_HERE, | 113 FROM_HERE, |
| 114 base::Bind( | 114 base::Bind( |
| 115 &ExtensionSettings::EndCreationOfStorage, | 115 &ExtensionSettings::EndCreationOfStorage, |
| 116 this, | 116 this, |
| 117 extension_id, | 117 extension_id, |
| 118 storage, | 118 storage, |
| 119 callback)); | 119 callback)); |
| 120 } | 120 } |
| 121 | 121 |
| 122 ExtensionSettingsStorage* ExtensionSettings::CreateStorage( | 122 SyncableExtensionSettingsStorage* ExtensionSettings::CreateStorage( |
| 123 const std::string& extension_id, | 123 const std::string& extension_id, |
| 124 ExtensionSettingsStorage::Type type, | 124 ExtensionSettingsStorage::Type type, |
| 125 bool cached) { | 125 bool cached) { |
| 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 127 ExtensionSettingsStorage* storage = NULL; | 127 ExtensionSettingsStorage* storage = NULL; |
| 128 switch (type) { | 128 switch (type) { |
| 129 case ExtensionSettingsStorage::NOOP: | 129 case ExtensionSettingsStorage::NOOP: |
| 130 storage = new ExtensionSettingsNoopStorage(); | 130 storage = new ExtensionSettingsNoopStorage(); |
| 131 break; | 131 break; |
| 132 case ExtensionSettingsStorage::LEVELDB: | 132 case ExtensionSettingsStorage::LEVELDB: |
| 133 storage = ExtensionSettingsLeveldbStorage::Create( | 133 storage = ExtensionSettingsLeveldbStorage::Create( |
| 134 base_path_, extension_id); | 134 base_path_, extension_id); |
| 135 break; | 135 break; |
| 136 default: | 136 default: |
| 137 NOTREACHED(); | 137 NOTREACHED(); |
| 138 } | 138 } |
| 139 if (storage != NULL && cached) { | 139 if (storage == NULL) { |
| 140 return NULL; | |
| 141 } | |
| 142 if (cached) { | |
| 140 storage = new ExtensionSettingsStorageCache(storage); | 143 storage = new ExtensionSettingsStorageCache(storage); |
| 141 } | 144 } |
| 142 return storage; | 145 // Always send changes to sync. The merge algorithm is such that local data |
| 146 // will be overwritten by sync data, so even if it's purely in-memory storage | |
| 147 // the settings will be consistent across browser restarts. | |
| 148 return new SyncableExtensionSettingsStorage(extension_id, storage); | |
| 143 } | 149 } |
| 144 | 150 |
| 145 void ExtensionSettings::EndCreationOfStorage( | 151 void ExtensionSettings::EndCreationOfStorage( |
| 146 const std::string& extension_id, | 152 const std::string& extension_id, |
| 147 ExtensionSettingsStorage* storage, | 153 SyncableExtensionSettingsStorage* storage, |
| 148 const Callback& callback) { | 154 const Callback& callback) { |
| 149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 150 // Cache the result now. To avoid a race condition, check again to see | 156 // Cache the result now. To avoid a race condition, check again to see |
| 151 // whether a storage has been created already; if so, use that one. | 157 // whether a storage has been created already; if so, use that one. |
| 152 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | 158 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing = |
| 153 storage_objs_.find(extension_id); | 159 storage_objs_.find(extension_id); |
| 154 if (existing == storage_objs_.end()) { | 160 if (existing == storage_objs_.end()) { |
| 155 storage_objs_[extension_id] = storage; | 161 storage_objs_[extension_id] = storage; |
| 156 } else { | 162 } else { |
| 157 storage->DeleteSoon(); | 163 storage->DeleteSoon(); |
| 158 storage = existing->second; | 164 storage = existing->second; |
| 159 DCHECK(storage != NULL); | 165 DCHECK(storage != NULL); |
| 160 } | 166 } |
| 167 if (sync_processor_ != NULL) { | |
| 168 StartSyncingStorage(extension_id, storage); | |
| 169 } | |
| 161 callback.Run(storage); | 170 callback.Run(storage); |
| 162 } | 171 } |
| 172 | |
| 173 SyncDataList ExtensionSettings::GetAllSyncData( | |
| 174 syncable::ModelType type) const { | |
| 175 // Not possible to do this synchronously, or while satisfying const, but only | |
|
akalin
2011/08/31 07:49:34
Explain to me again why the sync methods can't liv
not at google - send to devlin
2011/09/02 05:00:40
The extension settings code already thread jumps b
| |
| 176 // used for debugging so fine to ignore. | |
| 177 return SyncDataList(); | |
| 178 } | |
| 179 | |
| 180 SyncError ExtensionSettings::MergeDataAndStartSyncing( | |
| 181 syncable::ModelType type, | |
| 182 const SyncDataList& initial_sync_data, | |
| 183 SyncChangeProcessor* sync_processor) { | |
| 184 DCHECK(type == syncable::EXTENSION_SETTINGS); | |
| 185 DCHECK(sync_processor_ == NULL); | |
| 186 sync_processor_ = sync_processor; | |
| 187 | |
| 188 // Unmerged sync data could conceivably be non-empty if this method is called | |
| 189 // in quick succession, before the storage creation methods return (clearing | |
| 190 // the data). Clearing the unmerged data here is safe, it will just get | |
| 191 // repopulated with the new and correct data. | |
| 192 std::map<std::string, DictionaryValue*>::iterator unmerged_it; | |
| 193 for (unmerged_it = unmerged_sync_data_.begin(); | |
| 194 unmerged_it != unmerged_sync_data_.end(); ++unmerged_it) { | |
| 195 delete unmerged_it->second; | |
| 196 } | |
| 197 unmerged_sync_data_.clear(); | |
| 198 | |
| 199 // Merge algorithm when starting to sync: for each extension, if there are no | |
| 200 // sycned settings yet, send local settings to sync. Otherwise, completely | |
| 201 // overwrite local settings with those from sync. | |
| 202 // | |
| 203 // Firstly group the initial data per extension. | |
| 204 for (SyncDataList::const_iterator it = initial_sync_data.begin(); | |
| 205 it != initial_sync_data.end(); ++it) { | |
| 206 ExtensionSettingSyncData data(*it); | |
| 207 if (data.value() == NULL) { | |
| 208 LOG(WARNING) << "NULL value in sync data"; | |
| 209 continue; | |
| 210 } | |
| 211 DictionaryValue* sync_data = unmerged_sync_data_[data.extension_id()]; | |
| 212 if (sync_data == NULL) { | |
| 213 sync_data = new DictionaryValue(); | |
| 214 unmerged_sync_data_[data.extension_id()] = sync_data; | |
| 215 } | |
| 216 DCHECK(!sync_data->HasKey(data.key())) << | |
| 217 "Duplicate settings for " << data.extension_id() << "/" << data.key(); | |
| 218 sync_data->Set(data.key(), data.value()->DeepCopy()); | |
| 219 } | |
| 220 | |
| 221 // Immediately start syncing the settings of existing storage areas. | |
| 222 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator storage_it; | |
| 223 for (storage_it = storage_objs_.begin(); | |
| 224 storage_it != storage_objs_.end(); ++storage_it) { | |
| 225 StartSyncingStorage(storage_it->first, storage_it->second); | |
| 226 } | |
| 227 | |
| 228 // Asynchronously start syncing the remaining settings. | |
| 229 for (unmerged_it = unmerged_sync_data_.begin(); | |
| 230 unmerged_it != unmerged_sync_data_.end(); ++unmerged_it) { | |
| 231 // StartSyncingStorage will be called from EndCreationOfStorage, so the | |
| 232 // actual callback from getting the storage doesn't need to do anything. | |
| 233 GetStorage( | |
| 234 unmerged_it->first, | |
| 235 base::Bind( | |
| 236 &ExtensionSettings::AssertNoSyncData, | |
| 237 this, | |
| 238 unmerged_it->first)); | |
| 239 } | |
| 240 | |
| 241 return SyncError(); | |
| 242 } | |
| 243 | |
| 244 void ExtensionSettings::AssertNoSyncData( | |
| 245 const std::string& extension_id, | |
| 246 SyncableExtensionSettingsStorage* storage) { | |
| 247 DCHECK_EQ(0u, unmerged_sync_data_.count(extension_id)); | |
| 248 } | |
| 249 | |
| 250 void ExtensionSettings::StartSyncingStorage( | |
| 251 const std::string& extension_id, | |
| 252 SyncableExtensionSettingsStorage* storage) { | |
| 253 DCHECK(sync_processor_ != NULL); | |
| 254 scoped_ptr<DictionaryValue> sync_data(unmerged_sync_data_[extension_id]); | |
| 255 unmerged_sync_data_.erase(extension_id); | |
| 256 if (sync_data.get() == NULL) { | |
| 257 DictionaryValue no_data; | |
| 258 storage->StartSyncing(no_data, sync_processor_); | |
| 259 } else { | |
| 260 storage->StartSyncing(*sync_data, sync_processor_); | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 static void CallProcessSyncChanges( | |
| 265 ExtensionSettingSyncDataList* sync_changes, | |
| 266 SyncableExtensionSettingsStorage* storage) { | |
| 267 scoped_ptr<ExtensionSettingSyncDataList> changes_ownership(sync_changes); | |
| 268 storage->ProcessSyncChanges(*sync_changes); | |
| 269 } | |
| 270 | |
| 271 SyncError ExtensionSettings::ProcessSyncChanges( | |
| 272 const tracked_objects::Location& from_here, | |
| 273 const SyncChangeList& sync_changes) { | |
| 274 DCHECK(sync_processor_ != NULL); | |
| 275 | |
| 276 // Gather changes per extension, so that all the processing can be done from a | |
| 277 // single GetStorage() call. | |
| 278 std::map<std::string, ExtensionSettingSyncDataList> all_sync_data; | |
| 279 for (SyncChangeList::const_iterator it = sync_changes.begin(); | |
| 280 it != sync_changes.end(); ++it) { | |
| 281 ExtensionSettingSyncData data(*it); | |
| 282 if (data.value() == NULL) { | |
| 283 LOG(WARNING) << "NULL value in sync data"; | |
| 284 continue; | |
| 285 } | |
| 286 all_sync_data[data.extension_id()].push_back(data); | |
| 287 } | |
| 288 | |
| 289 std::map<std::string, ExtensionSettingSyncDataList>::iterator it; | |
| 290 for (it = all_sync_data.begin(); it != all_sync_data.end(); ++it) { | |
| 291 GetStorage( | |
| 292 it->first, | |
| 293 base::Bind( | |
| 294 &CallProcessSyncChanges, | |
| 295 new ExtensionSettingSyncDataList(it->second))); | |
|
akalin
2011/08/31 07:49:34
this may leak. Just pass by value.
not at google - send to devlin
2011/09/02 05:00:40
How can it leak?
But passing by value works, so,
| |
| 296 } | |
| 297 | |
| 298 return SyncError(); | |
| 299 } | |
| 300 | |
| 301 void ExtensionSettings::StopSyncing(syncable::ModelType type) { | |
| 302 DCHECK(type == syncable::EXTENSION_SETTINGS); | |
| 303 DCHECK(sync_processor_ != NULL); | |
| 304 sync_processor_ = NULL; | |
| 305 | |
| 306 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; | |
| 307 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | |
| 308 it->second->StopSyncing(); | |
| 309 } | |
| 310 } | |
| OLD | NEW |