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/sync/glue/sync_backend_registrar.h"
6
7 #include <cstddef>
8
9 #include "base/compiler_specific.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "chrome/browser/history/history_service_factory.h"
13 #include "chrome/browser/password_manager/password_store_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sync/glue/browser_thread_model_worker.h"
16 #include "chrome/browser/sync/glue/history_model_worker.h"
17 #include "chrome/browser/sync/glue/password_model_worker.h"
18 #include "chrome/browser/sync/glue/ui_model_worker.h"
19 #include "components/password_manager/core/browser/password_store.h"
20 #include "components/sync_driver/change_processor.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "sync/internal_api/public/engine/passive_model_worker.h"
23 #include "sync/internal_api/public/user_share.h"
24
25 using content::BrowserThread;
26
27 namespace browser_sync {
28
29 namespace {
30
31 // Returns true if the current thread is the native thread for the
32 // given group (or if it is undeterminable).
IsOnThreadForGroup(syncer::ModelType type,syncer::ModelSafeGroup group)33 bool IsOnThreadForGroup(syncer::ModelType type, syncer::ModelSafeGroup group) {
34 switch (group) {
35 case syncer::GROUP_PASSIVE:
36 return IsControlType(type);
37 case syncer::GROUP_UI:
38 return BrowserThread::CurrentlyOn(BrowserThread::UI);
39 case syncer::GROUP_DB:
40 return BrowserThread::CurrentlyOn(BrowserThread::DB);
41 case syncer::GROUP_FILE:
42 return BrowserThread::CurrentlyOn(BrowserThread::FILE);
43 case syncer::GROUP_HISTORY:
44 // TODO(sync): How to check we're on the right thread?
45 return type == syncer::TYPED_URLS;
46 case syncer::GROUP_PASSWORD:
47 // TODO(sync): How to check we're on the right thread?
48 return type == syncer::PASSWORDS;
49 case syncer::MODEL_SAFE_GROUP_COUNT:
50 default:
51 return false;
52 }
53 }
54
55 } // namespace
56
SyncBackendRegistrar(const std::string & name,Profile * profile,scoped_ptr<base::Thread> sync_thread)57 SyncBackendRegistrar::SyncBackendRegistrar(
58 const std::string& name,
59 Profile* profile,
60 scoped_ptr<base::Thread> sync_thread) :
61 name_(name),
62 profile_(profile) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
64 CHECK(profile_);
65
66 sync_thread_ = sync_thread.Pass();
67 if (!sync_thread_) {
68 sync_thread_.reset(new base::Thread("Chrome_SyncThread"));
69 CHECK(sync_thread_->Start());
70 }
71
72 workers_[syncer::GROUP_DB] = new DatabaseModelWorker(this);
73 workers_[syncer::GROUP_DB]->RegisterForLoopDestruction();
74
75 workers_[syncer::GROUP_FILE] = new FileModelWorker(this);
76 workers_[syncer::GROUP_FILE]->RegisterForLoopDestruction();
77
78 workers_[syncer::GROUP_UI] = new UIModelWorker(this);
79 workers_[syncer::GROUP_UI]->RegisterForLoopDestruction();
80
81 // GROUP_PASSIVE worker does work on sync_loop_. But sync_loop_ is not
82 // stopped until all workers have stopped. To break the cycle, use UI loop
83 // instead.
84 workers_[syncer::GROUP_PASSIVE] =
85 new syncer::PassiveModelWorker(sync_thread_->message_loop(), this);
86 workers_[syncer::GROUP_PASSIVE]->RegisterForLoopDestruction();
87
88 HistoryService* history_service =
89 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
90 if (history_service) {
91 workers_[syncer::GROUP_HISTORY] =
92 new HistoryModelWorker(history_service->AsWeakPtr(), this);
93 workers_[syncer::GROUP_HISTORY]->RegisterForLoopDestruction();
94
95 }
96
97 scoped_refptr<password_manager::PasswordStore> password_store =
98 PasswordStoreFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
99 if (password_store.get()) {
100 workers_[syncer::GROUP_PASSWORD] =
101 new PasswordModelWorker(password_store, this);
102 workers_[syncer::GROUP_PASSWORD]->RegisterForLoopDestruction();
103 }
104 }
105
SetInitialTypes(syncer::ModelTypeSet initial_types)106 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types) {
107 base::AutoLock lock(lock_);
108
109 // This function should be called only once, shortly after construction. The
110 // routing info at that point is expected to be empty.
111 DCHECK(routing_info_.empty());
112
113 // Set our initial state to reflect the current status of the sync directory.
114 // This will ensure that our calculations in ConfigureDataTypes() will always
115 // return correct results.
116 for (syncer::ModelTypeSet::Iterator it = initial_types.First();
117 it.Good(); it.Inc()) {
118 routing_info_[it.Get()] = syncer::GROUP_PASSIVE;
119 }
120
121 if (!workers_.count(syncer::GROUP_HISTORY)) {
122 LOG_IF(WARNING, initial_types.Has(syncer::TYPED_URLS))
123 << "History store disabled, cannot sync Omnibox History";
124 routing_info_.erase(syncer::TYPED_URLS);
125 }
126
127 if (!workers_.count(syncer::GROUP_PASSWORD)) {
128 LOG_IF(WARNING, initial_types.Has(syncer::PASSWORDS))
129 << "Password store not initialized, cannot sync passwords";
130 routing_info_.erase(syncer::PASSWORDS);
131 }
132
133 last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_);
134 }
135
IsNigoriEnabled() const136 bool SyncBackendRegistrar::IsNigoriEnabled() const {
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
138 base::AutoLock lock(lock_);
139 return routing_info_.find(syncer::NIGORI) != routing_info_.end();
140 }
141
ConfigureDataTypes(syncer::ModelTypeSet types_to_add,syncer::ModelTypeSet types_to_remove)142 syncer::ModelTypeSet SyncBackendRegistrar::ConfigureDataTypes(
143 syncer::ModelTypeSet types_to_add,
144 syncer::ModelTypeSet types_to_remove) {
145 DCHECK(Intersection(types_to_add, types_to_remove).Empty());
146 syncer::ModelTypeSet filtered_types_to_add = types_to_add;
147 if (workers_.count(syncer::GROUP_HISTORY) == 0) {
148 LOG(WARNING) << "No history worker -- removing TYPED_URLS";
149 filtered_types_to_add.Remove(syncer::TYPED_URLS);
150 }
151 if (workers_.count(syncer::GROUP_PASSWORD) == 0) {
152 LOG(WARNING) << "No password worker -- removing PASSWORDS";
153 filtered_types_to_add.Remove(syncer::PASSWORDS);
154 }
155
156 base::AutoLock lock(lock_);
157 syncer::ModelTypeSet newly_added_types;
158 for (syncer::ModelTypeSet::Iterator it =
159 filtered_types_to_add.First();
160 it.Good(); it.Inc()) {
161 // Add a newly specified data type as syncer::GROUP_PASSIVE into the
162 // routing_info, if it does not already exist.
163 if (routing_info_.count(it.Get()) == 0) {
164 routing_info_[it.Get()] = syncer::GROUP_PASSIVE;
165 newly_added_types.Put(it.Get());
166 }
167 }
168 for (syncer::ModelTypeSet::Iterator it = types_to_remove.First();
169 it.Good(); it.Inc()) {
170 routing_info_.erase(it.Get());
171 }
172
173 // TODO(akalin): Use SVLOG/SLOG if we add any more logging.
174 DVLOG(1) << name_ << ": Adding types "
175 << syncer::ModelTypeSetToString(types_to_add)
176 << " (with newly-added types "
177 << syncer::ModelTypeSetToString(newly_added_types)
178 << ") and removing types "
179 << syncer::ModelTypeSetToString(types_to_remove)
180 << " to get new routing info "
181 <<syncer::ModelSafeRoutingInfoToString(routing_info_);
182 last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_);
183
184 return newly_added_types;
185 }
186
GetLastConfiguredTypes() const187 syncer::ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const {
188 return last_configured_types_;
189 }
190
RequestWorkerStopOnUIThread()191 void SyncBackendRegistrar::RequestWorkerStopOnUIThread() {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193 base::AutoLock lock(lock_);
194 for (WorkerMap::const_iterator it = workers_.begin();
195 it != workers_.end(); ++it) {
196 it->second->RequestStop();
197 }
198 }
199
ActivateDataType(syncer::ModelType type,syncer::ModelSafeGroup group,ChangeProcessor * change_processor,syncer::UserShare * user_share)200 void SyncBackendRegistrar::ActivateDataType(
201 syncer::ModelType type,
202 syncer::ModelSafeGroup group,
203 ChangeProcessor* change_processor,
204 syncer::UserShare* user_share) {
205 DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type);
206
207 base::AutoLock lock(lock_);
208 // Ensure that the given data type is in the PASSIVE group.
209 syncer::ModelSafeRoutingInfo::iterator i = routing_info_.find(type);
210 DCHECK(i != routing_info_.end());
211 DCHECK_EQ(i->second, syncer::GROUP_PASSIVE);
212 routing_info_[type] = group;
213
214 // Add the data type's change processor to the list of change
215 // processors so it can receive updates.
216 DCHECK_EQ(processors_.count(type), 0U);
217 processors_[type] = change_processor;
218
219 // Start the change processor.
220 change_processor->Start(user_share);
221 DCHECK(GetProcessorUnsafe(type));
222 }
223
DeactivateDataType(syncer::ModelType type)224 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type) {
225 DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type);
226
227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsControlType(type));
228 base::AutoLock lock(lock_);
229
230 routing_info_.erase(type);
231 ignore_result(processors_.erase(type));
232 DCHECK(!GetProcessorUnsafe(type));
233 }
234
IsTypeActivatedForTest(syncer::ModelType type) const235 bool SyncBackendRegistrar::IsTypeActivatedForTest(
236 syncer::ModelType type) const {
237 return GetProcessor(type) != NULL;
238 }
239
OnChangesApplied(syncer::ModelType model_type,int64 model_version,const syncer::BaseTransaction * trans,const syncer::ImmutableChangeRecordList & changes)240 void SyncBackendRegistrar::OnChangesApplied(
241 syncer::ModelType model_type,
242 int64 model_version,
243 const syncer::BaseTransaction* trans,
244 const syncer::ImmutableChangeRecordList& changes) {
245 ChangeProcessor* processor = GetProcessor(model_type);
246 if (!processor)
247 return;
248
249 processor->ApplyChangesFromSyncModel(trans, model_version, changes);
250 }
251
OnChangesComplete(syncer::ModelType model_type)252 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type) {
253 ChangeProcessor* processor = GetProcessor(model_type);
254 if (!processor)
255 return;
256
257 // This call just notifies the processor that it can commit; it
258 // already buffered any changes it plans to makes so needs no
259 // further information.
260 processor->CommitChangesFromSyncModel();
261 }
262
GetWorkers(std::vector<scoped_refptr<syncer::ModelSafeWorker>> * out)263 void SyncBackendRegistrar::GetWorkers(
264 std::vector<scoped_refptr<syncer::ModelSafeWorker> >* out) {
265 base::AutoLock lock(lock_);
266 out->clear();
267 for (WorkerMap::const_iterator it = workers_.begin();
268 it != workers_.end(); ++it) {
269 out->push_back(it->second.get());
270 }
271 }
272
GetModelSafeRoutingInfo(syncer::ModelSafeRoutingInfo * out)273 void SyncBackendRegistrar::GetModelSafeRoutingInfo(
274 syncer::ModelSafeRoutingInfo* out) {
275 base::AutoLock lock(lock_);
276 syncer::ModelSafeRoutingInfo copy(routing_info_);
277 out->swap(copy);
278 }
279
GetProcessor(syncer::ModelType type) const280 ChangeProcessor* SyncBackendRegistrar::GetProcessor(
281 syncer::ModelType type) const {
282 base::AutoLock lock(lock_);
283 ChangeProcessor* processor = GetProcessorUnsafe(type);
284 if (!processor)
285 return NULL;
286
287 // We can only check if |processor| exists, as otherwise the type is
288 // mapped to syncer::GROUP_PASSIVE.
289 CHECK(IsCurrentThreadSafeForModel(type));
290 return processor;
291 }
292
GetProcessorUnsafe(syncer::ModelType type) const293 ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe(
294 syncer::ModelType type) const {
295 lock_.AssertAcquired();
296 std::map<syncer::ModelType, ChangeProcessor*>::const_iterator
297 it = processors_.find(type);
298
299 // Until model association happens for a datatype, it will not
300 // appear in the processors list. During this time, it is OK to
301 // drop changes on the floor (since model association has not
302 // happened yet). When the data type is activated, model
303 // association takes place then the change processor is added to the
304 // |processors_| list.
305 if (it == processors_.end())
306 return NULL;
307
308 return it->second;
309 }
310
IsCurrentThreadSafeForModel(syncer::ModelType model_type) const311 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
312 syncer::ModelType model_type) const {
313 lock_.AssertAcquired();
314 return IsOnThreadForGroup(model_type,
315 GetGroupForModelType(model_type, routing_info_));
316 }
317
~SyncBackendRegistrar()318 SyncBackendRegistrar::~SyncBackendRegistrar() {
319 DCHECK(workers_.empty());
320 }
321
OnWorkerLoopDestroyed(syncer::ModelSafeGroup group)322 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) {
323 RemoveWorker(group);
324 }
325
OnWorkerUnregistrationDone(syncer::ModelSafeGroup group)326 void SyncBackendRegistrar::OnWorkerUnregistrationDone(
327 syncer::ModelSafeGroup group) {
328 RemoveWorker(group);
329 }
330
RemoveWorker(syncer::ModelSafeGroup group)331 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group) {
332 DVLOG(1) << "Remove " << ModelSafeGroupToString(group) << " worker.";
333
334 bool last_worker = false;
335 {
336 base::AutoLock al(lock_);
337 WorkerMap::iterator it = workers_.find(group);
338 CHECK(it != workers_.end());
339 stopped_workers_.push_back(it->second);
340 workers_.erase(it);
341 last_worker = workers_.empty();
342 }
343
344 if (last_worker) {
345 // Self-destruction after last worker.
346 DVLOG(1) << "Destroy registrar on loop of "
347 << ModelSafeGroupToString(group);
348 delete this;
349 }
350 }
351
ReleaseSyncThread()352 scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() {
353 return sync_thread_.Pass();
354 }
355
Shutdown()356 void SyncBackendRegistrar::Shutdown() {
357 // All data types should have been deactivated by now.
358 DCHECK(processors_.empty());
359
360 // Unregister worker from observing loop destruction.
361 base::AutoLock al(lock_);
362 for (WorkerMap::iterator it = workers_.begin();
363 it != workers_.end(); ++it) {
364 it->second->UnregisterForLoopDestruction(
365 base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone,
366 base::Unretained(this)));
367 }
368 }
369
sync_thread()370 base::Thread* SyncBackendRegistrar::sync_thread() {
371 return sync_thread_.get();
372 }
373
374 } // namespace browser_sync
375