Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(208)

Side by Side Diff: chrome/browser/extensions/extension_settings.cc

Issue 7189029: Implement an initial extension settings API. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Change where extension settings are saved, update TODO, api test Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698