Chromium Code Reviews| Index: chrome/browser/extensions/extension_settings.cc |
| diff --git a/chrome/browser/extensions/extension_settings.cc b/chrome/browser/extensions/extension_settings.cc |
| index 0d855b95c9acb3747cc00bc25d3195fa4f2ac936..bd3feddcc84e3b92e3a180bc6580da78d5d21a04 100644 |
| --- a/chrome/browser/extensions/extension_settings.cc |
| +++ b/chrome/browser/extensions/extension_settings.cc |
| @@ -8,72 +8,95 @@ |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/logging.h" |
| +#include "base/memory/linked_ptr.h" |
| #include "base/memory/scoped_ptr.h" |
| -#include "content/browser/browser_thread.h" |
| #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" |
| #include "chrome/browser/extensions/extension_settings_noop_storage.h" |
| #include "chrome/browser/extensions/extension_settings_storage_cache.h" |
| +#include "chrome/browser/extensions/extension_settings_sync_util.h" |
| +#include "chrome/common/extensions/extension.h" |
| +#include "content/browser/browser_thread.h" |
| #include "third_party/leveldatabase/src/include/leveldb/iterator.h" |
| #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| ExtensionSettings::ExtensionSettings(const FilePath& base_path) |
| - : base_path_(base_path) {} |
| + : base_path_(base_path), |
| + extension_service_(NULL), |
| + sync_processor_(NULL) {} |
|
akalin
2011/09/15 19:56:44
add DCHECK for FILE thread
not at google - send to devlin
2011/09/16 05:18:59
... except it doesn't need to be constructed on th
akalin
2011/09/16 15:36:40
An object which is constructed/destroyed on one th
not at google - send to devlin
2011/09/19 07:10:46
I don't see why it can't be understood by itself.
|
| ExtensionSettings::~ExtensionSettings() { |
| - std::map<std::string, ExtensionSettingsStorage*>::iterator it; |
| + std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; |
|
akalin
2011/09/15 19:56:44
add DCHECK for FILE thread
not at google - send to devlin
2011/09/16 05:18:59
Ditto above comment, which is why they're deleted
akalin
2011/09/16 15:36:40
See earlier comment. If this class lives complete
not at google - send to devlin
2011/09/19 07:10:46
Yep, I made this change in the version I uploaded
|
| for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { |
| BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second); |
| } |
| } |
| +void ExtensionSettings::SetExtensionService( |
| + ExtensionServiceInterface* extension_service) { |
| + extension_service_ = extension_service; |
| +} |
| + |
| ExtensionSettingsStorage* ExtensionSettings::GetStorage( |
| - const std::string& extension_id) { |
| + const std::string& extension_id) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DictionaryValue empty; |
|
akalin
2011/09/15 19:56:44
i prefer just inlining this, e.g.
return GetOrCre
not at google - send to devlin
2011/09/16 05:18:59
Ah right. Yes, I would do that but DictionaryValu
akalin
2011/09/16 15:36:40
Right, I forgot about that. Ugh.
|
| + return GetOrCreateStorageWithSyncData(extension_id, empty); |
| +} |
| - std::map<std::string, ExtensionSettingsStorage*>::iterator existing = |
| - storage_objs_.find(extension_id); |
| - if (existing != storage_objs_.end()) { |
| - return existing->second; |
| - } |
| - |
| - ExtensionSettingsStorage* new_storage = |
| - CreateStorage(extension_id, ExtensionSettingsStorage::LEVELDB, true); |
| - if (new_storage == NULL) { |
| - // Failed to create a leveldb storage for some reason. Use an in memory |
| - // storage area (no-op wrapped in a cache) instead. |
| - new_storage = |
| - CreateStorage(extension_id, ExtensionSettingsStorage::NOOP, true); |
| - DCHECK(new_storage != NULL); |
| +SyncableExtensionSettingsStorage* |
| +ExtensionSettings::GetOrCreateStorageWithSyncData( |
| + const std::string& extension_id, const DictionaryValue& sync_data) const { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + SyncableExtensionSettingsStorage* storage = GetOrCreateAndInitStorage( |
| + ExtensionSettingsStorage::LEVELDB, |
| + true, |
| + extension_id, |
| + sync_data); |
| + if (!storage) { |
|
akalin
2011/09/15 19:56:44
What happens in this case? That means that the se
not at google - send to devlin
2011/09/16 05:18:59
I thought about this earlier, and I think it's fin
|
| + // Fall back to an in-memory storage area (cached NOOP). |
| + storage = GetOrCreateAndInitStorage( |
| + ExtensionSettingsStorage::NOOP, |
| + true, |
| + extension_id, |
| + sync_data); |
| + DCHECK(storage); |
| } |
| - |
| - storage_objs_[extension_id] = new_storage; |
| - return new_storage; |
| + return storage; |
| } |
| ExtensionSettingsStorage* ExtensionSettings::GetStorageForTesting( |
| ExtensionSettingsStorage::Type type, |
| bool cached, |
| - const std::string& extension_id) { |
| + const std::string& extension_id) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DictionaryValue empty; |
| + ExtensionSettingsStorage* storage = |
| + GetOrCreateAndInitStorage(type, cached, extension_id, empty); |
| + DCHECK(storage); |
| + return storage; |
| +} |
| - std::map<std::string, ExtensionSettingsStorage*>::iterator existing = |
| +SyncableExtensionSettingsStorage* ExtensionSettings::GetOrCreateAndInitStorage( |
| + ExtensionSettingsStorage::Type type, |
| + bool cached, |
| + const std::string& extension_id, |
| + const DictionaryValue& initial_sync_data) const { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing = |
| storage_objs_.find(extension_id); |
| if (existing != storage_objs_.end()) { |
| return existing->second; |
| } |
| - |
| - ExtensionSettingsStorage* new_storage = |
| - CreateStorage(extension_id, type, cached); |
| - DCHECK(new_storage != NULL); |
| - storage_objs_[extension_id] = new_storage; |
| - return new_storage; |
| + return CreateAndInitStorage(type, cached, extension_id, initial_sync_data); |
| } |
| -ExtensionSettingsStorage* ExtensionSettings::CreateStorage( |
| - const std::string& extension_id, |
| +SyncableExtensionSettingsStorage* ExtensionSettings::CreateAndInitStorage( |
| ExtensionSettingsStorage::Type type, |
| - bool cached) { |
| + bool cached, |
| + const std::string& extension_id, |
| + const DictionaryValue& initial_sync_data) const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK(storage_objs_.count(extension_id) == 0); |
|
akalin
2011/09/15 19:56:44
DCHECK_EQ
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| ExtensionSettingsStorage* storage = NULL; |
| switch (type) { |
| case ExtensionSettingsStorage::NOOP: |
| @@ -86,8 +109,154 @@ ExtensionSettingsStorage* ExtensionSettings::CreateStorage( |
| default: |
| NOTREACHED(); |
| } |
| - if (storage != NULL && cached) { |
| + if (!storage) { |
| + return NULL; |
| + } |
| + if (cached) { |
| storage = new ExtensionSettingsStorageCache(storage); |
| } |
| - return storage; |
| + |
| + SyncableExtensionSettingsStorage* synced_storage = |
| + new SyncableExtensionSettingsStorage(extension_id, storage); |
| + if (sync_processor_) { |
| + synced_storage->StartSyncing(initial_sync_data, sync_processor_); |
| + } |
| + storage_objs_[extension_id] = synced_storage; |
| + return synced_storage; |
| +} |
| + |
| +SyncDataList ExtensionSettings::GetAllSyncData( |
| + syncable::ModelType type) const { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK_EQ(type, syncable::EXTENSION_SETTINGS); |
| + DCHECK(extension_service_); |
| + |
| + // For all extensions, get all their settings. |
| + // This has the effect of bringing in the entire state of extension settings |
| + // in memory; sad. |
| + SyncDataList all_sync_data; |
|
akalin
2011/09/15 19:56:44
I think the logic that gathers all the extensions
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| + AppendAllSyncData(&all_sync_data, *extension_service_->extensions()); |
| + AppendAllSyncData(&all_sync_data, |
| + *extension_service_->disabled_extensions()); |
| + AppendAllSyncData(&all_sync_data, |
| + *extension_service_->terminated_extensions()); |
| + return all_sync_data; |
| +} |
| + |
| +void ExtensionSettings::AppendAllSyncData( |
| + SyncDataList* sync_data, const ExtensionList& extensions) const { |
| + for (ExtensionList::const_iterator it = extensions.begin(); |
|
akalin
2011/09/15 19:56:44
DCHECK, file thread
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| + it != extensions.end(); ++it) { |
| + std::string extension_id = (*it)->id(); |
| + ExtensionSettingsStorage::Result maybe_settings = |
| + GetStorage(extension_id)->Get(); |
| + if (maybe_settings.HasError()) { |
| + LOG(WARNING) << "Failed to get settings for " << extension_id << ": " << |
| + maybe_settings.GetError(); |
| + continue; |
| + } |
| + |
| + DictionaryValue* settings = maybe_settings.GetSettings(); |
| + for (DictionaryValue::key_iterator key_it = settings->begin_keys(); |
| + key_it != settings->end_keys(); ++key_it) { |
| + Value *value; |
|
akalin
2011/09/15 19:56:44
= NULL
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| + settings->GetWithoutPathExpansion(*key_it, &value); |
| + sync_data->push_back( |
| + extension_settings_sync_util::CreateData( |
| + extension_id, *key_it, *value)); |
| + } |
| + } |
| +} |
| + |
| +SyncError ExtensionSettings::MergeDataAndStartSyncing( |
| + syncable::ModelType type, |
| + const SyncDataList& initial_sync_data, |
| + SyncChangeProcessor* sync_processor) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK_EQ(type, syncable::EXTENSION_SETTINGS); |
| + DCHECK(!sync_processor_); |
| + sync_processor_ = sync_processor; |
| + |
| + // Group the initial sync data by extension id. |
| + std::map<std::string, linked_ptr<DictionaryValue> > grouped_sync_data; |
| + for (SyncDataList::const_iterator it = initial_sync_data.begin(); |
| + it != initial_sync_data.end(); ++it) { |
| + ExtensionSettingSyncData data(*it); |
| + if (!data.value()) { |
| + LOG(WARNING) << "NULL value in sync data"; |
|
akalin
2011/09/15 19:56:44
prefer
LOG(WARNING) << "Could not parse setting:
not at google - send to devlin
2011/09/16 05:18:59
Changing Value* to Value& made this moot. Which i
|
| + continue; |
| + } |
| + linked_ptr<DictionaryValue> sync_data = |
| + grouped_sync_data[data.extension_id()]; |
| + if (!sync_data.get()) { |
| + sync_data = linked_ptr<DictionaryValue>(new DictionaryValue()); |
| + grouped_sync_data[data.extension_id()] = sync_data; |
| + } |
| + DCHECK(!sync_data->HasKey(data.key())) << |
| + "Duplicate settings for " << data.extension_id() << "/" << data.key(); |
| + sync_data->Set(data.key(), data.value()->DeepCopy()); |
| + } |
| + |
| + // Start syncing all existing storage areas. Any storage areas created in |
| + // the future will start being synced as part of the creation process. |
| + for (std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it = |
| + storage_objs_.begin(); it != storage_objs_.end(); ++it) { |
| + linked_ptr<DictionaryValue> sync_data = grouped_sync_data[it->first]; |
|
akalin
2011/09/16 15:36:40
prefer using group_sync_data.find() so that you do
not at google - send to devlin
2011/09/19 07:10:46
Done.
|
| + if (sync_data.get()) { |
| + it->second->StartSyncing(*sync_data, sync_processor); |
| + } else { |
|
akalin
2011/09/15 19:56:44
from the logic above, I don't think sync_data shou
not at google - send to devlin
2011/09/16 05:18:59
I think it can be NULL if sync data exists for sto
akalin
2011/09/16 15:36:40
Ah, I see, right.
|
| + DictionaryValue empty; |
| + it->second->StartSyncing(empty, sync_processor); |
| + } |
| + grouped_sync_data.erase(it->first); |
|
akalin
2011/09/15 19:56:44
no need for this, and in fact it's dangerous given
not at google - send to devlin
2011/09/16 05:18:59
Should be safe, I'm incrementing an iterator to st
akalin
2011/09/16 15:36:40
My mistake, I didn't see you were iterating over s
not at google - send to devlin
2011/09/19 07:10:46
Done.
|
| + } |
| + |
| + // Eagerly create and init the rest of the storage areas that have sync data. |
| + // Under normal circumstances (i.e. not first-time sync) this will be all of |
| + // them. |
| + for (std::map<std::string, linked_ptr<DictionaryValue> >::iterator it = |
| + grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { |
| + GetOrCreateStorageWithSyncData(it->first, *it->second); |
| + } |
| + |
| + return SyncError(); |
| +} |
| + |
| +SyncError ExtensionSettings::ProcessSyncChanges( |
| + const tracked_objects::Location& from_here, |
| + const SyncChangeList& sync_changes) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK(sync_processor_); |
| + |
| + // Group changes by extension, to pass all changes in a single method call. |
| + std::map<std::string, ExtensionSettingSyncDataList> grouped_sync_data; |
| + for (SyncChangeList::const_iterator it = sync_changes.begin(); |
| + it != sync_changes.end(); ++it) { |
| + ExtensionSettingSyncData data(*it); |
| + if (!data.value()) { |
| + LOG(WARNING) << "NULL value in sync data"; |
|
akalin
2011/09/15 19:56:44
revamp error message as above (possibly decomp int
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| + continue; |
| + } |
| + grouped_sync_data[data.extension_id()].push_back(data); |
| + } |
| + |
| + std::map<std::string, ExtensionSettingSyncDataList>::iterator it; |
|
akalin
2011/09/15 19:56:44
move into for loop
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| + DictionaryValue empty; |
| + for (it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { |
| + GetOrCreateStorageWithSyncData(it->first, empty)-> |
| + ProcessSyncChanges(it->second); |
| + } |
| + |
| + return SyncError(); |
| +} |
| + |
| +void ExtensionSettings::StopSyncing(syncable::ModelType type) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + DCHECK(sync_processor_); |
| + sync_processor_ = NULL; |
| + |
| + std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; |
|
akalin
2011/09/15 19:56:44
fold into for loop
not at google - send to devlin
2011/09/16 05:18:59
Done.
|
| + for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { |
| + it->second->StopSyncing(); |
| + } |
| } |