OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/api/storage/sync_storage_backend.h" | |
6 | |
7 #include "base/files/file_enumerator.h" | |
8 #include "base/logging.h" | |
9 #include "chrome/browser/extensions/api/storage/settings_sync_processor.h" | |
10 #include "chrome/browser/extensions/api/storage/settings_sync_util.h" | |
11 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h" | |
12 #include "content/public/browser/browser_thread.h" | |
13 #include "sync/api/sync_error_factory.h" | |
14 | |
15 using content::BrowserThread; | |
16 | |
17 namespace extensions { | |
18 | |
19 namespace { | |
20 | |
21 void AddAllSyncData(const std::string& extension_id, | |
22 const base::DictionaryValue& src, | |
23 syncer::ModelType type, | |
24 syncer::SyncDataList* dst) { | |
25 for (base::DictionaryValue::Iterator it(src); !it.IsAtEnd(); it.Advance()) { | |
26 dst->push_back(settings_sync_util::CreateData( | |
27 extension_id, it.key(), it.value(), type)); | |
28 } | |
29 } | |
30 | |
31 } // namespace | |
32 | |
33 SyncStorageBackend::SyncStorageBackend( | |
34 const scoped_refptr<SettingsStorageFactory>& storage_factory, | |
35 const base::FilePath& base_path, | |
36 const SettingsStorageQuotaEnforcer::Limits& quota, | |
37 const scoped_refptr<SettingsObserverList>& observers, | |
38 syncer::ModelType sync_type, | |
39 const syncer::SyncableService::StartSyncFlare& flare) | |
40 : SettingsBackend(storage_factory, base_path, quota), | |
41 observers_(observers), | |
42 sync_type_(sync_type), | |
43 flare_(flare) { | |
44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
45 DCHECK(sync_type_ == syncer::EXTENSION_SETTINGS || | |
46 sync_type_ == syncer::APP_SETTINGS); | |
47 } | |
48 | |
49 SyncStorageBackend::~SyncStorageBackend() {} | |
50 | |
51 ValueStore* SyncStorageBackend::GetStorage(const std::string& extension_id) { | |
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
53 base::DictionaryValue empty; | |
54 return GetOrCreateStorageWithSyncData(extension_id, empty); | |
55 } | |
56 | |
57 SyncableSettingsStorage* SyncStorageBackend::GetOrCreateStorageWithSyncData( | |
58 const std::string& extension_id, | |
59 const base::DictionaryValue& sync_data) const { | |
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
61 | |
62 StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id); | |
63 if (maybe_storage != storage_objs_.end()) { | |
64 return maybe_storage->second.get(); | |
65 } | |
66 | |
67 scoped_ptr<SettingsStorageQuotaEnforcer> storage = | |
68 CreateStorageForExtension(extension_id); | |
69 | |
70 // It's fine to create the quota enforcer underneath the sync layer, since | |
71 // sync will only go ahead if each underlying storage operation succeeds. | |
72 linked_ptr<SyncableSettingsStorage> syncable_storage( | |
73 new SyncableSettingsStorage( | |
74 observers_, extension_id, storage.release(), sync_type_, flare_)); | |
75 storage_objs_[extension_id] = syncable_storage; | |
76 | |
77 if (sync_processor_.get()) { | |
78 syncer::SyncError error = syncable_storage->StartSyncing( | |
79 sync_data, CreateSettingsSyncProcessor(extension_id).Pass()); | |
80 if (error.IsSet()) | |
81 syncable_storage.get()->StopSyncing(); | |
82 } | |
83 return syncable_storage.get(); | |
84 } | |
85 | |
86 void SyncStorageBackend::DeleteStorage(const std::string& extension_id) { | |
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
88 | |
89 // Clear settings when the extension is uninstalled. Leveldb implementations | |
90 // will also delete the database from disk when the object is destroyed as a | |
91 // result of being removed from |storage_objs_|. | |
92 // | |
93 // TODO(kalman): always GetStorage here (rather than only clearing if it | |
94 // exists) since the storage area may have been unloaded, but we still want | |
95 // to clear the data from disk. | |
96 // However, this triggers http://crbug.com/111072. | |
97 StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id); | |
98 if (maybe_storage == storage_objs_.end()) | |
99 return; | |
100 maybe_storage->second->Clear(); | |
101 storage_objs_.erase(extension_id); | |
102 } | |
103 | |
104 syncer::SyncableService* SyncStorageBackend::GetAsSyncableService() { | |
105 return this; | |
106 } | |
107 | |
108 std::set<std::string> SyncStorageBackend::GetKnownExtensionIDs() const { | |
109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
110 std::set<std::string> result; | |
111 | |
112 // Storage areas can be in-memory as well as on disk. |storage_objs_| will | |
113 // contain all that are in-memory. | |
114 for (StorageObjMap::iterator it = storage_objs_.begin(); | |
115 it != storage_objs_.end(); | |
116 ++it) { | |
117 result.insert(it->first); | |
118 } | |
119 | |
120 // Leveldb databases are directories inside base_path(). | |
121 base::FileEnumerator extension_dirs( | |
122 base_path(), false, base::FileEnumerator::DIRECTORIES); | |
123 while (!extension_dirs.Next().empty()) { | |
124 base::FilePath extension_dir = extension_dirs.GetInfo().GetName(); | |
125 DCHECK(!extension_dir.IsAbsolute()); | |
126 // Extension IDs are created as std::strings so they *should* be ASCII. | |
127 std::string maybe_as_ascii(extension_dir.MaybeAsASCII()); | |
128 if (!maybe_as_ascii.empty()) { | |
129 result.insert(maybe_as_ascii); | |
130 } | |
131 } | |
132 | |
133 return result; | |
134 } | |
135 | |
136 syncer::SyncDataList SyncStorageBackend::GetAllSyncData(syncer::ModelType type) | |
137 const { | |
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
139 // Ignore the type, it's just for sanity checking; assume that whatever base | |
140 // path we're constructed with is correct for the sync type. | |
141 DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS); | |
142 | |
143 // For all extensions, get all their settings. This has the effect | |
144 // of bringing in the entire state of extension settings in memory; sad. | |
145 syncer::SyncDataList all_sync_data; | |
146 std::set<std::string> known_extension_ids(GetKnownExtensionIDs()); | |
147 | |
148 for (std::set<std::string>::const_iterator it = known_extension_ids.begin(); | |
149 it != known_extension_ids.end(); | |
150 ++it) { | |
151 ValueStore::ReadResult maybe_settings = | |
152 GetOrCreateStorageWithSyncData(*it, base::DictionaryValue())->Get(); | |
153 if (maybe_settings->HasError()) { | |
154 LOG(WARNING) << "Failed to get settings for " << *it << ": " | |
155 << maybe_settings->error().message; | |
156 continue; | |
157 } | |
158 AddAllSyncData(*it, maybe_settings->settings(), type, &all_sync_data); | |
159 } | |
160 | |
161 return all_sync_data; | |
162 } | |
163 | |
164 syncer::SyncMergeResult SyncStorageBackend::MergeDataAndStartSyncing( | |
165 syncer::ModelType type, | |
166 const syncer::SyncDataList& initial_sync_data, | |
167 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, | |
168 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { | |
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
170 DCHECK_EQ(sync_type_, type); | |
171 DCHECK(!sync_processor_.get()); | |
172 DCHECK(sync_processor.get()); | |
173 DCHECK(sync_error_factory.get()); | |
174 | |
175 sync_processor_ = sync_processor.Pass(); | |
176 sync_error_factory_ = sync_error_factory.Pass(); | |
177 | |
178 // Group the initial sync data by extension id. | |
179 std::map<std::string, linked_ptr<base::DictionaryValue> > grouped_sync_data; | |
180 for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin(); | |
181 it != initial_sync_data.end(); | |
182 ++it) { | |
183 SettingSyncData data(*it); | |
184 linked_ptr<base::DictionaryValue> sync_data = | |
185 grouped_sync_data[data.extension_id()]; | |
186 if (!sync_data.get()) { | |
187 sync_data = | |
188 linked_ptr<base::DictionaryValue>(new base::DictionaryValue()); | |
189 grouped_sync_data[data.extension_id()] = sync_data; | |
190 } | |
191 DCHECK(!sync_data->HasKey(data.key())) << "Duplicate settings for " | |
192 << data.extension_id() << "/" | |
193 << data.key(); | |
194 sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy()); | |
195 } | |
196 | |
197 // Start syncing all existing storage areas. Any storage areas created in | |
198 // the future will start being synced as part of the creation process. | |
199 for (StorageObjMap::iterator it = storage_objs_.begin(); | |
200 it != storage_objs_.end(); | |
201 ++it) { | |
202 std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator | |
203 maybe_sync_data = grouped_sync_data.find(it->first); | |
204 syncer::SyncError error; | |
205 if (maybe_sync_data != grouped_sync_data.end()) { | |
206 error = it->second->StartSyncing( | |
207 *maybe_sync_data->second, | |
208 CreateSettingsSyncProcessor(it->first).Pass()); | |
209 grouped_sync_data.erase(it->first); | |
210 } else { | |
211 base::DictionaryValue empty; | |
212 error = it->second->StartSyncing( | |
213 empty, CreateSettingsSyncProcessor(it->first).Pass()); | |
214 } | |
215 if (error.IsSet()) | |
216 it->second->StopSyncing(); | |
217 } | |
218 | |
219 // Eagerly create and init the rest of the storage areas that have sync data. | |
220 // Under normal circumstances (i.e. not first-time sync) this will be all of | |
221 // them. | |
222 for (std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator it = | |
223 grouped_sync_data.begin(); | |
224 it != grouped_sync_data.end(); | |
225 ++it) { | |
226 GetOrCreateStorageWithSyncData(it->first, *it->second); | |
227 } | |
228 | |
229 return syncer::SyncMergeResult(type); | |
230 } | |
231 | |
232 syncer::SyncError SyncStorageBackend::ProcessSyncChanges( | |
233 const tracked_objects::Location& from_here, | |
234 const syncer::SyncChangeList& sync_changes) { | |
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
236 DCHECK(sync_processor_.get()); | |
237 | |
238 // Group changes by extension, to pass all changes in a single method call. | |
239 std::map<std::string, SettingSyncDataList> grouped_sync_data; | |
240 for (syncer::SyncChangeList::const_iterator it = sync_changes.begin(); | |
241 it != sync_changes.end(); | |
242 ++it) { | |
243 SettingSyncData data(*it); | |
244 grouped_sync_data[data.extension_id()].push_back(data); | |
245 } | |
246 | |
247 // Create any storage areas that don't exist yet but have sync data. | |
248 base::DictionaryValue empty; | |
249 for (std::map<std::string, SettingSyncDataList>::iterator it = | |
250 grouped_sync_data.begin(); | |
251 it != grouped_sync_data.end(); | |
252 ++it) { | |
253 SyncableSettingsStorage* storage = | |
254 GetOrCreateStorageWithSyncData(it->first, empty); | |
255 syncer::SyncError error = storage->ProcessSyncChanges(it->second); | |
256 if (error.IsSet()) | |
257 storage->StopSyncing(); | |
258 } | |
259 | |
260 return syncer::SyncError(); | |
261 } | |
262 | |
263 void SyncStorageBackend::StopSyncing(syncer::ModelType type) { | |
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
265 DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS); | |
266 DCHECK_EQ(sync_type_, type); | |
267 | |
268 for (StorageObjMap::iterator it = storage_objs_.begin(); | |
269 it != storage_objs_.end(); | |
270 ++it) { | |
271 // Some storage areas may have already stopped syncing if they had areas | |
272 // and syncing was disabled, but StopSyncing is safe to call multiple times. | |
273 it->second->StopSyncing(); | |
274 } | |
275 | |
276 sync_processor_.reset(); | |
277 sync_error_factory_.reset(); | |
278 } | |
279 | |
280 scoped_ptr<SettingsSyncProcessor> | |
281 SyncStorageBackend::CreateSettingsSyncProcessor(const std::string& extension_id) | |
282 const { | |
283 CHECK(sync_processor_.get()); | |
284 return scoped_ptr<SettingsSyncProcessor>(new SettingsSyncProcessor( | |
285 extension_id, sync_type_, sync_processor_.get())); | |
286 } | |
287 | |
288 } // namespace extensions | |
OLD | NEW |