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.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/json/json_reader.h" | |
9 #include "base/json/json_writer.h" | |
10 #include "base/logging.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "content/browser/browser_thread.h" | |
13 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" | |
14 #include "chrome/browser/extensions/extension_settings_noop_storage.h" | |
15 #include "chrome/browser/extensions/extension_settings_storage_cache.h" | |
16 #include "third_party/leveldb/include/leveldb/iterator.h" | |
17 #include "third_party/leveldb/include/leveldb/write_batch.h" | |
18 | |
19 // TODO(kalman): Policy for dots in the key names. Make sure the behaviour of | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
Filing these as bugs that block a master extension
not at google - send to devlin
2011/06/22 09:40:38
Ok. I won't file the bugs yet but will remove the
| |
20 // DictionaryValue and JSON classes is consistent with leveldb. | |
21 // TODO(kalman): Integration tests. | |
22 // TODO(kalman): More unit tests (see extension_settings_storage_unittest.cc). | |
23 // TODO(kalman): Use structured cloning rather than JSON. | |
24 // TODO(kalman): Quotas. | |
25 | |
26 namespace { | |
27 | |
28 // Creates a storage area of a requested type on the FILE thread. | |
29 class CreateStorageClosure { | |
30 public: | |
31 // Fallback type may be 0 to not have any fallback type. | |
32 // Ownership of callback is taken. | |
33 CreateStorageClosure(const FilePath& base_path, | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
These arguments should be one per line (see on "Fo
not at google - send to devlin
2011/06/22 09:40:38
Done.
| |
34 std::map<std::string, ExtensionSettingsStorage*>* storage_objs, | |
35 const std::string& extension_id, int type, int fallback_type, | |
36 ExtensionSettings::Callback* callback) | |
37 : base_path_(base_path), storage_objs_(storage_objs), | |
38 extension_id_(extension_id), type_(type), fallback_type_(fallback_type), | |
39 callback_(callback), storage_(NULL) {} | |
40 | |
41 ~CreateStorageClosure() {} | |
42 | |
43 void Run() { | |
44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
We generally have these as CHECKs in extension cod
not at google - send to devlin
2011/06/22 09:40:38
Really? Seems to be about equal. grep says 79 oc
| |
45 BrowserThread::PostTask( | |
46 BrowserThread::FILE, | |
47 FROM_HERE, | |
48 base::Bind( | |
49 &CreateStorageClosure::RunOnFileThread, base::Unretained(this))); | |
50 } | |
51 | |
52 private: | |
53 void RunOnFileThread() { | |
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
55 storage_ = CreateStorage(type_); | |
56 if (storage_ == NULL && fallback_type_ > 0) { | |
57 storage_ = CreateStorage(fallback_type_); | |
58 } | |
59 BrowserThread::PostTask( | |
60 BrowserThread::UI, | |
61 FROM_HERE, | |
62 base::Bind(&CreateStorageClosure::Done, base::Unretained(this))); | |
63 } | |
64 | |
65 void Done() { | |
66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
67 DCHECK(storage_ != NULL); | |
68 // Cache the result now. To avoid a race condition, check again to see | |
69 // whether a storage has been created already; if so, use that one. | |
70 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | |
71 storage_objs_->find(extension_id_); | |
72 if (existing == storage_objs_->end()) { | |
73 (*storage_objs_)[extension_id_] = storage_; | |
74 } else { | |
75 delete storage_; | |
76 storage_ = existing->second; | |
77 DCHECK(storage_ != NULL); | |
78 } | |
79 callback_->Run(storage_); | |
80 delete this; | |
81 } | |
82 | |
83 // Creates a storage object of a given type. If that fails, returns NULL. | |
84 ExtensionSettingsStorage* CreateStorage(int type) { | |
85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
86 ExtensionSettingsStorage* storage = NULL; | |
87 | |
88 switch (type & ~ExtensionSettingsStorage::CACHED) { | |
89 case ExtensionSettingsStorage::NOOP: | |
90 storage = new ExtensionSettingsNoopStorage(); | |
91 break; | |
92 | |
93 case ExtensionSettingsStorage::LEVELDB: { | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
Ideally the details of the LevelDB implementation
not at google - send to devlin
2011/06/22 09:40:38
Done.
| |
94 FilePath path = base_path_.Append(extension_id_); | |
95 // TODO(kalman): why doesn't FilePath/leveldb handle this internally? | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
I see a similar pattern elsewhere in the codebase.
not at google - send to devlin
2011/06/22 09:40:38
Yes, that's why I have it here -- the other leveld
| |
96 #if defined(OS_POSIX) | |
97 std::string os_path(path.value()); | |
98 #elif defined(OS_WIN) | |
99 std::string os_path = base::SysWideToUTF8(path.value()); | |
100 #endif | |
101 leveldb::Options options; | |
102 options.create_if_missing = true; | |
103 leveldb::DB* db; | |
104 leveldb::Status status = leveldb::DB::Open(options, os_path, &db); | |
105 if (!status.ok()) { | |
106 LOG(WARNING) << "Failed to create leveldb at " << path.value() << | |
107 ": " << status.ToString(); | |
108 return NULL; | |
109 } | |
110 storage = new ExtensionSettingsLeveldbStorage(db); | |
111 break; | |
112 } | |
113 | |
114 default: | |
115 NOTREACHED(); | |
116 } | |
117 | |
118 if (type & ExtensionSettingsStorage::CACHED) { | |
119 storage = new ExtensionSettingsStorageCache(storage); | |
120 } | |
121 | |
122 return storage; | |
123 } | |
124 | |
125 const FilePath& base_path_; | |
126 std::map<std::string, ExtensionSettingsStorage*>* storage_objs_; | |
127 std::string extension_id_; | |
128 int type_; | |
129 int fallback_type_; | |
130 scoped_ptr<ExtensionSettings::Callback> callback_; | |
131 | |
132 ExtensionSettingsStorage* storage_; | |
133 }; | |
134 | |
135 // Returns a specified storage object on the message loop of the current thread. | |
136 // Used to force an asynchronous API even when a storage area already exists. | |
137 class GetExistingStorageClosure { | |
138 public: | |
139 GetExistingStorageClosure(ExtensionSettings::Callback* callback, | |
140 ExtensionSettingsStorage* storage) | |
141 : callback_(callback), storage_(storage) {} | |
142 | |
143 ~GetExistingStorageClosure() { | |
144 delete callback_; | |
145 } | |
146 | |
147 void Run() { | |
148 MessageLoop::current()->PostTask(FROM_HERE, | |
149 base::Bind(&GetExistingStorageClosure::Run2, base::Unretained(this))); | |
150 } | |
151 | |
152 private: | |
153 void Run2() { | |
154 callback_->Run(storage_); | |
155 delete this; | |
156 } | |
157 | |
158 ExtensionSettings::Callback* callback_; | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
Any reason why this isn't a scoped_ptr, like it is
not at google - send to devlin
2011/06/22 09:40:38
Nope. Done.
| |
159 ExtensionSettingsStorage* storage_; | |
160 }; | |
161 | |
162 // Deletes a storage area on the FILE thread. | |
163 class DeleteStorageClosure { | |
164 public: | |
165 explicit DeleteStorageClosure(ExtensionSettingsStorage* storage) | |
166 : storage_(storage) {} | |
167 | |
168 ~DeleteStorageClosure() {} | |
169 | |
170 void Run() { | |
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
172 BrowserThread::PostTask( | |
173 BrowserThread::FILE, | |
174 FROM_HERE, | |
175 base::Bind( | |
176 &DeleteStorageClosure::RunOnFileThread, base::Unretained(this))); | |
177 } | |
178 | |
179 private: | |
180 void RunOnFileThread() { | |
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
182 delete storage_; | |
183 delete this; | |
184 } | |
185 | |
186 ExtensionSettingsStorage* storage_; | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
Ditto here.
not at google - send to devlin
2011/06/22 09:40:38
Done.
not at google - send to devlin
2011/06/22 09:40:38
Done.
| |
187 }; | |
188 | |
189 } // namespace | |
190 | |
191 ExtensionSettings::ExtensionSettings(const FilePath& base_path) | |
192 : base_path_(base_path) { | |
193 } | |
194 | |
195 ExtensionSettings::~ExtensionSettings() { | |
196 // Need to delete all the storage objects we created; and any leveldb-based | |
197 // ones must be deleted on the FILE thread. | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
Same thing here about moving implementation-specif
not at google - send to devlin
2011/06/22 09:40:38
Done.
not at google - send to devlin
2011/06/22 09:40:38
Done.
| |
198 std::map<std::string, ExtensionSettingsStorage*>::iterator it; | |
199 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | |
200 ExtensionSettingsStorage* storage = it->second; | |
201 if (storage->type() & ExtensionSettingsStorage::LEVELDB) { | |
202 (new DeleteStorageClosure(storage))->Run(); | |
203 } else { | |
204 delete storage; | |
205 } | |
206 } | |
207 } | |
208 | |
209 void ExtensionSettings::GetStorage(const std::string& extension_id, | |
210 ExtensionSettings::Callback* callback) { | |
211 if (!GetExistingStorage(extension_id, callback)) { | |
212 (new CreateStorageClosure( | |
213 base_path_, | |
214 &storage_objs_, | |
215 extension_id, | |
216 ExtensionSettingsStorage::LEVELDB | ExtensionSettingsStorage::CACHED, | |
217 ExtensionSettingsStorage::NOOP | ExtensionSettingsStorage::CACHED, | |
218 callback))->Run(); | |
219 } | |
220 } | |
221 | |
222 void ExtensionSettings::GetStorageForTesting(int type, const std::string& | |
223 extension_id, | |
224 Callback* callback) { | |
225 if (!GetExistingStorage(extension_id, callback)) { | |
226 (new CreateStorageClosure( | |
227 base_path_, | |
228 &storage_objs_, | |
229 extension_id, | |
230 type, | |
231 0, | |
232 callback))->Run(); | |
233 } | |
234 } | |
235 | |
236 bool ExtensionSettings::GetExistingStorage(const std::string& extension_id, | |
237 ExtensionSettings::Callback* callback) { | |
238 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | |
239 storage_objs_.find(extension_id); | |
240 if (existing == storage_objs_.end()) { | |
241 // No existing storage. | |
242 return false; | |
243 } | |
244 // Existing storage. Reply with that. | |
245 ExtensionSettingsStorage* storage = existing->second; | |
246 DCHECK(storage != NULL); | |
247 (new GetExistingStorageClosure(callback, storage))->Run(); | |
Mihai Parparita -not on Chrome
2011/06/21 23:40:11
Having to have GetExistingStorageClosure to make t
not at google - send to devlin
2011/06/22 09:40:38
Hm. That would be a lot nicer. The same can prob
not at google - send to devlin
2011/06/22 23:49:42
Oops. Because this is C++ not C.
not at google - send to devlin
2011/06/23 05:08:33
Another self correction: the Closure classes suppo
not at google - send to devlin
2011/06/23 13:44:26
Ok, done this now.
| |
248 return true; | |
249 } | |
OLD | NEW |