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