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..72c53ed072fb8e92438d4e07a56264f75f7da8dd 100644 |
--- a/chrome/browser/extensions/extension_settings.cc |
+++ b/chrome/browser/extensions/extension_settings.cc |
@@ -9,71 +9,93 @@ |
#include "base/json/json_writer.h" |
#include "base/logging.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) {} |
ExtensionSettings::~ExtensionSettings() { |
- std::map<std::string, ExtensionSettingsStorage*>::iterator it; |
+ std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; |
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; |
+ 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 == NULL) { |
+ // Fall back to an in-memory storage area (cached NOOP). |
+ storage = GetOrCreateAndInitStorage( |
+ ExtensionSettingsStorage::NOOP, |
+ true, |
+ extension_id, |
+ sync_data); |
+ DCHECK(storage != NULL); |
} |
- |
- 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 != NULL); |
+ 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); |
ExtensionSettingsStorage* storage = NULL; |
switch (type) { |
case ExtensionSettingsStorage::NOOP: |
@@ -86,8 +108,197 @@ ExtensionSettingsStorage* ExtensionSettings::CreateStorage( |
default: |
NOTREACHED(); |
} |
- if (storage != NULL && cached) { |
+ if (storage == NULL) { |
+ return NULL; |
+ } |
+ if (cached) { |
storage = new ExtensionSettingsStorageCache(storage); |
} |
- return storage; |
+ |
+ SyncableExtensionSettingsStorage* synced_storage = |
+ new SyncableExtensionSettingsStorage(extension_id, storage); |
+ if (sync_processor_ != NULL) { |
+ synced_storage->StartSyncing(initial_sync_data, sync_processor_); |
+ } |
+ storage_objs_[extension_id] = synced_storage; |
+ return synced_storage; |
+} |
+ |
+SyncDataList ExtensionSettings::GetAllSyncData( |
+ syncable::ModelType type) const { |
+ if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
akalin
2011/09/14 05:57:29
ExtensionSettings lives on the FILE thread right?
not at google - send to devlin
2011/09/14 20:23:57
Done.
|
+ LOG(WARNING) << "Can't synchronously get extension settings unless on " << |
+ "the FILE thread"; |
+ return SyncDataList(); |
+ } |
+ DCHECK(type == syncable::EXTENSION_SETTINGS); |
+ DCHECK(extension_service_ != NULL); |
+ |
+ // 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; |
+ const ExtensionList* extensions = extension_service_->extensions(); |
+ for (ExtensionList::const_iterator it = extensions->begin(); |
+ 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; |
+ settings->GetWithoutPathExpansion(*key_it, &value); |
+ all_sync_data.push_back( |
+ extension_settings_sync_util::CreateData( |
+ extension_id, *key_it, *value)); |
+ } |
+ } |
+ |
+ return all_sync_data; |
+} |
+ |
+SyncError ExtensionSettings::MergeDataAndStartSyncing( |
+ syncable::ModelType type, |
+ const SyncDataList& initial_sync_data, |
+ SyncChangeProcessor* sync_processor) { |
+ if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
+ MergeDataAndStartSyncing2(type, initial_sync_data, sync_processor); |
+ } else { |
+ BrowserThread::PostTask( |
akalin
2011/09/14 05:57:29
Remove this and DCHECK that you're on FILE thread.
not at google - send to devlin
2011/09/14 20:23:57
Done.
|
+ BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind( |
+ &ExtensionSettings::MergeDataAndStartSyncing2, |
+ this, |
+ type, |
+ initial_sync_data, |
+ sync_processor)); |
+ } |
+ return SyncError(); |
+} |
+ |
+void ExtensionSettings::MergeDataAndStartSyncing2( |
+ syncable::ModelType type, |
+ const SyncDataList& initial_sync_data, |
+ SyncChangeProcessor* sync_processor) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ DCHECK(type == syncable::EXTENSION_SETTINGS); |
+ DCHECK(sync_processor_ == NULL); |
+ sync_processor_ = sync_processor; |
+ |
+ // Group the initial sync data by extension id. |
+ std::map<std::string, 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() == NULL) { |
+ LOG(WARNING) << "NULL value in sync data"; |
+ continue; |
+ } |
+ DictionaryValue* sync_data = grouped_sync_data[data.extension_id()]; |
+ if (sync_data == NULL) { |
+ sync_data = 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. |
+ std::map<std::string, SyncableExtensionSettingsStorage*>::iterator storage_it; |
+ for (storage_it = storage_objs_.begin(); |
+ storage_it != storage_objs_.end(); ++storage_it) { |
+ scoped_ptr<DictionaryValue> sync_data(grouped_sync_data[storage_it->first]); |
+ if (sync_data.get() == NULL) { |
+ DictionaryValue empty; |
+ storage_it->second->StartSyncing(empty, sync_processor); |
+ } else { |
+ storage_it->second->StartSyncing(*sync_data, sync_processor); |
+ } |
+ grouped_sync_data.erase(storage_it->first); |
+ } |
+ |
+ // Eagerly create and init the rest of the storage areas that have sync data. |
+ // Under normal circumstances this will probably be all of them. |
+ std::map<std::string, DictionaryValue*>::iterator data_it; |
+ for (data_it = grouped_sync_data.begin(); |
+ data_it != grouped_sync_data.end(); ++data_it) { |
+ scoped_ptr<DictionaryValue> sync_data(data_it->second); |
+ GetOrCreateStorageWithSyncData(data_it->first, *sync_data); |
+ } |
+} |
+ |
+SyncError ExtensionSettings::ProcessSyncChanges( |
+ const tracked_objects::Location& from_here, |
+ const SyncChangeList& sync_changes) { |
+ if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
akalin
2011/09/14 05:57:29
Here, too
not at google - send to devlin
2011/09/14 20:23:57
Done.
|
+ ProcessSyncChanges2(from_here, sync_changes); |
+ } else { |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind( |
+ &ExtensionSettings::ProcessSyncChanges2, |
+ this, |
+ from_here, |
+ sync_changes)); |
+ } |
+ return SyncError(); |
+} |
+ |
+void ExtensionSettings::ProcessSyncChanges2( |
+ const tracked_objects::Location& from_here, |
+ const SyncChangeList& sync_changes) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ DCHECK(sync_processor_ != NULL); |
+ |
+ // 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() == NULL) { |
+ LOG(WARNING) << "NULL value in sync data"; |
+ continue; |
+ } |
+ grouped_sync_data[data.extension_id()].push_back(data); |
+ } |
+ |
+ std::map<std::string, ExtensionSettingSyncDataList>::iterator it; |
+ DictionaryValue empty; |
+ for (it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) { |
+ GetOrCreateStorageWithSyncData(it->first, empty)-> |
+ ProcessSyncChanges(it->second); |
+ } |
+} |
+ |
+void ExtensionSettings::StopSyncing(syncable::ModelType type) { |
+ if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
+ // Re-run ths method on the FILE thread. |
akalin
2011/09/14 05:57:29
DCHECK, etc.
not at google - send to devlin
2011/09/14 20:23:57
Done.
|
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind( |
+ &ExtensionSettings::StopSyncing, |
+ this, |
+ type)); |
+ return; |
+ } |
+ DCHECK(type == syncable::EXTENSION_SETTINGS); |
+ DCHECK(sync_processor_ != NULL); |
+ sync_processor_ = NULL; |
+ |
+ std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; |
+ for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { |
+ it->second->StopSyncing(); |
+ } |
} |