OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/extensions/extension_settings_storage_cache.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/task.h" |
| 11 #include "base/json/json_writer.h" |
| 12 |
| 13 namespace { |
| 14 |
| 15 void RemoveAll(DictionaryValue* from, const ListValue* keys_to_remove) { |
| 16 std::string key; |
| 17 for (ListValue::const_iterator it = keys_to_remove->begin(); |
| 18 it != keys_to_remove->end(); ++it) { |
| 19 if ((*it)->GetAsString(&key)) { |
| 20 from->Remove(key, NULL); |
| 21 } |
| 22 } |
| 23 } |
| 24 |
| 25 // Callback which delegates to a given callback but caches any result if |
| 26 // successful. Takes an optional parameter of existing settings to merge with |
| 27 // before returning to the callback (may be NULL) and an optional parameter of |
| 28 // keys to remove from the cache post-success (may be NULL). |
| 29 class CacheModifyingCallback : public ExtensionSettingsStorage::Callback { |
| 30 public: |
| 31 // Takes ownership of callback, existing, and keys_to_remove. |
| 32 CacheModifyingCallback(ExtensionSettingsStorage::Callback* delegate, |
| 33 DictionaryValue* cache, DictionaryValue* existing, |
| 34 ListValue* keys_to_remove) |
| 35 : delegate_(delegate), cache_(cache), existing_(existing), |
| 36 keys_to_remove_(keys_to_remove) { |
| 37 } |
| 38 |
| 39 ~CacheModifyingCallback() {} |
| 40 |
| 41 static CacheModifyingCallback* Create( |
| 42 ExtensionSettingsStorage::Callback* delegate, DictionaryValue* cache) { |
| 43 return new CacheModifyingCallback(delegate, cache, NULL, NULL); |
| 44 } |
| 45 |
| 46 static CacheModifyingCallback* Create( |
| 47 ExtensionSettingsStorage::Callback* delegate, DictionaryValue* cache, |
| 48 DictionaryValue* existing) { |
| 49 return new CacheModifyingCallback(delegate, cache, existing, NULL); |
| 50 } |
| 51 |
| 52 static CacheModifyingCallback* Create( |
| 53 ExtensionSettingsStorage::Callback* delegate, DictionaryValue* cache, |
| 54 ListValue* remove) { |
| 55 return new CacheModifyingCallback(delegate, cache, NULL, remove); |
| 56 } |
| 57 |
| 58 void OnSuccess(DictionaryValue* settings) { |
| 59 cache_->MergeDictionary(settings); |
| 60 if (existing_ != NULL) { |
| 61 settings->MergeDictionary(existing_.get()); |
| 62 } |
| 63 if (keys_to_remove_ != NULL) { |
| 64 RemoveAll(cache_, keys_to_remove_.get()); |
| 65 } |
| 66 delegate_->OnSuccess(settings); |
| 67 } |
| 68 |
| 69 void OnFailure(const std::string& message) { |
| 70 delegate_->OnFailure(message); |
| 71 } |
| 72 |
| 73 private: |
| 74 scoped_ptr<ExtensionSettingsStorage::Callback> delegate_; |
| 75 DictionaryValue* cache_; |
| 76 scoped_ptr<DictionaryValue> existing_; |
| 77 scoped_ptr<ListValue> keys_to_remove_; |
| 78 }; |
| 79 |
| 80 // Runs OnSuccess() on the message loop of the calling thread. |
| 81 class SuccessClosure { |
| 82 public: |
| 83 SuccessClosure(ExtensionSettingsStorage::Callback* callback, |
| 84 DictionaryValue* settings) : callback_(callback), settings_(settings) { |
| 85 } |
| 86 |
| 87 ~SuccessClosure() { |
| 88 delete callback_; |
| 89 } |
| 90 |
| 91 void Run() { |
| 92 MessageLoop::current()->PostTask(FROM_HERE, |
| 93 base::Bind(&SuccessClosure::Run2, base::Unretained(this))); |
| 94 } |
| 95 |
| 96 private: |
| 97 void Run2() { |
| 98 callback_->OnSuccess(settings_); |
| 99 delete this; |
| 100 } |
| 101 |
| 102 ExtensionSettingsStorage::Callback* callback_; |
| 103 DictionaryValue* settings_; |
| 104 }; |
| 105 |
| 106 } // namespace |
| 107 |
| 108 ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( |
| 109 ExtensionSettingsStorage* delegate) : delegate_(delegate) { |
| 110 } |
| 111 |
| 112 void ExtensionSettingsStorageCache::DestroyEventually() { |
| 113 delegate_->DestroyEventually(); |
| 114 delete this; |
| 115 } |
| 116 |
| 117 void ExtensionSettingsStorageCache::Get(const std::string& key, |
| 118 ExtensionSettingsStorageCache::Callback* callback) { |
| 119 Value *value; |
| 120 if (GetFromCache(key, &value)) { |
| 121 DictionaryValue* settings = new DictionaryValue(); |
| 122 settings->Set(key, value); |
| 123 (new SuccessClosure(callback, settings))->Run(); |
| 124 } else { |
| 125 delegate_->Get(key, CacheModifyingCallback::Create(callback, &cache_)); |
| 126 } |
| 127 } |
| 128 |
| 129 void ExtensionSettingsStorageCache::Get(const ListValue& keys, |
| 130 ExtensionSettingsStorageCache::Callback* callback) { |
| 131 std::string key; |
| 132 DictionaryValue* settings = new DictionaryValue(); |
| 133 ListValue missing_keys; |
| 134 |
| 135 for (ListValue::const_iterator it = keys.begin(); it != keys.end(); ++it) { |
| 136 if ((*it)->GetAsString(&key)) { |
| 137 Value *value; |
| 138 if (GetFromCache(key, &value)) { |
| 139 settings->Set(key, value); |
| 140 } else { |
| 141 missing_keys.Append(Value::CreateStringValue(key)); |
| 142 } |
| 143 } |
| 144 } |
| 145 |
| 146 if (missing_keys.empty()) { |
| 147 (new SuccessClosure(callback, settings))->Run(); |
| 148 } else { |
| 149 delegate_->Get(missing_keys, |
| 150 CacheModifyingCallback::Create(callback, &cache_, settings)); |
| 151 } |
| 152 } |
| 153 |
| 154 void ExtensionSettingsStorageCache::Get( |
| 155 ExtensionSettingsStorageCache::Callback* callback) { |
| 156 // Copy the cache when passing in, as a semi-hack so that caching a no-op |
| 157 // storage object works (which always returns empty from Get()). |
| 158 // TODO(kalman): Consider doing this conditionally on type == NOOP. |
| 159 delegate_->Get( |
| 160 CacheModifyingCallback::Create(callback, &cache_, cache_.DeepCopy())); |
| 161 } |
| 162 |
| 163 void ExtensionSettingsStorageCache::Set(const std::string& key, |
| 164 const Value& value, ExtensionSettingsStorageCache::Callback* callback) { |
| 165 // Invalidate the cached entry first, in case the set fails. |
| 166 cache_.Remove(key, NULL); |
| 167 delegate_->Set(key, value, CacheModifyingCallback::Create(callback, &cache_)); |
| 168 } |
| 169 |
| 170 void ExtensionSettingsStorageCache::Set(const DictionaryValue& values, |
| 171 ExtensionSettingsStorageCache::Callback* callback) { |
| 172 // Invalidate the cached entries first, in case the set fails. |
| 173 for (DictionaryValue::key_iterator it = values.begin_keys(); |
| 174 it != values.end_keys(); ++it) { |
| 175 cache_.RemoveWithoutPathExpansion(*it, NULL); |
| 176 } |
| 177 delegate_->Set(values, CacheModifyingCallback::Create(callback, &cache_)); |
| 178 } |
| 179 |
| 180 void ExtensionSettingsStorageCache::Remove(const std::string& key, |
| 181 ExtensionSettingsStorageCache::Callback *callback) { |
| 182 // Invalidate the cached entry first, in case the remove fails. |
| 183 // We will also need to do if after the callback, to avoid race conditions |
| 184 // whether other API calls fill the cache on the UI thread. |
| 185 // This would be a good time to use structured cloning... |
| 186 cache_.Remove(key, NULL); |
| 187 ListValue* key_list = new ListValue(); |
| 188 key_list->Append(Value::CreateStringValue(key)); |
| 189 delegate_->Remove(key, |
| 190 CacheModifyingCallback::Create(callback, &cache_, key_list)); |
| 191 } |
| 192 |
| 193 void ExtensionSettingsStorageCache::Remove(const ListValue& keys, |
| 194 ExtensionSettingsStorageCache::Callback *callback) { |
| 195 std::string key; |
| 196 // Invalidate each cached entry first, in case the remove fails. |
| 197 // We will also need to do if after the callback, to avoid race conditions |
| 198 // whether other API calls fill the cache on the UI thread. |
| 199 RemoveAll(&cache_, &keys); |
| 200 delegate_->Remove(keys, |
| 201 CacheModifyingCallback::Create(callback, &cache_, keys.DeepCopy())); |
| 202 } |
| 203 |
| 204 void ExtensionSettingsStorageCache::Clear( |
| 205 ExtensionSettingsStorageCache::Callback* callback) { |
| 206 cache_.Clear(); |
| 207 delegate_->Clear(CacheModifyingCallback::Create(callback, &cache_)); |
| 208 } |
| 209 |
| 210 bool ExtensionSettingsStorageCache::GetFromCache(const std::string& key, |
| 211 Value** value) { |
| 212 Value* cached_value; |
| 213 if (!cache_.Get(key, &cached_value)) { |
| 214 return false; |
| 215 } |
| 216 *value = cached_value->DeepCopy(); |
| 217 return true; |
| 218 } |
OLD | NEW |