| Index: chrome/browser/extensions/extension_settings.cc
|
| diff --git a/chrome/browser/extensions/extension_settings.cc b/chrome/browser/extensions/extension_settings.cc
|
| index a35fb5bf81bfa1a3a60e61137b8deb153839203f..b0bdc87f71858f0c95cf42b6e23358774824cdfd 100644
|
| --- a/chrome/browser/extensions/extension_settings.cc
|
| +++ b/chrome/browser/extensions/extension_settings.cc
|
| @@ -13,15 +13,16 @@
|
| #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/syncable_extension_settings_storage.h"
|
| #include "third_party/leveldb/include/leveldb/iterator.h"
|
| #include "third_party/leveldb/include/leveldb/write_batch.h"
|
|
|
| ExtensionSettings::ExtensionSettings(const FilePath& base_path)
|
| - : base_path_(base_path) {
|
| + : base_path_(base_path), 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) {
|
| it->second->DeleteSoon();
|
| }
|
| @@ -52,14 +53,14 @@ void ExtensionSettings::GetStorageForTesting(
|
|
|
| bool ExtensionSettings::GetExistingStorage(
|
| const std::string& extension_id, const Callback& callback) {
|
| - std::map<std::string, ExtensionSettingsStorage*>::iterator existing =
|
| + std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing =
|
| storage_objs_.find(extension_id);
|
| if (existing == storage_objs_.end()) {
|
| // No existing storage.
|
| return false;
|
| }
|
| // Existing storage. Reply with that.
|
| - ExtensionSettingsStorage* storage = existing->second;
|
| + SyncableExtensionSettingsStorage* storage = existing->second;
|
| DCHECK(storage != NULL);
|
| MessageLoop::current()->PostTask(
|
| FROM_HERE,
|
| @@ -72,7 +73,7 @@ bool ExtensionSettings::GetExistingStorage(
|
| }
|
|
|
| void ExtensionSettings::RunWithStorage(
|
| - Callback* callback, ExtensionSettingsStorage* storage) {
|
| + Callback* callback, SyncableExtensionSettingsStorage* storage) {
|
| callback->Run(storage);
|
| delete callback;
|
| }
|
| @@ -103,7 +104,8 @@ void ExtensionSettings::CreateStorageOnFileThread(
|
| bool cached,
|
| const Callback& callback) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| - ExtensionSettingsStorage* storage = CreateStorage(extension_id, type, cached);
|
| + SyncableExtensionSettingsStorage* storage =
|
| + CreateStorage(extension_id, type, cached);
|
| if (storage == NULL && fallback_type != type) {
|
| storage = CreateStorage(extension_id, fallback_type, cached);
|
| }
|
| @@ -119,7 +121,7 @@ void ExtensionSettings::CreateStorageOnFileThread(
|
| callback));
|
| }
|
|
|
| -ExtensionSettingsStorage* ExtensionSettings::CreateStorage(
|
| +SyncableExtensionSettingsStorage* ExtensionSettings::CreateStorage(
|
| const std::string& extension_id,
|
| ExtensionSettingsStorage::Type type,
|
| bool cached) {
|
| @@ -136,20 +138,26 @@ ExtensionSettingsStorage* ExtensionSettings::CreateStorage(
|
| default:
|
| NOTREACHED();
|
| }
|
| - if (storage != NULL && cached) {
|
| + if (storage == NULL) {
|
| + return NULL;
|
| + }
|
| + if (cached) {
|
| storage = new ExtensionSettingsStorageCache(storage);
|
| }
|
| - return storage;
|
| + // Always send changes to sync. The merge algorithm is such that local data
|
| + // will be overwritten by sync data, so even if it's purely in-memory storage
|
| + // the settings will be consistent across browser restarts.
|
| + return new SyncableExtensionSettingsStorage(extension_id, storage);
|
| }
|
|
|
| void ExtensionSettings::EndCreationOfStorage(
|
| const std::string& extension_id,
|
| - ExtensionSettingsStorage* storage,
|
| + SyncableExtensionSettingsStorage* storage,
|
| const Callback& callback) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| // Cache the result now. To avoid a race condition, check again to see
|
| // whether a storage has been created already; if so, use that one.
|
| - std::map<std::string, ExtensionSettingsStorage*>::iterator existing =
|
| + std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing =
|
| storage_objs_.find(extension_id);
|
| if (existing == storage_objs_.end()) {
|
| storage_objs_[extension_id] = storage;
|
| @@ -158,5 +166,148 @@ void ExtensionSettings::EndCreationOfStorage(
|
| storage = existing->second;
|
| DCHECK(storage != NULL);
|
| }
|
| + if (sync_processor_ != NULL) {
|
| + StartSyncingStorage(extension_id, storage);
|
| + }
|
| callback.Run(storage);
|
| }
|
| +
|
| +SyncDataList ExtensionSettings::GetAllSyncData(
|
| + syncable::ModelType type) const {
|
| + // Not possible to do this synchronously, or while satisfying const, but only
|
| + // used for debugging so fine to ignore.
|
| + return SyncDataList();
|
| +}
|
| +
|
| +SyncError ExtensionSettings::MergeDataAndStartSyncing(
|
| + syncable::ModelType type,
|
| + const SyncDataList& initial_sync_data,
|
| + SyncChangeProcessor* sync_processor) {
|
| + DCHECK(type == syncable::EXTENSION_SETTINGS);
|
| + DCHECK(sync_processor_ == NULL);
|
| + sync_processor_ = sync_processor;
|
| +
|
| + // Unmerged sync data could conceivably be non-empty if this method is called
|
| + // in quick succession, before the storage creation methods return (clearing
|
| + // the data). Clearing the unmerged data here is safe, it will just get
|
| + // repopulated with the new and correct data.
|
| + std::map<std::string, DictionaryValue*>::iterator unmerged_it;
|
| + for (unmerged_it = unmerged_sync_data_.begin();
|
| + unmerged_it != unmerged_sync_data_.end(); ++unmerged_it) {
|
| + delete unmerged_it->second;
|
| + }
|
| + unmerged_sync_data_.clear();
|
| +
|
| + // Merge algorithm when starting to sync: for each extension, if there are no
|
| + // sycned settings yet, push local settings to sync. Otherwise, completely
|
| + // overwrite local settings with those from sync.
|
| + //
|
| + // Firstly group the initial data per extension.
|
| + for (SyncDataList::const_iterator it = initial_sync_data.begin();
|
| + it != initial_sync_data.end(); ++it) {
|
| + ExtensionSettingsSyncData data(*it);
|
| + if (data.value() == NULL) {
|
| + LOG(WARNING) << "NULL value in sync data";
|
| + continue;
|
| + }
|
| + DictionaryValue* sync_data = unmerged_sync_data_[data.extension_id()];
|
| + if (sync_data == NULL) {
|
| + sync_data = new DictionaryValue();
|
| + unmerged_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());
|
| + }
|
| +
|
| + // Immediately push sync state to local for existing storage areas.
|
| + std::map<std::string, SyncableExtensionSettingsStorage*>::iterator storage_it;
|
| + for (storage_it = storage_objs_.begin();
|
| + storage_it != storage_objs_.end(); ++storage_it) {
|
| + StartSyncingStorage(storage_it->first, storage_it->second);
|
| + }
|
| +
|
| + // Asynchronously push sync state to local for the remaining settings.
|
| + for (unmerged_it = unmerged_sync_data_.begin();
|
| + unmerged_it != unmerged_sync_data_.end(); ++unmerged_it) {
|
| + // StartSyncingStorage will be called from EndCreationOfStorage, so the
|
| + // actual callback from getting the storage doesn't need to do anything.
|
| + GetStorage(
|
| + unmerged_it->first,
|
| + base::Bind(
|
| + &ExtensionSettings::AssertNoSyncData,
|
| + this,
|
| + unmerged_it->first));
|
| + }
|
| +
|
| + return SyncError();
|
| +}
|
| +
|
| +void ExtensionSettings::AssertNoSyncData(
|
| + const std::string& extension_id,
|
| + SyncableExtensionSettingsStorage* storage) {
|
| + DCHECK_EQ(0u, unmerged_sync_data_.count(extension_id));
|
| +}
|
| +
|
| +void ExtensionSettings::StartSyncingStorage(
|
| + const std::string& extension_id,
|
| + SyncableExtensionSettingsStorage* storage) {
|
| + DCHECK(sync_processor_ != NULL);
|
| + scoped_ptr<DictionaryValue> sync_data(unmerged_sync_data_[extension_id]);
|
| + unmerged_sync_data_.erase(extension_id);
|
| + if (sync_data.get() == NULL) {
|
| + DictionaryValue no_data;
|
| + storage->StartSyncing(no_data, sync_processor_);
|
| + } else {
|
| + storage->StartSyncing(*sync_data, sync_processor_);
|
| + }
|
| +}
|
| +
|
| +static void CallProcessSyncChanges(
|
| + ExtensionSettingsSyncDataList* sync_changes,
|
| + SyncableExtensionSettingsStorage* storage) {
|
| + storage->ProcessSyncChanges(*sync_changes);
|
| + delete sync_changes;
|
| +}
|
| +
|
| +SyncError ExtensionSettings::ProcessSyncChanges(
|
| + const tracked_objects::Location& from_here,
|
| + const SyncChangeList& sync_changes) {
|
| + DCHECK(sync_processor_ != NULL);
|
| +
|
| + // Gather changes per extension, so that all the processing can be done from a
|
| + // single GetStorage() call.
|
| + std::map<std::string, ExtensionSettingsSyncDataList> all_sync_data;
|
| + for (SyncChangeList::const_iterator it = sync_changes.begin();
|
| + it != sync_changes.end(); ++it) {
|
| + ExtensionSettingsSyncData data(*it);
|
| + if (data.value() == NULL) {
|
| + LOG(WARNING) << "NULL value in sync data";
|
| + continue;
|
| + }
|
| + all_sync_data[data.extension_id()].push_back(data);
|
| + }
|
| +
|
| + // Push changes to storage areas.
|
| + std::map<std::string, ExtensionSettingsSyncDataList>::iterator it;
|
| + for (it = all_sync_data.begin(); it != all_sync_data.end(); ++it) {
|
| + GetStorage(
|
| + it->first,
|
| + base::Bind(
|
| + &CallProcessSyncChanges,
|
| + new ExtensionSettingsSyncDataList(it->second)));
|
| + }
|
| +
|
| + return SyncError();
|
| +}
|
| +
|
| +void ExtensionSettings::StopSyncing(syncable::ModelType type) {
|
| + 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();
|
| + }
|
| +}
|
|
|