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..7a2029edfbe49a304ffd64b6366e0b4291947f02 |
--- /dev/null |
+++ b/chrome/browser/extensions/extension_settings_storage_cache.cc |
@@ -0,0 +1,265 @@ |
+// 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) { |
+ } |
+ |
+ static CacheModifyingCallback* Create( |
+ ExtensionSettingsStorage::Callback* delegate, |
+ base::WeakPtr<DictionaryValue> cache_ptr_) { |
+ return new CacheModifyingCallback( |
+ delegate, |
+ cache_ptr_, |
+ NULL, |
+ NULL); |
+ } |
+ |
+ static CacheModifyingCallback* Create( |
+ ExtensionSettingsStorage::Callback* delegate, |
+ base::WeakPtr<DictionaryValue> cache_ptr_, |
+ DictionaryValue* existing) { |
+ return new CacheModifyingCallback( |
+ delegate, |
+ cache_ptr_, |
+ existing, |
+ NULL); |
+ } |
+ |
+ static CacheModifyingCallback* Create( |
+ ExtensionSettingsStorage::Callback* delegate, |
+ base::WeakPtr<DictionaryValue> cache_ptr_, |
+ ListValue* remove) { |
+ return new CacheModifyingCallback( |
+ delegate, |
+ cache_ptr_, |
+ 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. |
+ // Also, settings may be NULL for Remove/Clear. |
+ if (settings != NULL && cache_.get() != NULL) { |
+ cache_->MergeDictionary(settings); |
+ } |
+ if (settings != NULL && 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_; |
+}; |
+ |
+} // namespace |
+ |
+ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( |
+ ExtensionSettingsStorage* delegate) |
+ : delegate_(delegate), cache_ptr_factory_(&cache_) { |
+} |
+ |
+ExtensionSettingsStorageCache::~ExtensionSettingsStorageCache() { |
+} |
+ |
+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); |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &ExtensionSettingsStorageCache::Callback::OnSuccess, |
+ base::Unretained(callback), |
+ settings)); |
+ } else { |
+ delegate_->Get( |
+ key, |
+ CacheModifyingCallback::Create( |
+ callback, |
+ cache_ptr_factory_.GetWeakPtr())); |
+ } |
+} |
+ |
+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()) { |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &ExtensionSettingsStorageCache::Callback::OnSuccess, |
+ base::Unretained(callback), |
+ settings)); |
+ } else { |
+ delegate_->Get( |
+ missing_keys, |
+ CacheModifyingCallback::Create( |
+ callback, |
+ cache_ptr_factory_.GetWeakPtr(), |
+ 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()). |
+ delegate_->Get( |
+ CacheModifyingCallback::Create( |
+ callback, |
+ cache_ptr_factory_.GetWeakPtr(), |
+ 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_.GetWeakPtr())); |
+} |
+ |
+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_.GetWeakPtr())); |
+} |
+ |
+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_.GetWeakPtr(), |
+ 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_.GetWeakPtr(), |
+ keys.DeepCopy())); |
+} |
+ |
+void ExtensionSettingsStorageCache::Clear( |
+ ExtensionSettingsStorageCache::Callback* callback) { |
+ cache_.Clear(); |
+ delegate_->Clear( |
+ CacheModifyingCallback::Create( |
+ callback, |
+ cache_ptr_factory_.GetWeakPtr())); |
+} |
+ |
+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; |
+} |