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/memory/scoped_ptr.h" |
| 10 #include "base/message_loop.h" |
| 11 #include "base/task.h" |
| 12 #include "base/json/json_writer.h" |
| 13 |
| 14 namespace { |
| 15 |
| 16 void RemoveAll(DictionaryValue* from, const ListValue* keys_to_remove) { |
| 17 std::string key; |
| 18 for (ListValue::const_iterator it = keys_to_remove->begin(); |
| 19 it != keys_to_remove->end(); ++it) { |
| 20 if ((*it)->GetAsString(&key)) { |
| 21 from->Remove(key, NULL); |
| 22 } |
| 23 } |
| 24 } |
| 25 |
| 26 // Callback which delegates to a given callback but caches any result if |
| 27 // successful. Takes an optional parameter of existing settings to merge with |
| 28 // before returning to the callback (may be NULL) and an optional parameter of |
| 29 // keys to remove from the cache post-success (may be NULL). |
| 30 class CacheModifyingCallback : public ExtensionSettingsStorage::Callback { |
| 31 public: |
| 32 // Takes ownership of callback, existing, and keys_to_remove. |
| 33 CacheModifyingCallback( |
| 34 ExtensionSettingsStorage::Callback* delegate, |
| 35 base::WeakPtr<DictionaryValue> cache, |
| 36 DictionaryValue* existing, |
| 37 ListValue* keys_to_remove) |
| 38 : delegate_(delegate), |
| 39 cache_(cache), |
| 40 existing_(existing), |
| 41 keys_to_remove_(keys_to_remove) { |
| 42 } |
| 43 |
| 44 static CacheModifyingCallback* Create( |
| 45 ExtensionSettingsStorage::Callback* delegate, |
| 46 base::WeakPtr<DictionaryValue> cache_ptr_) { |
| 47 return new CacheModifyingCallback( |
| 48 delegate, |
| 49 cache_ptr_, |
| 50 NULL, |
| 51 NULL); |
| 52 } |
| 53 |
| 54 static CacheModifyingCallback* Create( |
| 55 ExtensionSettingsStorage::Callback* delegate, |
| 56 base::WeakPtr<DictionaryValue> cache_ptr_, |
| 57 DictionaryValue* existing) { |
| 58 return new CacheModifyingCallback( |
| 59 delegate, |
| 60 cache_ptr_, |
| 61 existing, |
| 62 NULL); |
| 63 } |
| 64 |
| 65 static CacheModifyingCallback* Create( |
| 66 ExtensionSettingsStorage::Callback* delegate, |
| 67 base::WeakPtr<DictionaryValue> cache_ptr_, |
| 68 ListValue* remove) { |
| 69 return new CacheModifyingCallback( |
| 70 delegate, |
| 71 cache_ptr_, |
| 72 NULL, |
| 73 remove); |
| 74 } |
| 75 |
| 76 virtual void OnSuccess(DictionaryValue* settings) OVERRIDE { |
| 77 // Note checking that the weak reference to the cache is still valid. |
| 78 // It might be NULL if the owning ExtensionSettingsStorageCache has been |
| 79 // deleted. |
| 80 // Also, settings may be NULL for Remove/Clear. |
| 81 if (settings != NULL && cache_.get() != NULL) { |
| 82 cache_->MergeDictionary(settings); |
| 83 } |
| 84 if (settings != NULL && existing_ != NULL) { |
| 85 settings->MergeDictionary(existing_.get()); |
| 86 } |
| 87 if (cache_.get() != NULL && keys_to_remove_ != NULL) { |
| 88 RemoveAll(cache_, keys_to_remove_.get()); |
| 89 } |
| 90 delegate_->OnSuccess(settings); |
| 91 } |
| 92 |
| 93 virtual void OnFailure(const std::string& message) OVERRIDE { |
| 94 delegate_->OnFailure(message); |
| 95 } |
| 96 |
| 97 private: |
| 98 scoped_ptr<ExtensionSettingsStorage::Callback> delegate_; |
| 99 base::WeakPtr<DictionaryValue> cache_; |
| 100 scoped_ptr<DictionaryValue> existing_; |
| 101 scoped_ptr<ListValue> keys_to_remove_; |
| 102 }; |
| 103 |
| 104 } // namespace |
| 105 |
| 106 ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( |
| 107 ExtensionSettingsStorage* delegate) |
| 108 : delegate_(delegate), cache_ptr_factory_(&cache_) { |
| 109 } |
| 110 |
| 111 ExtensionSettingsStorageCache::~ExtensionSettingsStorageCache() { |
| 112 } |
| 113 |
| 114 void ExtensionSettingsStorageCache::DeleteSoon() { |
| 115 delegate_->DeleteSoon(); |
| 116 delete this; |
| 117 } |
| 118 |
| 119 void ExtensionSettingsStorageCache::Get(const std::string& key, |
| 120 ExtensionSettingsStorageCache::Callback* callback) { |
| 121 Value *value; |
| 122 if (GetFromCache(key, &value)) { |
| 123 DictionaryValue* settings = new DictionaryValue(); |
| 124 settings->Set(key, value); |
| 125 MessageLoop::current()->PostTask( |
| 126 FROM_HERE, |
| 127 base::Bind( |
| 128 &ExtensionSettingsStorageCache::Callback::OnSuccess, |
| 129 base::Unretained(callback), |
| 130 settings)); |
| 131 } else { |
| 132 delegate_->Get( |
| 133 key, |
| 134 CacheModifyingCallback::Create( |
| 135 callback, |
| 136 cache_ptr_factory_.GetWeakPtr())); |
| 137 } |
| 138 } |
| 139 |
| 140 void ExtensionSettingsStorageCache::Get(const ListValue& keys, |
| 141 ExtensionSettingsStorageCache::Callback* callback) { |
| 142 std::string key; |
| 143 DictionaryValue* settings = new DictionaryValue(); |
| 144 ListValue missing_keys; |
| 145 |
| 146 for (ListValue::const_iterator it = keys.begin(); it != keys.end(); ++it) { |
| 147 if ((*it)->GetAsString(&key)) { |
| 148 Value *value; |
| 149 if (GetFromCache(key, &value)) { |
| 150 settings->Set(key, value); |
| 151 } else { |
| 152 missing_keys.Append(Value::CreateStringValue(key)); |
| 153 } |
| 154 } |
| 155 } |
| 156 |
| 157 if (missing_keys.empty()) { |
| 158 MessageLoop::current()->PostTask( |
| 159 FROM_HERE, |
| 160 base::Bind( |
| 161 &ExtensionSettingsStorageCache::Callback::OnSuccess, |
| 162 base::Unretained(callback), |
| 163 settings)); |
| 164 } else { |
| 165 delegate_->Get( |
| 166 missing_keys, |
| 167 CacheModifyingCallback::Create( |
| 168 callback, |
| 169 cache_ptr_factory_.GetWeakPtr(), |
| 170 settings)); |
| 171 } |
| 172 } |
| 173 |
| 174 void ExtensionSettingsStorageCache::Get( |
| 175 ExtensionSettingsStorageCache::Callback* callback) { |
| 176 // Copy the cache when passing in, as a semi-hack so that caching a no-op |
| 177 // storage object works (which always returns empty from Get()). |
| 178 delegate_->Get( |
| 179 CacheModifyingCallback::Create( |
| 180 callback, |
| 181 cache_ptr_factory_.GetWeakPtr(), |
| 182 cache_.DeepCopy())); |
| 183 } |
| 184 |
| 185 void ExtensionSettingsStorageCache::Set( |
| 186 const std::string& key, |
| 187 const Value& value, |
| 188 ExtensionSettingsStorageCache::Callback* callback) { |
| 189 // Invalidate the cached entry first, in case the set fails. |
| 190 cache_.Remove(key, NULL); |
| 191 delegate_->Set( |
| 192 key, |
| 193 value, |
| 194 CacheModifyingCallback::Create( |
| 195 callback, |
| 196 cache_ptr_factory_.GetWeakPtr())); |
| 197 } |
| 198 |
| 199 void ExtensionSettingsStorageCache::Set( |
| 200 const DictionaryValue& values, |
| 201 ExtensionSettingsStorageCache::Callback* callback) { |
| 202 // Invalidate the cached entries first, in case the set fails. |
| 203 for (DictionaryValue::key_iterator it = values.begin_keys(); |
| 204 it != values.end_keys(); ++it) { |
| 205 cache_.RemoveWithoutPathExpansion(*it, NULL); |
| 206 } |
| 207 delegate_->Set( |
| 208 values, |
| 209 CacheModifyingCallback::Create( |
| 210 callback, |
| 211 cache_ptr_factory_.GetWeakPtr())); |
| 212 } |
| 213 |
| 214 void ExtensionSettingsStorageCache::Remove( |
| 215 const std::string& key, |
| 216 ExtensionSettingsStorageCache::Callback *callback) { |
| 217 // Invalidate the cached entry first, in case the remove fails. |
| 218 // We will also need to do if after the callback, to avoid race conditions |
| 219 // whether other API calls fill the cache on the UI thread. |
| 220 // This would be a good time to use structured cloning... |
| 221 cache_.Remove(key, NULL); |
| 222 ListValue* key_list = new ListValue(); |
| 223 key_list->Append(Value::CreateStringValue(key)); |
| 224 delegate_->Remove( |
| 225 key, |
| 226 CacheModifyingCallback::Create( |
| 227 callback, |
| 228 cache_ptr_factory_.GetWeakPtr(), |
| 229 key_list)); |
| 230 } |
| 231 |
| 232 void ExtensionSettingsStorageCache::Remove( |
| 233 const ListValue& keys, |
| 234 ExtensionSettingsStorageCache::Callback *callback) { |
| 235 std::string key; |
| 236 // Invalidate each cached entry first, in case the remove fails. |
| 237 // We will also need to do if after the callback, to avoid race conditions |
| 238 // whether other API calls fill the cache on the UI thread. |
| 239 RemoveAll(&cache_, &keys); |
| 240 delegate_->Remove( |
| 241 keys, |
| 242 CacheModifyingCallback::Create( |
| 243 callback, |
| 244 cache_ptr_factory_.GetWeakPtr(), |
| 245 keys.DeepCopy())); |
| 246 } |
| 247 |
| 248 void ExtensionSettingsStorageCache::Clear( |
| 249 ExtensionSettingsStorageCache::Callback* callback) { |
| 250 cache_.Clear(); |
| 251 delegate_->Clear( |
| 252 CacheModifyingCallback::Create( |
| 253 callback, |
| 254 cache_ptr_factory_.GetWeakPtr())); |
| 255 } |
| 256 |
| 257 bool ExtensionSettingsStorageCache::GetFromCache( |
| 258 const std::string& key, Value** value) { |
| 259 Value* cached_value; |
| 260 if (!cache_.Get(key, &cached_value)) { |
| 261 return false; |
| 262 } |
| 263 *value = cached_value->DeepCopy(); |
| 264 return true; |
| 265 } |
OLD | NEW |