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