Index: chrome/browser/extensions/syncable_extension_settings_storage.cc |
diff --git a/chrome/browser/extensions/syncable_extension_settings_storage.cc b/chrome/browser/extensions/syncable_extension_settings_storage.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3c862d2552ebdd47eac6ec884370f139fd883b35 |
--- /dev/null |
+++ b/chrome/browser/extensions/syncable_extension_settings_storage.cc |
@@ -0,0 +1,389 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/extensions/syncable_extension_settings_storage.h" |
+ |
+#include "base/bind.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop.h" |
+#include "base/task.h" |
+#include "chrome/browser/extensions/extension_settings_sync_helper.h" |
+#include "chrome/browser/sync/api/sync_data.h" |
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h" |
+ |
+SyncableExtensionSettingsStorage::SyncDeps::SyncDeps( |
+ SyncChangeProcessor* processor) : processor_(processor) { |
+ DCHECK(processor != NULL); |
+} |
+ |
+SyncableExtensionSettingsStorage::SyncDeps::~SyncDeps() { |
+} |
+ |
+SyncableExtensionSettingsStorage::SyncableExtensionSettingsStorage( |
+ std::string extension_id, ExtensionSettingsStorage* delegate) |
+ : extension_id_(extension_id), delegate_(delegate), sync_deps_(NULL) {} |
+ |
+SyncableExtensionSettingsStorage::~SyncableExtensionSettingsStorage() {} |
+ |
+void SyncableExtensionSettingsStorage::Get( |
+ const std::string& key, ExtensionSettingsStorage::Callback* callback) { |
+ delegate_->Get(key, callback); |
+} |
+ |
+void SyncableExtensionSettingsStorage::Get( |
+ const ListValue& keys, ExtensionSettingsStorage::Callback* callback) { |
+ delegate_->Get(keys, callback); |
+} |
+ |
+void SyncableExtensionSettingsStorage::Get( |
+ ExtensionSettingsStorage::Callback* callback) { |
+ delegate_->Get(callback); |
+} |
+ |
+namespace { |
+ |
+class AddOrUpdateSyncCallback : public ExtensionSettingsStorage::Callback { |
+ public: |
+ AddOrUpdateSyncCallback( |
+ Callback* delegate, |
+ const std::string& extension_id, |
+ base::WeakPtr<SyncableExtensionSettingsStorage::SyncDeps> sync_deps) |
+ : delegate_(delegate), |
+ extension_id_(extension_id), |
+ sync_deps_(sync_deps) {} |
+ |
+ void OnSuccess(DictionaryValue* settings) OVERRIDE { |
+ if (!sync_deps_) { |
+ delegate_->OnSuccess(settings); |
+ return; |
+ } |
+ |
+ SyncChangeList sync_changes; |
+ for (DictionaryValue::key_iterator it = settings->begin_keys(); |
+ it != settings->end_keys(); ++it) { |
+ Value* value; |
+ settings->GetWithoutPathExpansion(*it, &value); |
+ if (sync_deps_->keys()->count(*it)) { |
+ // TODO Key is synced, send ACTION_UPDATE to sync. |
+ LOG(INFO) << "Pushing sync update to sync for " << *it; |
+ sync_changes.push_back(ExtensionSettingsSyncHelper::CreateUpdate( |
+ extension_id_, *it, *value)); |
+ } else { |
+ // TODO Key is not synced, send ACTION_ADD to sync. |
+ LOG(INFO) << "Pushing sync add to sync for " << *it; |
+ sync_changes.push_back(ExtensionSettingsSyncHelper::CreateAdd( |
+ extension_id_, *it, *value)); |
+ } |
+ } |
+ |
+ SyncError error = |
+ sync_deps_->processor()->ProcessSyncChanges(FROM_HERE, sync_changes); |
+ if (error.IsSet()) { |
+ LOG(WARNING) << "Failed to send changes to sync: " << error.message(); |
+ } else { |
+ // Sync was successful, record those keys as synced. |
+ sync_deps_->keys()->insert(settings->begin_keys(), settings->end_keys()); |
+ } |
+ |
+ delegate_->OnSuccess(settings); |
+ } |
+ |
+ void OnFailure(const std::string& error) { |
+ LOG(WARNING) << "Settings operation failed, not sending to sync: " << error; |
+ delegate_->OnFailure(error); |
+ } |
+ |
+ private: |
+ scoped_ptr<Callback> delegate_; |
+ std::string extension_id_; |
+ base::WeakPtr<SyncableExtensionSettingsStorage::SyncDeps> sync_deps_; |
+}; |
+ |
+} // namespace |
+ |
+void SyncableExtensionSettingsStorage::Set( |
+ const std::string& key, |
+ const Value& value, |
+ ExtensionSettingsStorage::Callback* callback) { |
+ if (sync_deps_ != NULL) { |
+ callback = new AddOrUpdateSyncCallback( |
+ callback, extension_id_, sync_deps_->AsWeakPtr()); |
+ } |
+ delegate_->Set(key, value, callback); |
+} |
+ |
+void SyncableExtensionSettingsStorage::Set( |
+ const DictionaryValue& values, |
+ ExtensionSettingsStorage::Callback* callback) { |
+ if (sync_deps_ != NULL) { |
+ callback = new AddOrUpdateSyncCallback( |
+ callback, extension_id_, sync_deps_->AsWeakPtr()); |
+ } |
+ delegate_->Set(values, callback); |
+} |
+ |
+namespace { |
+ |
+class RemoveFromSyncCallback : public ExtensionSettingsStorage::Callback { |
+ public: |
+ RemoveFromSyncCallback( |
+ Callback* delegate, |
+ const std::string& extension_id, |
+ // Ownership of keys_to_remove is taken. |
+ std::set<std::string>* keys_to_remove, |
+ base::WeakPtr<SyncableExtensionSettingsStorage::SyncDeps> sync_deps) |
+ : delegate_(delegate), |
+ extension_id_(extension_id), |
+ keys_to_remove_(keys_to_remove), |
+ sync_deps_(sync_deps) { |
+ DCHECK(!keys_to_remove->empty()); |
+ } |
+ |
+ void OnSuccess(DictionaryValue* settings) OVERRIDE { |
+ if (!sync_deps_) { |
+ delegate_->OnSuccess(settings); |
+ return; |
+ } |
+ |
+ SyncChangeList sync_changes; |
+ for (std::set<std::string>::iterator it = keys_to_remove_->begin(); |
+ it != keys_to_remove_->end(); ++it) { |
+ LOG(INFO) << |
+ "Pushing sync remove to sync for " << extension_id_ << "/" << *it; |
+ SyncChange sync_delete = |
+ ExtensionSettingsSyncHelper::CreateDelete(extension_id_, *it); |
+ sync_changes.push_back(sync_delete); |
+ } |
+ |
+ SyncError error = |
+ sync_deps_->processor()->ProcessSyncChanges(FROM_HERE, sync_changes); |
+ if (error.IsSet()) { |
+ LOG(WARNING) << "Failed to send changes to sync: " << error.message(); |
+ } else { |
+ // Sync was successful, record those keys as deleted from sync. |
+ sync_deps_->keys()->erase( |
+ keys_to_remove_->begin(), keys_to_remove_->end()); |
+ } |
+ |
+ delegate_->OnSuccess(settings); |
+ } |
+ |
+ void OnFailure(const std::string& error) { |
+ LOG(WARNING) << "Settings operation failed, not sending to sync: " << error; |
+ delegate_->OnFailure(error); |
+ } |
+ |
+ private: |
+ scoped_ptr<Callback> delegate_; |
+ std::string extension_id_; |
+ scoped_ptr<std::set<std::string> > keys_to_remove_; |
+ base::WeakPtr<SyncableExtensionSettingsStorage::SyncDeps> sync_deps_; |
+}; |
+ |
+} // namespace |
+ |
+void SyncableExtensionSettingsStorage::Remove( |
+ const std::string& key, ExtensionSettingsStorage::Callback* callback) { |
+ if (sync_deps_ != NULL) { |
+ std::set<std::string>* keys_to_remove = new std::set<std::string>(); |
+ keys_to_remove->insert(key); |
+ callback = new RemoveFromSyncCallback( |
+ callback, |
+ extension_id_, |
+ keys_to_remove, |
+ sync_deps_->AsWeakPtr()); |
+ } |
+ delegate_->Remove(key, callback); |
+} |
+ |
+void SyncableExtensionSettingsStorage::Remove( |
+ const ListValue& keys, ExtensionSettingsStorage::Callback* callback) { |
+ if (sync_deps_ != NULL) { |
+ std::set<std::string>* keys_to_remove = new std::set<std::string>(); |
+ for (ListValue::const_iterator it = keys.begin(); it != keys.end(); ++it) { |
+ std::string key_to_remove; |
+ if ((*it)->GetAsString(&key_to_remove)) { |
+ keys_to_remove->insert(key_to_remove); |
+ } |
+ } |
+ callback = new RemoveFromSyncCallback( |
+ callback, |
+ extension_id_, |
+ keys_to_remove, |
+ sync_deps_->AsWeakPtr()); |
+ } |
+ delegate_->Remove(keys, callback); |
+} |
+ |
+void SyncableExtensionSettingsStorage::Clear( |
+ ExtensionSettingsStorage::Callback* callback) { |
+ if (sync_deps_ != NULL) { |
+ callback = new RemoveFromSyncCallback( |
+ callback, |
+ extension_id_, |
+ // Remove all the synced keys. |
+ new std::set<std::string>(*sync_deps_->keys()), |
+ sync_deps_->AsWeakPtr()); |
+ } |
+ delegate_->Clear(callback); |
+} |
+ |
+void SyncableExtensionSettingsStorage::DeleteSoon() { |
+ delegate_->DeleteSoon(); |
+ delete this; |
+} |
+ |
+// Sync-related methods. |
+ |
+namespace { |
+ |
+class SendAllLocalSettingsToSync : public ExtensionSettingsStorage::Callback { |
+ public: |
+ SendAllLocalSettingsToSync( |
+ const std::string& extension_id, |
+ base::WeakPtr<SyncableExtensionSettingsStorage::SyncDeps> sync_deps) |
+ : extension_id_(extension_id), sync_deps_(sync_deps) {} |
+ |
+ virtual void OnSuccess(DictionaryValue* settings) OVERRIDE { |
+ scoped_ptr<DictionaryValue> settings_ownership(settings); |
+ if (!sync_deps_) { |
+ return; |
+ } |
+ |
+ SyncChangeList add_to_sync; |
+ for (DictionaryValue::key_iterator it = settings->begin_keys(); |
+ it != settings->end_keys(); ++it) { |
+ Value* value; |
+ settings->GetWithoutPathExpansion(*it, &value); |
+ add_to_sync.push_back(ExtensionSettingsSyncHelper::CreateAdd( |
+ extension_id_, *it, *value)); |
+ } |
+ |
+ SyncError error = |
+ sync_deps_->processor()->ProcessSyncChanges(FROM_HERE, add_to_sync); |
+ if (error.IsSet()) { |
+ LOG(WARNING) << "Failed to send changes to sync: " << error.message(); |
+ } else { |
+ // Sync was successful, record those keys as synced. |
+ sync_deps_->keys()->insert(settings->begin_keys(), settings->end_keys()); |
+ } |
+ } |
+ |
+ virtual void OnFailure(const std::string& error) OVERRIDE { |
+ LOG(WARNING) << "Settings operation failed, not sending to sync: " << error; |
+ } |
+ |
+ private: |
+ std::string extension_id_; |
+ base::WeakPtr<SyncableExtensionSettingsStorage::SyncDeps> sync_deps_; |
+}; |
+ |
+class OverwriteLocalSettingsCallback |
+ : public ExtensionSettingsStorage::Callback { |
+ public: |
+ OverwriteLocalSettingsCallback( |
+ // Ownership NOT taken. |
+ SyncableExtensionSettingsStorage* storage, |
+ // Ownership taken. |
+ DictionaryValue* sync_state) |
+ : storage_(storage), sync_state_(sync_state) {} |
+ |
+ virtual void OnSuccess(DictionaryValue* local_state) { |
+ scoped_ptr<DictionaryValue> local_state_ownership(local_state); |
+ // Pretend they're sync changes to process. |
+ ExtensionSettingsSyncDataList sync_changes; |
+ for (DictionaryValue::key_iterator it = local_state->begin_keys(); |
+ it != local_state->end_keys(); ++it) { |
+ Value* sync_value; |
+ if (sync_state_->RemoveWithoutPathExpansion(*it, &sync_value)) { |
+ Value* local_value; |
+ local_state->GetWithoutPathExpansion(*it, &local_value); |
+ if (!local_value->Equals(sync_value)) { |
+ // Sync value is different, update locally with new value. |
+ sync_changes.push_back(ExtensionSettingsSyncData( |
+ SyncChange::ACTION_UPDATE, "", *it, sync_value)); |
+ } |
+ } else { |
+ // No sync value, delete locally. |
+ sync_changes.push_back(ExtensionSettingsSyncData( |
+ SyncChange::ACTION_DELETE, "", *it, new DictionaryValue())); |
+ } |
+ } |
+ |
+ for (DictionaryValue::key_iterator it = sync_state_->begin_keys(); |
+ it != sync_state_->end_keys(); ++it) { |
+ Value* sync_value; |
+ // Missing local value for synced setting, add locally. |
+ // If it's safe to RemoveWithoutPathExpansion the DeepCopy would be |
+ // unnecessary. |
+ sync_state_->GetWithoutPathExpansion(*it, &sync_value); |
+ sync_changes.push_back(ExtensionSettingsSyncData( |
+ SyncChange::ACTION_ADD, "", *it, sync_value->DeepCopy())); |
+ } |
+ |
+ if (!sync_changes.empty()) { |
+ storage_->ProcessSyncChanges(sync_changes); |
+ } |
+ } |
+ |
+ virtual void OnFailure(const std::string& error) { |
+ LOG(WARNING) << |
+ "Settings operation failed, local state not in sync: " << error; |
+ } |
+ |
+ private: |
+ // XXX(kalman): weak pointer or refcount or ughhhhh. |
+ SyncableExtensionSettingsStorage* storage_; |
+ scoped_ptr<DictionaryValue> sync_state_; |
+}; |
+ |
+} // namespace |
+ |
+void SyncableExtensionSettingsStorage::StartSyncing( |
+ const DictionaryValue& sync_state, SyncChangeProcessor* sync_processor) { |
+ DCHECK(sync_deps_ == NULL); |
+ sync_deps_ = new SyncDeps(sync_processor); |
+ sync_deps_->keys()->insert(sync_state.begin_keys(), sync_state.end_keys()); |
+ |
+ // Push local state to sync if there is no sync data yet, otherwise overwrite |
+ // local state with sync state (via a callback). |
+ if (sync_state.empty()) { |
+ Get(new SendAllLocalSettingsToSync(extension_id_, sync_deps_->AsWeakPtr())); |
+ } else { |
+ Get(new OverwriteLocalSettingsCallback(this, sync_state.DeepCopy())); |
+ } |
+} |
+ |
+void SyncableExtensionSettingsStorage::StopSyncing() { |
+ DCHECK(sync_deps_ != NULL); |
+ delete sync_deps_; |
+ sync_deps_ = NULL; |
+} |
+ |
+void SyncableExtensionSettingsStorage::ProcessSyncChanges( |
+ const ExtensionSettingsSyncDataList& sync_changes) { |
+ DCHECK(sync_deps_ != NULL); |
+ DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_; |
+ for (ExtensionSettingsSyncDataList::const_iterator it = sync_changes.begin(); |
+ it != sync_changes.end(); ++it) { |
+ switch (it->change_type()) { |
+ case SyncChange::ACTION_ADD: |
+ case SyncChange::ACTION_UPDATE: |
+ sync_deps_->keys()->insert(it->key()); |
+ delegate_->Set( |
+ it->key(), |
+ *it->value(), |
+ new ExtensionSettingsStorage::NoopCallback()); |
+ break; |
+ case SyncChange::ACTION_DELETE: |
+ sync_deps_->keys()->erase(it->key()); |
+ delegate_->Remove( |
+ it->key(), |
+ new ExtensionSettingsStorage::NoopCallback()); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+ } |
+} |