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 | |
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 CreateStorageClosure( | |
32 const FilePath& base_path, | |
33 std::map<std::string, ExtensionSettingsStorage*>* storage_objs, | |
34 const std::string& extension_id, | |
35 ExtensionSettingsStorage::Type type, | |
36 ExtensionSettingsStorage::Type fallback_type, | |
37 bool cached, | |
38 const ExtensionSettings::Callback& callback) | |
39 : base_path_(base_path), | |
40 storage_objs_(storage_objs), | |
41 extension_id_(extension_id), | |
42 type_(type), | |
43 fallback_type_(fallback_type), | |
44 cached_(cached), | |
45 callback_(callback), | |
46 storage_(NULL) {} | |
47 | |
48 ~CreateStorageClosure() {} | |
49 | |
50 void Run() { | |
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
52 BrowserThread::PostTask( | |
53 BrowserThread::FILE, | |
54 FROM_HERE, | |
55 base::Bind( | |
56 &CreateStorageClosure::RunOnFileThread, base::Unretained(this))); | |
Matt Perry
2011/06/23 19:14:11
nit: indent +2
not at google - send to devlin
2011/06/27 08:51:02
Done.
| |
57 } | |
58 | |
59 private: | |
60 void RunOnFileThread() { | |
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
62 storage_ = CreateStorage(type_); | |
63 if (storage_ == NULL && type_ != fallback_type_) { | |
64 storage_ = CreateStorage(fallback_type_); | |
65 } | |
66 BrowserThread::PostTask( | |
67 BrowserThread::UI, | |
68 FROM_HERE, | |
69 base::Bind(&CreateStorageClosure::Done, base::Unretained(this))); | |
70 } | |
71 | |
72 void Done() { | |
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
74 DCHECK(storage_ != NULL); | |
75 // Cache the result now. To avoid a race condition, check again to see | |
76 // whether a storage has been created already; if so, use that one. | |
77 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | |
78 storage_objs_->find(extension_id_); | |
79 if (existing == storage_objs_->end()) { | |
80 (*storage_objs_)[extension_id_] = storage_; | |
81 } else { | |
82 delete storage_; | |
83 storage_ = existing->second; | |
84 DCHECK(storage_ != NULL); | |
85 } | |
86 callback_.Run(storage_); | |
87 delete this; | |
88 } | |
89 | |
90 // Creates a storage object of a given type. If that fails, returns NULL. | |
91 ExtensionSettingsStorage* CreateStorage(ExtensionSettingsStorage::Type type) { | |
92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
93 ExtensionSettingsStorage* storage = NULL; | |
94 | |
95 switch (type) { | |
96 case ExtensionSettingsStorage::NOOP: | |
97 storage = new ExtensionSettingsNoopStorage(); | |
98 break; | |
99 case ExtensionSettingsStorage::LEVELDB: | |
100 storage = ExtensionSettingsLeveldbStorage::Create( | |
101 base_path_, extension_id_); | |
102 break; | |
103 default: | |
104 NOTREACHED(); | |
105 } | |
106 | |
107 if (storage != NULL && cached_) { | |
108 storage = new ExtensionSettingsStorageCache(storage); | |
109 } | |
110 | |
111 return storage; | |
112 } | |
113 | |
114 const FilePath& base_path_; | |
115 std::map<std::string, ExtensionSettingsStorage*>* storage_objs_; | |
116 std::string extension_id_; | |
117 ExtensionSettingsStorage::Type type_; | |
118 ExtensionSettingsStorage::Type fallback_type_; | |
119 bool cached_; | |
120 ExtensionSettings::Callback callback_; | |
121 | |
122 ExtensionSettingsStorage* storage_; | |
123 }; | |
124 | |
125 } // namespace | |
126 | |
127 ExtensionSettings::ExtensionSettings(const FilePath& base_path) | |
128 : base_path_(base_path) { | |
129 } | |
130 | |
131 ExtensionSettings::~ExtensionSettings() { | |
132 std::map<std::string, ExtensionSettingsStorage*>::iterator it; | |
133 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | |
134 it->second->DestroyEventually(); | |
135 } | |
136 } | |
137 | |
138 void ExtensionSettings::GetStorage( | |
139 const std::string& extension_id, | |
140 const ExtensionSettings::Callback& callback) { | |
141 if (!GetExistingStorage(extension_id, callback)) { | |
142 (new CreateStorageClosure( | |
143 base_path_, | |
144 &storage_objs_, | |
145 extension_id, | |
146 ExtensionSettingsStorage::LEVELDB, | |
147 ExtensionSettingsStorage::NOOP, | |
148 true, | |
149 callback))->Run(); | |
150 } | |
151 } | |
152 | |
153 void ExtensionSettings::GetStorageForTesting( | |
154 ExtensionSettingsStorage::Type type, | |
155 bool cached, | |
156 const std::string& extension_id, | |
157 const Callback& callback) { | |
158 if (!GetExistingStorage(extension_id, callback)) { | |
159 (new CreateStorageClosure( | |
160 base_path_, | |
161 &storage_objs_, | |
162 extension_id, | |
163 type, | |
164 type, | |
165 cached, | |
166 callback))->Run(); | |
167 } | |
168 } | |
169 | |
170 bool ExtensionSettings::GetExistingStorage( | |
171 const std::string& extension_id, const Callback& callback) { | |
172 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | |
173 storage_objs_.find(extension_id); | |
174 if (existing == storage_objs_.end()) { | |
175 // No existing storage. | |
176 return false; | |
177 } | |
178 // Existing storage. Reply with that. | |
179 ExtensionSettingsStorage* storage = existing->second; | |
180 DCHECK(storage != NULL); | |
181 MessageLoop::current()->PostTask( | |
182 FROM_HERE, | |
183 base::Bind(&Callback::Run, base::Unretained(&callback), storage)); | |
184 return true; | |
185 } | |
OLD | NEW |