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

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

Issue 7775008: Enable sync for the settings from the Extension Settings API. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Reordering Created 9 years, 3 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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698