Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2145)

Unified Diff: chrome/browser/extensions/extension_settings.cc

Issue 7775008: Enable sync for the settings from the Extension Settings API. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Review #1 Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();
+ }
}

Powered by Google App Engine
This is Rietveld 408576698