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(); |
+ } |
+} |