Chromium Code Reviews| 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 |