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