OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_settings.h" | 5 #include "chrome/browser/extensions/extension_settings.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
12 #include "content/browser/browser_thread.h" | 12 #include "content/browser/browser_thread.h" |
13 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" | 13 #include "chrome/browser/extensions/extension_settings_leveldb_storage.h" |
14 #include "chrome/browser/extensions/extension_settings_noop_storage.h" | 14 #include "chrome/browser/extensions/extension_settings_noop_storage.h" |
15 #include "chrome/browser/extensions/extension_settings_storage_cache.h" | 15 #include "chrome/browser/extensions/extension_settings_storage_cache.h" |
16 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | 16 #include "chrome/browser/extensions/syncable_extension_settings_storage.h" |
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
18 | 17 |
19 ExtensionSettings::ExtensionSettings(const FilePath& base_path) | 18 ExtensionSettings::ExtensionSettings(const FilePath& base_path) |
20 : base_path_(base_path) { | 19 : base_path_(base_path), sync_processor_(NULL) { |
21 } | 20 } |
22 | 21 |
23 ExtensionSettings::~ExtensionSettings() { | 22 ExtensionSettings::~ExtensionSettings() { |
24 std::map<std::string, ExtensionSettingsStorage*>::iterator it; | 23 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; |
25 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | 24 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { |
26 it->second->DeleteSoon(); | 25 it->second->DeleteSoon(); |
27 } | 26 } |
28 } | 27 } |
29 | 28 |
30 void ExtensionSettings::GetStorage( | 29 void ExtensionSettings::GetStorage( |
31 const std::string& extension_id, | 30 const std::string& extension_id, |
32 const Callback& callback) { | 31 const Callback& callback) { |
33 if (!GetExistingStorage(extension_id, callback)) { | 32 if (!GetExistingStorage(extension_id, callback)) { |
34 StartCreationOfStorage( | 33 StartCreationOfStorage( |
(...skipping 10 matching lines...) Expand all Loading... | |
45 bool cached, | 44 bool cached, |
46 const std::string& extension_id, | 45 const std::string& extension_id, |
47 const Callback& callback) { | 46 const Callback& callback) { |
48 if (!GetExistingStorage(extension_id, callback)) { | 47 if (!GetExistingStorage(extension_id, callback)) { |
49 StartCreationOfStorage(extension_id, type, type, cached, callback); | 48 StartCreationOfStorage(extension_id, type, type, cached, callback); |
50 } | 49 } |
51 } | 50 } |
52 | 51 |
53 bool ExtensionSettings::GetExistingStorage( | 52 bool ExtensionSettings::GetExistingStorage( |
54 const std::string& extension_id, const Callback& callback) { | 53 const std::string& extension_id, const Callback& callback) { |
55 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | 54 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing = |
56 storage_objs_.find(extension_id); | 55 storage_objs_.find(extension_id); |
57 if (existing == storage_objs_.end()) { | 56 if (existing == storage_objs_.end()) { |
58 // No existing storage. | 57 // No existing storage. |
59 return false; | 58 return false; |
60 } | 59 } |
61 // Existing storage. Reply with that. | 60 // Existing storage. Reply with that. |
62 ExtensionSettingsStorage* storage = existing->second; | 61 SyncableExtensionSettingsStorage* storage = existing->second; |
63 DCHECK(storage != NULL); | 62 DCHECK(storage != NULL); |
64 MessageLoop::current()->PostTask( | 63 MessageLoop::current()->PostTask( |
65 FROM_HERE, | 64 FROM_HERE, |
66 base::Bind( | 65 base::Bind( |
67 &ExtensionSettings::RunWithStorage, | 66 &ExtensionSettings::RunWithStorage, |
68 this, | 67 this, |
69 new Callback(callback), | 68 new Callback(callback), |
70 storage)); | 69 storage)); |
71 return true; | 70 return true; |
72 } | 71 } |
73 | 72 |
74 void ExtensionSettings::RunWithStorage( | 73 void ExtensionSettings::RunWithStorage( |
75 Callback* callback, ExtensionSettingsStorage* storage) { | 74 Callback* callback, SyncableExtensionSettingsStorage* storage) { |
76 callback->Run(storage); | 75 callback->Run(storage); |
77 delete callback; | 76 delete callback; |
78 } | 77 } |
79 | 78 |
80 void ExtensionSettings::StartCreationOfStorage( | 79 void ExtensionSettings::StartCreationOfStorage( |
81 const std::string& extension_id, | 80 const std::string& extension_id, |
82 ExtensionSettingsStorage::Type type, | 81 ExtensionSettingsStorage::Type type, |
83 ExtensionSettingsStorage::Type fallback_type, | 82 ExtensionSettingsStorage::Type fallback_type, |
84 bool cached, | 83 bool cached, |
85 const Callback& callback) { | 84 const Callback& callback) { |
(...skipping 10 matching lines...) Expand all Loading... | |
96 callback)); | 95 callback)); |
97 } | 96 } |
98 | 97 |
99 void ExtensionSettings::CreateStorageOnFileThread( | 98 void ExtensionSettings::CreateStorageOnFileThread( |
100 const std::string& extension_id, | 99 const std::string& extension_id, |
101 ExtensionSettingsStorage::Type type, | 100 ExtensionSettingsStorage::Type type, |
102 ExtensionSettingsStorage::Type fallback_type, | 101 ExtensionSettingsStorage::Type fallback_type, |
103 bool cached, | 102 bool cached, |
104 const Callback& callback) { | 103 const Callback& callback) { |
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
106 ExtensionSettingsStorage* storage = CreateStorage(extension_id, type, cached); | 105 SyncableExtensionSettingsStorage* storage = |
106 CreateStorage(extension_id, type, cached); | |
107 if (storage == NULL && fallback_type != type) { | 107 if (storage == NULL && fallback_type != type) { |
108 storage = CreateStorage(extension_id, fallback_type, cached); | 108 storage = CreateStorage(extension_id, fallback_type, cached); |
109 } | 109 } |
110 DCHECK(storage != NULL); | 110 DCHECK(storage != NULL); |
111 BrowserThread::PostTask( | 111 BrowserThread::PostTask( |
112 BrowserThread::UI, | 112 BrowserThread::UI, |
113 FROM_HERE, | 113 FROM_HERE, |
114 base::Bind( | 114 base::Bind( |
115 &ExtensionSettings::EndCreationOfStorage, | 115 &ExtensionSettings::EndCreationOfStorage, |
116 this, | 116 this, |
117 extension_id, | 117 extension_id, |
118 storage, | 118 storage, |
119 callback)); | 119 callback)); |
120 } | 120 } |
121 | 121 |
122 ExtensionSettingsStorage* ExtensionSettings::CreateStorage( | 122 SyncableExtensionSettingsStorage* ExtensionSettings::CreateStorage( |
123 const std::string& extension_id, | 123 const std::string& extension_id, |
124 ExtensionSettingsStorage::Type type, | 124 ExtensionSettingsStorage::Type type, |
125 bool cached) { | 125 bool cached) { |
126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
127 ExtensionSettingsStorage* storage = NULL; | 127 ExtensionSettingsStorage* storage = NULL; |
128 switch (type) { | 128 switch (type) { |
129 case ExtensionSettingsStorage::NOOP: | 129 case ExtensionSettingsStorage::NOOP: |
130 storage = new ExtensionSettingsNoopStorage(); | 130 storage = new ExtensionSettingsNoopStorage(); |
131 break; | 131 break; |
132 case ExtensionSettingsStorage::LEVELDB: | 132 case ExtensionSettingsStorage::LEVELDB: |
133 storage = ExtensionSettingsLeveldbStorage::Create( | 133 storage = ExtensionSettingsLeveldbStorage::Create( |
134 base_path_, extension_id); | 134 base_path_, extension_id); |
135 break; | 135 break; |
136 default: | 136 default: |
137 NOTREACHED(); | 137 NOTREACHED(); |
138 } | 138 } |
139 if (storage != NULL && cached) { | 139 if (storage == NULL) { |
140 return NULL; | |
141 } | |
142 if (cached) { | |
140 storage = new ExtensionSettingsStorageCache(storage); | 143 storage = new ExtensionSettingsStorageCache(storage); |
141 } | 144 } |
142 return storage; | 145 // Always send changes to sync. The merge algorithm is such that local data |
146 // will be overwritten by sync data, so even if it's purely in-memory storage | |
147 // the settings will be consistent across browser restarts. | |
148 return new SyncableExtensionSettingsStorage(extension_id, storage); | |
143 } | 149 } |
144 | 150 |
145 void ExtensionSettings::EndCreationOfStorage( | 151 void ExtensionSettings::EndCreationOfStorage( |
146 const std::string& extension_id, | 152 const std::string& extension_id, |
147 ExtensionSettingsStorage* storage, | 153 SyncableExtensionSettingsStorage* storage, |
148 const Callback& callback) { | 154 const Callback& callback) { |
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
150 // Cache the result now. To avoid a race condition, check again to see | 156 // Cache the result now. To avoid a race condition, check again to see |
151 // whether a storage has been created already; if so, use that one. | 157 // whether a storage has been created already; if so, use that one. |
152 std::map<std::string, ExtensionSettingsStorage*>::iterator existing = | 158 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator existing = |
153 storage_objs_.find(extension_id); | 159 storage_objs_.find(extension_id); |
154 if (existing == storage_objs_.end()) { | 160 if (existing == storage_objs_.end()) { |
155 storage_objs_[extension_id] = storage; | 161 storage_objs_[extension_id] = storage; |
156 } else { | 162 } else { |
157 storage->DeleteSoon(); | 163 storage->DeleteSoon(); |
158 storage = existing->second; | 164 storage = existing->second; |
159 DCHECK(storage != NULL); | 165 DCHECK(storage != NULL); |
160 } | 166 } |
167 if (sync_processor_ != NULL) { | |
168 StartSyncingStorage(extension_id, storage); | |
169 } | |
161 callback.Run(storage); | 170 callback.Run(storage); |
162 } | 171 } |
172 | |
173 SyncDataList ExtensionSettings::GetAllSyncData( | |
174 syncable::ModelType type) const { | |
175 // Not possible to do this synchronously, or while satisfying const, but only | |
akalin
2011/08/31 07:49:34
Explain to me again why the sync methods can't liv
not at google - send to devlin
2011/09/02 05:00:40
The extension settings code already thread jumps b
| |
176 // used for debugging so fine to ignore. | |
177 return SyncDataList(); | |
178 } | |
179 | |
180 SyncError ExtensionSettings::MergeDataAndStartSyncing( | |
181 syncable::ModelType type, | |
182 const SyncDataList& initial_sync_data, | |
183 SyncChangeProcessor* sync_processor) { | |
184 DCHECK(type == syncable::EXTENSION_SETTINGS); | |
185 DCHECK(sync_processor_ == NULL); | |
186 sync_processor_ = sync_processor; | |
187 | |
188 // Unmerged sync data could conceivably be non-empty if this method is called | |
189 // in quick succession, before the storage creation methods return (clearing | |
190 // the data). Clearing the unmerged data here is safe, it will just get | |
191 // repopulated with the new and correct data. | |
192 std::map<std::string, DictionaryValue*>::iterator unmerged_it; | |
193 for (unmerged_it = unmerged_sync_data_.begin(); | |
194 unmerged_it != unmerged_sync_data_.end(); ++unmerged_it) { | |
195 delete unmerged_it->second; | |
196 } | |
197 unmerged_sync_data_.clear(); | |
198 | |
199 // Merge algorithm when starting to sync: for each extension, if there are no | |
200 // sycned settings yet, send local settings to sync. Otherwise, completely | |
201 // overwrite local settings with those from sync. | |
202 // | |
203 // Firstly group the initial data per extension. | |
204 for (SyncDataList::const_iterator it = initial_sync_data.begin(); | |
205 it != initial_sync_data.end(); ++it) { | |
206 ExtensionSettingSyncData data(*it); | |
207 if (data.value() == NULL) { | |
208 LOG(WARNING) << "NULL value in sync data"; | |
209 continue; | |
210 } | |
211 DictionaryValue* sync_data = unmerged_sync_data_[data.extension_id()]; | |
212 if (sync_data == NULL) { | |
213 sync_data = new DictionaryValue(); | |
214 unmerged_sync_data_[data.extension_id()] = sync_data; | |
215 } | |
216 DCHECK(!sync_data->HasKey(data.key())) << | |
217 "Duplicate settings for " << data.extension_id() << "/" << data.key(); | |
218 sync_data->Set(data.key(), data.value()->DeepCopy()); | |
219 } | |
220 | |
221 // Immediately start syncing the settings of existing storage areas. | |
222 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator storage_it; | |
223 for (storage_it = storage_objs_.begin(); | |
224 storage_it != storage_objs_.end(); ++storage_it) { | |
225 StartSyncingStorage(storage_it->first, storage_it->second); | |
226 } | |
227 | |
228 // Asynchronously start syncing the remaining settings. | |
229 for (unmerged_it = unmerged_sync_data_.begin(); | |
230 unmerged_it != unmerged_sync_data_.end(); ++unmerged_it) { | |
231 // StartSyncingStorage will be called from EndCreationOfStorage, so the | |
232 // actual callback from getting the storage doesn't need to do anything. | |
233 GetStorage( | |
234 unmerged_it->first, | |
235 base::Bind( | |
236 &ExtensionSettings::AssertNoSyncData, | |
237 this, | |
238 unmerged_it->first)); | |
239 } | |
240 | |
241 return SyncError(); | |
242 } | |
243 | |
244 void ExtensionSettings::AssertNoSyncData( | |
245 const std::string& extension_id, | |
246 SyncableExtensionSettingsStorage* storage) { | |
247 DCHECK_EQ(0u, unmerged_sync_data_.count(extension_id)); | |
248 } | |
249 | |
250 void ExtensionSettings::StartSyncingStorage( | |
251 const std::string& extension_id, | |
252 SyncableExtensionSettingsStorage* storage) { | |
253 DCHECK(sync_processor_ != NULL); | |
254 scoped_ptr<DictionaryValue> sync_data(unmerged_sync_data_[extension_id]); | |
255 unmerged_sync_data_.erase(extension_id); | |
256 if (sync_data.get() == NULL) { | |
257 DictionaryValue no_data; | |
258 storage->StartSyncing(no_data, sync_processor_); | |
259 } else { | |
260 storage->StartSyncing(*sync_data, sync_processor_); | |
261 } | |
262 } | |
263 | |
264 static void CallProcessSyncChanges( | |
265 ExtensionSettingSyncDataList* sync_changes, | |
266 SyncableExtensionSettingsStorage* storage) { | |
267 scoped_ptr<ExtensionSettingSyncDataList> changes_ownership(sync_changes); | |
268 storage->ProcessSyncChanges(*sync_changes); | |
269 } | |
270 | |
271 SyncError ExtensionSettings::ProcessSyncChanges( | |
272 const tracked_objects::Location& from_here, | |
273 const SyncChangeList& sync_changes) { | |
274 DCHECK(sync_processor_ != NULL); | |
275 | |
276 // Gather changes per extension, so that all the processing can be done from a | |
277 // single GetStorage() call. | |
278 std::map<std::string, ExtensionSettingSyncDataList> all_sync_data; | |
279 for (SyncChangeList::const_iterator it = sync_changes.begin(); | |
280 it != sync_changes.end(); ++it) { | |
281 ExtensionSettingSyncData data(*it); | |
282 if (data.value() == NULL) { | |
283 LOG(WARNING) << "NULL value in sync data"; | |
284 continue; | |
285 } | |
286 all_sync_data[data.extension_id()].push_back(data); | |
287 } | |
288 | |
289 std::map<std::string, ExtensionSettingSyncDataList>::iterator it; | |
290 for (it = all_sync_data.begin(); it != all_sync_data.end(); ++it) { | |
291 GetStorage( | |
292 it->first, | |
293 base::Bind( | |
294 &CallProcessSyncChanges, | |
295 new ExtensionSettingSyncDataList(it->second))); | |
akalin
2011/08/31 07:49:34
this may leak. Just pass by value.
not at google - send to devlin
2011/09/02 05:00:40
How can it leak?
But passing by value works, so,
| |
296 } | |
297 | |
298 return SyncError(); | |
299 } | |
300 | |
301 void ExtensionSettings::StopSyncing(syncable::ModelType type) { | |
302 DCHECK(type == syncable::EXTENSION_SETTINGS); | |
303 DCHECK(sync_processor_ != NULL); | |
304 sync_processor_ = NULL; | |
305 | |
306 std::map<std::string, SyncableExtensionSettingsStorage*>::iterator it; | |
307 for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { | |
308 it->second->StopSyncing(); | |
309 } | |
310 } | |
OLD | NEW |