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/settings/syncable_settings_storage.h" | |
6 | |
7 #include "chrome/browser/extensions/settings/settings_namespace.h" | |
8 #include "chrome/browser/extensions/settings/settings_sync_processor.h" | |
9 #include "chrome/browser/extensions/settings/settings_sync_util.h" | |
10 #include "content/public/browser/browser_thread.h" | |
11 #include "sync/api/sync_data.h" | |
12 #include "sync/protocol/extension_setting_specifics.pb.h" | |
13 | |
14 namespace extensions { | |
15 | |
16 using content::BrowserThread; | |
17 | |
18 SyncableSettingsStorage::SyncableSettingsStorage( | |
19 const scoped_refptr<ObserverListThreadSafe<SettingsObserver> >& | |
20 observers, | |
21 const std::string& extension_id, | |
22 ValueStore* delegate) | |
23 : observers_(observers), | |
24 extension_id_(extension_id), | |
25 delegate_(delegate) { | |
26 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
27 } | |
28 | |
29 SyncableSettingsStorage::~SyncableSettingsStorage() { | |
30 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
31 } | |
32 | |
33 size_t SyncableSettingsStorage::GetBytesInUse(const std::string& key) { | |
34 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
35 return delegate_->GetBytesInUse(key); | |
36 } | |
37 | |
38 size_t SyncableSettingsStorage::GetBytesInUse( | |
39 const std::vector<std::string>& keys) { | |
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
41 return delegate_->GetBytesInUse(keys); | |
42 } | |
43 | |
44 size_t SyncableSettingsStorage::GetBytesInUse() { | |
45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
46 return delegate_->GetBytesInUse(); | |
47 } | |
48 | |
49 ValueStore::ReadResult SyncableSettingsStorage::Get( | |
50 const std::string& key) { | |
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
52 return delegate_->Get(key); | |
53 } | |
54 | |
55 ValueStore::ReadResult SyncableSettingsStorage::Get( | |
56 const std::vector<std::string>& keys) { | |
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
58 return delegate_->Get(keys); | |
59 } | |
60 | |
61 ValueStore::ReadResult SyncableSettingsStorage::Get() { | |
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
63 return delegate_->Get(); | |
64 } | |
65 | |
66 ValueStore::WriteResult SyncableSettingsStorage::Set( | |
67 WriteOptions options, const std::string& key, const Value& value) { | |
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
69 WriteResult result = delegate_->Set(options, key, value); | |
70 if (result->HasError()) { | |
71 return result.Pass(); | |
72 } | |
73 SyncResultIfEnabled(result); | |
74 return result.Pass(); | |
75 } | |
76 | |
77 ValueStore::WriteResult SyncableSettingsStorage::Set( | |
78 WriteOptions options, const DictionaryValue& values) { | |
79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
80 WriteResult result = delegate_->Set(options, values); | |
81 if (result->HasError()) { | |
82 return result.Pass(); | |
83 } | |
84 SyncResultIfEnabled(result); | |
85 return result.Pass(); | |
86 } | |
87 | |
88 ValueStore::WriteResult SyncableSettingsStorage::Remove( | |
89 const std::string& key) { | |
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
91 WriteResult result = delegate_->Remove(key); | |
92 if (result->HasError()) { | |
93 return result.Pass(); | |
94 } | |
95 SyncResultIfEnabled(result); | |
96 return result.Pass(); | |
97 } | |
98 | |
99 ValueStore::WriteResult SyncableSettingsStorage::Remove( | |
100 const std::vector<std::string>& keys) { | |
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
102 WriteResult result = delegate_->Remove(keys); | |
103 if (result->HasError()) { | |
104 return result.Pass(); | |
105 } | |
106 SyncResultIfEnabled(result); | |
107 return result.Pass(); | |
108 } | |
109 | |
110 ValueStore::WriteResult SyncableSettingsStorage::Clear() { | |
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
112 WriteResult result = delegate_->Clear(); | |
113 if (result->HasError()) { | |
114 return result.Pass(); | |
115 } | |
116 SyncResultIfEnabled(result); | |
117 return result.Pass(); | |
118 } | |
119 | |
120 void SyncableSettingsStorage::SyncResultIfEnabled( | |
121 const ValueStore::WriteResult& result) { | |
122 if (sync_processor_.get() && !result->changes().empty()) { | |
123 syncer::SyncError error = sync_processor_->SendChanges(result->changes()); | |
124 if (error.IsSet()) | |
125 StopSyncing(); | |
126 } | |
127 } | |
128 | |
129 // Sync-related methods. | |
130 | |
131 syncer::SyncError SyncableSettingsStorage::StartSyncing( | |
132 const DictionaryValue& sync_state, | |
133 scoped_ptr<SettingsSyncProcessor> sync_processor) { | |
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
135 DCHECK(!sync_processor_.get()); | |
136 | |
137 sync_processor_ = sync_processor.Pass(); | |
138 sync_processor_->Init(sync_state); | |
139 | |
140 ReadResult maybe_settings = delegate_->Get(); | |
141 if (maybe_settings->HasError()) { | |
142 return syncer::SyncError( | |
143 FROM_HERE, | |
144 std::string("Failed to get settings: ") + maybe_settings->error(), | |
145 sync_processor_->type()); | |
146 } | |
147 | |
148 const DictionaryValue& settings = *maybe_settings->settings().get(); | |
149 if (sync_state.empty()) | |
150 return SendLocalSettingsToSync(settings); | |
151 else | |
152 return OverwriteLocalSettingsWithSync(sync_state, settings); | |
153 } | |
154 | |
155 syncer::SyncError SyncableSettingsStorage::SendLocalSettingsToSync( | |
156 const DictionaryValue& settings) { | |
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
158 | |
159 ValueStoreChangeList changes; | |
160 for (DictionaryValue::Iterator i(settings); i.HasNext(); i.Advance()) { | |
161 changes.push_back(ValueStoreChange(i.key(), NULL, i.value().DeepCopy())); | |
162 } | |
163 | |
164 if (changes.empty()) | |
165 return syncer::SyncError(); | |
166 | |
167 syncer::SyncError error = sync_processor_->SendChanges(changes); | |
168 if (error.IsSet()) | |
169 StopSyncing(); | |
170 | |
171 return error; | |
172 } | |
173 | |
174 syncer::SyncError SyncableSettingsStorage::OverwriteLocalSettingsWithSync( | |
175 const DictionaryValue& sync_state, const DictionaryValue& settings) { | |
176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
177 // Treat this as a list of changes to sync and use ProcessSyncChanges. | |
178 // This gives notifications etc for free. | |
179 scoped_ptr<DictionaryValue> new_sync_state(sync_state.DeepCopy()); | |
180 | |
181 SettingSyncDataList changes; | |
182 for (DictionaryValue::Iterator it(settings); it.HasNext(); it.Advance()) { | |
183 Value* orphaned_sync_value = NULL; | |
184 if (new_sync_state->RemoveWithoutPathExpansion( | |
185 it.key(), &orphaned_sync_value)) { | |
186 scoped_ptr<Value> sync_value(orphaned_sync_value); | |
187 if (sync_value->Equals(&it.value())) { | |
188 // Sync and local values are the same, no changes to send. | |
189 } else { | |
190 // Sync value is different, update local setting with new value. | |
191 changes.push_back( | |
192 SettingSyncData( | |
193 syncer::SyncChange::ACTION_UPDATE, | |
194 extension_id_, | |
195 it.key(), | |
196 sync_value.Pass())); | |
197 } | |
198 } else { | |
199 // Not synced, delete local setting. | |
200 changes.push_back( | |
201 SettingSyncData( | |
202 syncer::SyncChange::ACTION_DELETE, | |
203 extension_id_, | |
204 it.key(), | |
205 scoped_ptr<Value>(new DictionaryValue()))); | |
206 } | |
207 } | |
208 | |
209 // Add all new settings to local settings. | |
210 while (!new_sync_state->empty()) { | |
211 std::string key = *new_sync_state->begin_keys(); | |
212 Value* value = NULL; | |
213 CHECK(new_sync_state->RemoveWithoutPathExpansion(key, &value)); | |
214 changes.push_back( | |
215 SettingSyncData( | |
216 syncer::SyncChange::ACTION_ADD, | |
217 extension_id_, | |
218 key, | |
219 scoped_ptr<Value>(value))); | |
220 } | |
221 | |
222 if (changes.empty()) | |
223 return syncer::SyncError(); | |
224 | |
225 return ProcessSyncChanges(changes); | |
226 } | |
227 | |
228 void SyncableSettingsStorage::StopSyncing() { | |
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
230 sync_processor_.reset(); | |
231 } | |
232 | |
233 syncer::SyncError SyncableSettingsStorage::ProcessSyncChanges( | |
234 const SettingSyncDataList& sync_changes) { | |
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
236 DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_; | |
237 | |
238 if (!sync_processor_.get()) { | |
239 return syncer::SyncError( | |
240 FROM_HERE, | |
241 std::string("Sync is inactive for ") + extension_id_, | |
242 syncer::UNSPECIFIED); | |
243 } | |
244 | |
245 std::vector<syncer::SyncError> errors; | |
246 ValueStoreChangeList changes; | |
247 | |
248 for (SettingSyncDataList::const_iterator it = sync_changes.begin(); | |
249 it != sync_changes.end(); ++it) { | |
250 DCHECK_EQ(extension_id_, it->extension_id()); | |
251 | |
252 const std::string& key = it->key(); | |
253 const Value& value = it->value(); | |
254 | |
255 scoped_ptr<Value> current_value; | |
256 { | |
257 ReadResult maybe_settings = Get(it->key()); | |
258 if (maybe_settings->HasError()) { | |
259 errors.push_back(syncer::SyncError( | |
260 FROM_HERE, | |
261 std::string("Error getting current sync state for ") + | |
262 extension_id_ + "/" + key + ": " + maybe_settings->error(), | |
263 sync_processor_->type())); | |
264 continue; | |
265 } | |
266 Value* value = NULL; | |
267 if (maybe_settings->settings()->GetWithoutPathExpansion(key, &value)) { | |
268 current_value.reset(value->DeepCopy()); | |
269 } | |
270 } | |
271 | |
272 syncer::SyncError error; | |
273 | |
274 switch (it->change_type()) { | |
275 case syncer::SyncChange::ACTION_ADD: | |
276 if (!current_value.get()) { | |
277 error = OnSyncAdd(key, value.DeepCopy(), &changes); | |
278 } else { | |
279 // Already a value; hopefully a local change has beaten sync in a | |
280 // race and it's not a bug, so pretend it's an update. | |
281 LOG(WARNING) << "Got add from sync for existing setting " << | |
282 extension_id_ << "/" << key; | |
283 error = OnSyncUpdate( | |
284 key, current_value.release(), value.DeepCopy(), &changes); | |
285 } | |
286 break; | |
287 | |
288 case syncer::SyncChange::ACTION_UPDATE: | |
289 if (current_value.get()) { | |
290 error = OnSyncUpdate( | |
291 key, current_value.release(), value.DeepCopy(), &changes); | |
292 } else { | |
293 // Similarly, pretend it's an add. | |
294 LOG(WARNING) << "Got update from sync for nonexistent setting" << | |
295 extension_id_ << "/" << key; | |
296 error = OnSyncAdd(key, value.DeepCopy(), &changes); | |
297 } | |
298 break; | |
299 | |
300 case syncer::SyncChange::ACTION_DELETE: | |
301 if (current_value.get()) { | |
302 error = OnSyncDelete(key, current_value.release(), &changes); | |
303 } else { | |
304 // Similarly, ignore it. | |
305 LOG(WARNING) << "Got delete from sync for nonexistent setting " << | |
306 extension_id_ << "/" << key; | |
307 } | |
308 break; | |
309 | |
310 default: | |
311 NOTREACHED(); | |
312 } | |
313 | |
314 if (error.IsSet()) { | |
315 errors.push_back(error); | |
316 } | |
317 } | |
318 | |
319 sync_processor_->NotifyChanges(changes); | |
320 | |
321 observers_->Notify( | |
322 &SettingsObserver::OnSettingsChanged, | |
323 extension_id_, | |
324 settings_namespace::SYNC, | |
325 ValueStoreChange::ToJson(changes)); | |
326 | |
327 // TODO(kalman): Something sensible with multiple errors. | |
328 return errors.empty() ? syncer::SyncError() : errors[0]; | |
329 } | |
330 | |
331 syncer::SyncError SyncableSettingsStorage::OnSyncAdd( | |
332 const std::string& key, | |
333 Value* new_value, | |
334 ValueStoreChangeList* changes) { | |
335 DCHECK(new_value); | |
336 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); | |
337 if (result->HasError()) { | |
338 return syncer::SyncError( | |
339 FROM_HERE, | |
340 std::string("Error pushing sync add to local settings: ") + | |
341 result->error(), | |
342 sync_processor_->type()); | |
343 } | |
344 changes->push_back(ValueStoreChange(key, NULL, new_value)); | |
345 return syncer::SyncError(); | |
346 } | |
347 | |
348 syncer::SyncError SyncableSettingsStorage::OnSyncUpdate( | |
349 const std::string& key, | |
350 Value* old_value, | |
351 Value* new_value, | |
352 ValueStoreChangeList* changes) { | |
353 DCHECK(old_value); | |
354 DCHECK(new_value); | |
355 WriteResult result = delegate_->Set(IGNORE_QUOTA, key, *new_value); | |
356 if (result->HasError()) { | |
357 return syncer::SyncError( | |
358 FROM_HERE, | |
359 std::string("Error pushing sync update to local settings: ") + | |
360 result->error(), | |
361 sync_processor_->type()); | |
362 } | |
363 changes->push_back(ValueStoreChange(key, old_value, new_value)); | |
364 return syncer::SyncError(); | |
365 } | |
366 | |
367 syncer::SyncError SyncableSettingsStorage::OnSyncDelete( | |
368 const std::string& key, | |
369 Value* old_value, | |
370 ValueStoreChangeList* changes) { | |
371 DCHECK(old_value); | |
372 WriteResult result = delegate_->Remove(key); | |
373 if (result->HasError()) { | |
374 return syncer::SyncError( | |
375 FROM_HERE, | |
376 std::string("Error pushing sync remove to local settings: ") + | |
377 result->error(), | |
378 sync_processor_->type()); | |
379 } | |
380 changes->push_back(ValueStoreChange(key, old_value, NULL)); | |
381 return syncer::SyncError(); | |
382 } | |
383 | |
384 } // namespace extensions | |
OLD | NEW |