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 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 ~CacheModifyingCallback() {} | |
45 | |
46 static CacheModifyingCallback* Create( | |
47 ExtensionSettingsStorage::Callback* delegate, DictionaryValue* cache) { | |
48 return new CacheModifyingCallback(delegate, cache, NULL, NULL); | |
49 } | |
50 | |
51 static CacheModifyingCallback* Create( | |
52 ExtensionSettingsStorage::Callback* delegate, | |
53 DictionaryValue* cache, | |
54 DictionaryValue* existing) { | |
55 return new CacheModifyingCallback(delegate, cache, existing, NULL); | |
56 } | |
57 | |
58 static CacheModifyingCallback* Create( | |
59 ExtensionSettingsStorage::Callback* delegate, | |
60 DictionaryValue* cache, | |
61 ListValue* remove) { | |
62 return new CacheModifyingCallback(delegate, cache, NULL, remove); | |
63 } | |
64 | |
65 virtual void OnSuccess(DictionaryValue* settings) OVERRIDE { | |
66 cache_->MergeDictionary(settings); | |
67 if (existing_ != NULL) { | |
68 settings->MergeDictionary(existing_.get()); | |
69 } | |
70 if (keys_to_remove_ != NULL) { | |
71 RemoveAll(cache_, keys_to_remove_.get()); | |
72 } | |
73 delegate_->OnSuccess(settings); | |
74 } | |
75 | |
76 virtual void OnFailure(const std::string& message) OVERRIDE { | |
77 delegate_->OnFailure(message); | |
78 } | |
79 | |
80 private: | |
81 scoped_ptr<ExtensionSettingsStorage::Callback> delegate_; | |
82 DictionaryValue* cache_; | |
Matt Perry
2011/06/23 19:14:11
who owns this ptr? it seems it won't get deleted i
not at google - send to devlin
2011/06/27 08:51:02
cache is owned by ExtensionSettingsStorageCache.
| |
83 scoped_ptr<DictionaryValue> existing_; | |
84 scoped_ptr<ListValue> keys_to_remove_; | |
85 }; | |
86 | |
87 // Runs OnSuccess() on the message loop of the calling thread. | |
88 class SuccessClosure { | |
89 public: | |
90 SuccessClosure( | |
91 ExtensionSettingsStorage::Callback* callback, DictionaryValue* settings) | |
92 : callback_(callback), settings_(settings) { | |
93 } | |
94 | |
95 ~SuccessClosure() { | |
96 delete callback_; | |
Matt Perry
2011/06/23 19:14:11
make callback_ a scoped_ptr
not at google - send to devlin
2011/06/27 08:51:02
Done.
| |
97 } | |
98 | |
99 void Run() { | |
100 MessageLoop::current()->PostTask( | |
101 FROM_HERE, | |
102 base::Bind(&SuccessClosure::Run2, base::Unretained(this))); | |
103 } | |
104 | |
105 private: | |
106 void Run2() { | |
107 callback_->OnSuccess(settings_); | |
108 delete this; | |
109 } | |
110 | |
111 ExtensionSettingsStorage::Callback* callback_; | |
112 DictionaryValue* settings_; | |
113 }; | |
114 | |
115 } // namespace | |
116 | |
117 ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( | |
118 ExtensionSettingsStorage* delegate) | |
119 : delegate_(delegate) { | |
120 } | |
121 | |
122 void ExtensionSettingsStorageCache::DestroyEventually() { | |
123 delegate_->DestroyEventually(); | |
124 delete this; | |
125 } | |
126 | |
127 void ExtensionSettingsStorageCache::Get(const std::string& key, | |
128 ExtensionSettingsStorageCache::Callback* callback) { | |
129 Value *value; | |
130 if (GetFromCache(key, &value)) { | |
131 DictionaryValue* settings = new DictionaryValue(); | |
132 settings->Set(key, value); | |
133 (new SuccessClosure(callback, settings))->Run(); | |
134 } else { | |
135 delegate_->Get(key, CacheModifyingCallback::Create(callback, &cache_)); | |
136 } | |
137 } | |
138 | |
139 void ExtensionSettingsStorageCache::Get(const ListValue& keys, | |
140 ExtensionSettingsStorageCache::Callback* callback) { | |
141 std::string key; | |
142 DictionaryValue* settings = new DictionaryValue(); | |
143 ListValue missing_keys; | |
144 | |
145 for (ListValue::const_iterator it = keys.begin(); it != keys.end(); ++it) { | |
146 if ((*it)->GetAsString(&key)) { | |
147 Value *value; | |
148 if (GetFromCache(key, &value)) { | |
149 settings->Set(key, value); | |
150 } else { | |
151 missing_keys.Append(Value::CreateStringValue(key)); | |
152 } | |
153 } | |
154 } | |
155 | |
156 if (missing_keys.empty()) { | |
157 (new SuccessClosure(callback, settings))->Run(); | |
158 } else { | |
159 delegate_->Get(missing_keys, | |
160 CacheModifyingCallback::Create(callback, &cache_, settings)); | |
161 } | |
162 } | |
163 | |
164 void ExtensionSettingsStorageCache::Get( | |
165 ExtensionSettingsStorageCache::Callback* callback) { | |
166 // Copy the cache when passing in, as a semi-hack so that caching a no-op | |
167 // storage object works (which always returns empty from Get()). | |
168 // TODO(kalman): Consider doing this conditionally on type == NOOP. | |
169 delegate_->Get( | |
170 CacheModifyingCallback::Create(callback, &cache_, cache_.DeepCopy())); | |
171 } | |
172 | |
173 void ExtensionSettingsStorageCache::Set( | |
174 const std::string& key, | |
175 const Value& value, | |
176 ExtensionSettingsStorageCache::Callback* callback) { | |
177 // Invalidate the cached entry first, in case the set fails. | |
178 cache_.Remove(key, NULL); | |
179 delegate_->Set(key, value, CacheModifyingCallback::Create(callback, &cache_)); | |
180 } | |
181 | |
182 void ExtensionSettingsStorageCache::Set( | |
183 const DictionaryValue& values, | |
184 ExtensionSettingsStorageCache::Callback* callback) { | |
185 // Invalidate the cached entries first, in case the set fails. | |
186 for (DictionaryValue::key_iterator it = values.begin_keys(); | |
187 it != values.end_keys(); ++it) { | |
188 cache_.RemoveWithoutPathExpansion(*it, NULL); | |
189 } | |
190 delegate_->Set(values, CacheModifyingCallback::Create(callback, &cache_)); | |
191 } | |
192 | |
193 void ExtensionSettingsStorageCache::Remove( | |
194 const std::string& key, | |
195 ExtensionSettingsStorageCache::Callback *callback) { | |
196 // Invalidate the 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 // This would be a good time to use structured cloning... | |
200 cache_.Remove(key, NULL); | |
201 ListValue* key_list = new ListValue(); | |
202 key_list->Append(Value::CreateStringValue(key)); | |
203 delegate_->Remove(key, | |
204 CacheModifyingCallback::Create(callback, &cache_, key_list)); | |
205 } | |
206 | |
207 void ExtensionSettingsStorageCache::Remove( | |
208 const ListValue& keys, | |
209 ExtensionSettingsStorageCache::Callback *callback) { | |
210 std::string key; | |
211 // Invalidate each cached entry first, in case the remove fails. | |
212 // We will also need to do if after the callback, to avoid race conditions | |
213 // whether other API calls fill the cache on the UI thread. | |
214 RemoveAll(&cache_, &keys); | |
215 delegate_->Remove(keys, | |
216 CacheModifyingCallback::Create(callback, &cache_, keys.DeepCopy())); | |
217 } | |
218 | |
219 void ExtensionSettingsStorageCache::Clear( | |
220 ExtensionSettingsStorageCache::Callback* callback) { | |
221 cache_.Clear(); | |
222 delegate_->Clear(CacheModifyingCallback::Create(callback, &cache_)); | |
223 } | |
224 | |
225 bool ExtensionSettingsStorageCache::GetFromCache( | |
226 const std::string& key, Value** value) { | |
227 Value* cached_value; | |
228 if (!cache_.Get(key, &cached_value)) { | |
229 return false; | |
230 } | |
231 *value = cached_value->DeepCopy(); | |
232 return true; | |
233 } | |
OLD | NEW |