Chromium Code Reviews| Index: chrome/browser/extensions/extension_settings_storage_cache.cc |
| diff --git a/chrome/browser/extensions/extension_settings_storage_cache.cc b/chrome/browser/extensions/extension_settings_storage_cache.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6954f305bce0eeaaa17b13e490935640e3d9f327 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/extension_settings_storage_cache.cc |
| @@ -0,0 +1,268 @@ |
| +// 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/extension_settings_storage_cache.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/message_loop.h" |
| +#include "base/task.h" |
| +#include "base/json/json_writer.h" |
| + |
| +namespace { |
| + |
| +void RemoveAll(DictionaryValue* from, const ListValue* keys_to_remove) { |
| + std::string key; |
| + for (ListValue::const_iterator it = keys_to_remove->begin(); |
| + it != keys_to_remove->end(); ++it) { |
| + if ((*it)->GetAsString(&key)) { |
| + from->Remove(key, NULL); |
| + } |
| + } |
| +} |
| + |
| +// Callback which delegates to a given callback but caches any result if |
| +// successful. Takes an optional parameter of existing settings to merge with |
| +// before returning to the callback (may be NULL) and an optional parameter of |
| +// keys to remove from the cache post-success (may be NULL). |
| +class CacheModifyingCallback : public ExtensionSettingsStorage::Callback { |
| + public: |
| + // Takes ownership of callback, existing, and keys_to_remove. |
| + CacheModifyingCallback( |
| + ExtensionSettingsStorage::Callback* delegate, |
| + base::WeakPtr<DictionaryValue> cache, |
| + DictionaryValue* existing, |
| + ListValue* keys_to_remove) |
| + : delegate_(delegate), |
| + cache_(cache), |
| + existing_(existing), |
| + keys_to_remove_(keys_to_remove) { |
| + } |
| + |
| + ~CacheModifyingCallback() {} |
| + |
| + // TODO(kalman): shouldn't expose the factory here. just the weakptr. |
| + static CacheModifyingCallback* Create( |
| + ExtensionSettingsStorage::Callback* delegate, |
| + base::WeakPtrFactory<DictionaryValue>& cache_ptr_factory) { |
| + return new CacheModifyingCallback( |
| + delegate, |
| + cache_ptr_factory.GetWeakPtr(), |
| + NULL, |
| + NULL); |
| + } |
| + |
| + static CacheModifyingCallback* Create( |
| + ExtensionSettingsStorage::Callback* delegate, |
| + base::WeakPtrFactory<DictionaryValue>& cache_ptr_factory, |
| + DictionaryValue* existing) { |
| + return new CacheModifyingCallback( |
| + delegate, |
| + cache_ptr_factory.GetWeakPtr(), |
| + existing, |
| + NULL); |
| + } |
| + |
| + static CacheModifyingCallback* Create( |
| + ExtensionSettingsStorage::Callback* delegate, |
| + base::WeakPtrFactory<DictionaryValue>& cache_ptr_factory, |
| + ListValue* remove) { |
| + return new CacheModifyingCallback( |
| + delegate, |
| + cache_ptr_factory.GetWeakPtr(), |
| + NULL, |
| + remove); |
| + } |
| + |
| + virtual void OnSuccess(DictionaryValue* settings) OVERRIDE { |
| + // Note checking that the weak reference to the cache is still valid. |
| + // It might be NULL if the owning ExtensionSettingsStorageCache has been |
| + // deleted. |
| + if (cache_.get() != NULL) { |
| + cache_->MergeDictionary(settings); |
| + } |
| + if (existing_ != NULL) { |
| + settings->MergeDictionary(existing_.get()); |
| + } |
| + if (cache_.get() != NULL && keys_to_remove_ != NULL) { |
| + RemoveAll(cache_, keys_to_remove_.get()); |
| + } |
| + delegate_->OnSuccess(settings); |
| + } |
| + |
| + virtual void OnFailure(const std::string& message) OVERRIDE { |
| + delegate_->OnFailure(message); |
| + } |
| + |
| + private: |
| + scoped_ptr<ExtensionSettingsStorage::Callback> delegate_; |
| + base::WeakPtr<DictionaryValue> cache_; |
| + scoped_ptr<DictionaryValue> existing_; |
| + scoped_ptr<ListValue> keys_to_remove_; |
| +}; |
| + |
| +// Runs OnSuccess() on the message loop of the calling thread. |
| +// TODO(kalman): don't make this a whole class. unnecessary. |
| +class SuccessClosure { |
| + public: |
| + SuccessClosure( |
| + ExtensionSettingsStorage::Callback* callback, DictionaryValue* settings) |
| + : callback_(callback), settings_(settings) { |
| + } |
| + |
| + ~SuccessClosure() {} |
| + |
| + void Run() { |
| + MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&SuccessClosure::Run2, base::Unretained(this))); |
| + } |
| + |
| + private: |
| + void Run2() { |
| + callback_->OnSuccess(settings_); |
| + delete this; |
| + } |
| + |
| + scoped_ptr<ExtensionSettingsStorage::Callback> callback_; |
| + DictionaryValue* settings_; |
| +}; |
| + |
| +} // namespace |
| + |
| +ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( |
| + ExtensionSettingsStorage* delegate) |
| + : delegate_(delegate), cache_ptr_factory_(&cache_) { |
| +} |
| + |
| +void ExtensionSettingsStorageCache::DeleteSoon() { |
| + delegate_->DeleteSoon(); |
| + delete this; |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Get(const std::string& key, |
| + ExtensionSettingsStorageCache::Callback* callback) { |
| + Value *value; |
| + if (GetFromCache(key, &value)) { |
| + DictionaryValue* settings = new DictionaryValue(); |
| + settings->Set(key, value); |
| + (new SuccessClosure(callback, settings))->Run(); |
| + } else { |
| + delegate_->Get( |
| + key, |
| + CacheModifyingCallback::Create(callback, cache_ptr_factory_)); |
|
Matt Perry
2011/06/29 18:08:11
you can just pass in cache_ptr_factory_.GetWeakPtr
not at google - send to devlin
2011/08/03 06:36:51
Done.
|
| + } |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Get(const ListValue& keys, |
| + ExtensionSettingsStorageCache::Callback* callback) { |
| + std::string key; |
| + DictionaryValue* settings = new DictionaryValue(); |
| + ListValue missing_keys; |
| + |
| + for (ListValue::const_iterator it = keys.begin(); it != keys.end(); ++it) { |
| + if ((*it)->GetAsString(&key)) { |
| + Value *value; |
| + if (GetFromCache(key, &value)) { |
| + settings->Set(key, value); |
| + } else { |
| + missing_keys.Append(Value::CreateStringValue(key)); |
| + } |
| + } |
| + } |
| + |
| + if (missing_keys.empty()) { |
| + (new SuccessClosure(callback, settings))->Run(); |
| + } else { |
| + delegate_->Get( |
| + missing_keys, |
| + CacheModifyingCallback::Create(callback, cache_ptr_factory_, settings)); |
| + } |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Get( |
| + ExtensionSettingsStorageCache::Callback* callback) { |
| + // Copy the cache when passing in, as a semi-hack so that caching a no-op |
| + // storage object works (which always returns empty from Get()). |
| + // TODO(kalman): Consider doing this conditionally on type == NOOP. |
| + delegate_->Get( |
| + CacheModifyingCallback::Create( |
| + callback, |
| + cache_ptr_factory_, |
| + cache_.DeepCopy())); |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Set( |
| + const std::string& key, |
| + const Value& value, |
| + ExtensionSettingsStorageCache::Callback* callback) { |
| + // Invalidate the cached entry first, in case the set fails. |
| + cache_.Remove(key, NULL); |
| + delegate_->Set( |
| + key, |
| + value, |
| + CacheModifyingCallback::Create(callback, cache_ptr_factory_)); |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Set( |
| + const DictionaryValue& values, |
| + ExtensionSettingsStorageCache::Callback* callback) { |
| + // Invalidate the cached entries first, in case the set fails. |
| + for (DictionaryValue::key_iterator it = values.begin_keys(); |
| + it != values.end_keys(); ++it) { |
| + cache_.RemoveWithoutPathExpansion(*it, NULL); |
| + } |
| + delegate_->Set( |
| + values, |
| + CacheModifyingCallback::Create(callback, cache_ptr_factory_)); |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Remove( |
| + const std::string& key, |
| + ExtensionSettingsStorageCache::Callback *callback) { |
| + // Invalidate the cached entry first, in case the remove fails. |
| + // We will also need to do if after the callback, to avoid race conditions |
| + // whether other API calls fill the cache on the UI thread. |
| + // This would be a good time to use structured cloning... |
| + cache_.Remove(key, NULL); |
| + ListValue* key_list = new ListValue(); |
| + key_list->Append(Value::CreateStringValue(key)); |
| + delegate_->Remove( |
| + key, |
| + CacheModifyingCallback::Create(callback, cache_ptr_factory_, key_list)); |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Remove( |
| + const ListValue& keys, |
| + ExtensionSettingsStorageCache::Callback *callback) { |
| + std::string key; |
| + // Invalidate each cached entry first, in case the remove fails. |
| + // We will also need to do if after the callback, to avoid race conditions |
| + // whether other API calls fill the cache on the UI thread. |
| + RemoveAll(&cache_, &keys); |
| + delegate_->Remove( |
| + keys, |
| + CacheModifyingCallback::Create( |
| + callback, |
| + cache_ptr_factory_, |
| + keys.DeepCopy())); |
| +} |
| + |
| +void ExtensionSettingsStorageCache::Clear( |
| + ExtensionSettingsStorageCache::Callback* callback) { |
| + cache_.Clear(); |
| + delegate_->Clear( |
| + CacheModifyingCallback::Create(callback, cache_ptr_factory_)); |
| +} |
| + |
| +bool ExtensionSettingsStorageCache::GetFromCache( |
| + const std::string& key, Value** value) { |
| + Value* cached_value; |
| + if (!cache_.Get(key, &cached_value)) { |
| + return false; |
| + } |
| + *value = cached_value->DeepCopy(); |
| + return true; |
| +} |