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.
5 #include "chrome/browser/sync/glue/sync_backend_registrar.h"
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 "components/password_manager/core/browser/password_store.h"
16 #include "components/password_manager/sync/browser/password_model_worker.h"
17 #include "components/sync_driver/change_processor.h"
18 #include "components/sync_driver/glue/browser_thread_model_worker.h"
19 #include "components/sync_driver/glue/history_model_worker.h"
20 #include "components/sync_driver/glue/ui_model_worker.h"
21 #include "sync/internal_api/public/engine/passive_model_worker.h"
22 #include "sync/internal_api/public/user_share.h"
24 namespace browser_sync
{
26 SyncBackendRegistrar::SyncBackendRegistrar(
27 const std::string
& name
,
29 scoped_ptr
<base::Thread
> sync_thread
,
30 const scoped_refptr
<base::SingleThreadTaskRunner
>& ui_thread
,
31 const scoped_refptr
<base::SingleThreadTaskRunner
>& db_thread
,
32 const scoped_refptr
<base::SingleThreadTaskRunner
>& file_thread
)
35 ui_thread_(ui_thread
),
36 db_thread_(db_thread
),
37 file_thread_(file_thread
) {
38 DCHECK(ui_thread_
->BelongsToCurrentThread());
41 sync_thread_
= sync_thread
.Pass();
43 sync_thread_
.reset(new base::Thread("Chrome_SyncThread"));
44 base::Thread::Options options
;
45 options
.timer_slack
= base::TIMER_SLACK_MAXIMUM
;
46 CHECK(sync_thread_
->StartWithOptions(options
));
49 workers_
[syncer::GROUP_DB
] =
50 new BrowserThreadModelWorker(db_thread_
, syncer::GROUP_DB
, this);
51 workers_
[syncer::GROUP_DB
]->RegisterForLoopDestruction();
53 workers_
[syncer::GROUP_FILE
] =
54 new BrowserThreadModelWorker(file_thread_
, syncer::GROUP_FILE
, this);
55 workers_
[syncer::GROUP_FILE
]->RegisterForLoopDestruction();
57 workers_
[syncer::GROUP_UI
] = new UIModelWorker(ui_thread_
, this);
58 workers_
[syncer::GROUP_UI
]->RegisterForLoopDestruction();
60 // GROUP_PASSIVE worker does work on sync_loop_. But sync_loop_ is not
61 // stopped until all workers have stopped. To break the cycle, use UI loop
63 workers_
[syncer::GROUP_PASSIVE
] =
64 new syncer::PassiveModelWorker(sync_thread_
->message_loop(), this);
65 workers_
[syncer::GROUP_PASSIVE
]->RegisterForLoopDestruction();
67 history::HistoryService
* history_service
=
68 HistoryServiceFactory::GetForProfile(profile
,
69 ServiceAccessType::IMPLICIT_ACCESS
);
70 if (history_service
) {
71 workers_
[syncer::GROUP_HISTORY
] =
72 new HistoryModelWorker(history_service
->AsWeakPtr(), ui_thread_
, this);
73 workers_
[syncer::GROUP_HISTORY
]->RegisterForLoopDestruction();
77 scoped_refptr
<password_manager::PasswordStore
> password_store
=
78 PasswordStoreFactory::GetForProfile(profile
,
79 ServiceAccessType::IMPLICIT_ACCESS
);
80 if (password_store
.get()) {
81 workers_
[syncer::GROUP_PASSWORD
] =
82 new PasswordModelWorker(password_store
, this);
83 workers_
[syncer::GROUP_PASSWORD
]->RegisterForLoopDestruction();
87 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types
) {
88 base::AutoLock
lock(lock_
);
90 // This function should be called only once, shortly after construction. The
91 // routing info at that point is expected to be empty.
92 DCHECK(routing_info_
.empty());
94 // Set our initial state to reflect the current status of the sync directory.
95 // This will ensure that our calculations in ConfigureDataTypes() will always
96 // return correct results.
97 for (syncer::ModelTypeSet::Iterator it
= initial_types
.First();
98 it
.Good(); it
.Inc()) {
99 routing_info_
[it
.Get()] = syncer::GROUP_PASSIVE
;
102 if (!workers_
.count(syncer::GROUP_HISTORY
)) {
103 LOG_IF(WARNING
, initial_types
.Has(syncer::TYPED_URLS
))
104 << "History store disabled, cannot sync Omnibox History";
105 routing_info_
.erase(syncer::TYPED_URLS
);
108 if (!workers_
.count(syncer::GROUP_PASSWORD
)) {
109 LOG_IF(WARNING
, initial_types
.Has(syncer::PASSWORDS
))
110 << "Password store not initialized, cannot sync passwords";
111 routing_info_
.erase(syncer::PASSWORDS
);
114 last_configured_types_
= syncer::GetRoutingInfoTypes(routing_info_
);
117 bool SyncBackendRegistrar::IsNigoriEnabled() const {
118 DCHECK(ui_thread_
->BelongsToCurrentThread());
119 base::AutoLock
lock(lock_
);
120 return routing_info_
.find(syncer::NIGORI
) != routing_info_
.end();
123 syncer::ModelTypeSet
SyncBackendRegistrar::ConfigureDataTypes(
124 syncer::ModelTypeSet types_to_add
,
125 syncer::ModelTypeSet types_to_remove
) {
126 DCHECK(Intersection(types_to_add
, types_to_remove
).Empty());
127 syncer::ModelTypeSet filtered_types_to_add
= types_to_add
;
128 if (workers_
.count(syncer::GROUP_HISTORY
) == 0) {
129 LOG(WARNING
) << "No history worker -- removing TYPED_URLS";
130 filtered_types_to_add
.Remove(syncer::TYPED_URLS
);
132 if (workers_
.count(syncer::GROUP_PASSWORD
) == 0) {
133 LOG(WARNING
) << "No password worker -- removing PASSWORDS";
134 filtered_types_to_add
.Remove(syncer::PASSWORDS
);
137 base::AutoLock
lock(lock_
);
138 syncer::ModelTypeSet newly_added_types
;
139 for (syncer::ModelTypeSet::Iterator it
=
140 filtered_types_to_add
.First();
141 it
.Good(); it
.Inc()) {
142 // Add a newly specified data type as syncer::GROUP_PASSIVE into the
143 // routing_info, if it does not already exist.
144 if (routing_info_
.count(it
.Get()) == 0) {
145 routing_info_
[it
.Get()] = syncer::GROUP_PASSIVE
;
146 newly_added_types
.Put(it
.Get());
149 for (syncer::ModelTypeSet::Iterator it
= types_to_remove
.First();
150 it
.Good(); it
.Inc()) {
151 routing_info_
.erase(it
.Get());
154 // TODO(akalin): Use SVLOG/SLOG if we add any more logging.
155 DVLOG(1) << name_
<< ": Adding types "
156 << syncer::ModelTypeSetToString(types_to_add
)
157 << " (with newly-added types "
158 << syncer::ModelTypeSetToString(newly_added_types
)
159 << ") and removing types "
160 << syncer::ModelTypeSetToString(types_to_remove
)
161 << " to get new routing info "
162 <<syncer::ModelSafeRoutingInfoToString(routing_info_
);
163 last_configured_types_
= syncer::GetRoutingInfoTypes(routing_info_
);
165 return newly_added_types
;
168 syncer::ModelTypeSet
SyncBackendRegistrar::GetLastConfiguredTypes() const {
169 return last_configured_types_
;
172 void SyncBackendRegistrar::RequestWorkerStopOnUIThread() {
173 DCHECK(ui_thread_
->BelongsToCurrentThread());
174 base::AutoLock
lock(lock_
);
175 for (WorkerMap::const_iterator it
= workers_
.begin();
176 it
!= workers_
.end(); ++it
) {
177 it
->second
->RequestStop();
181 void SyncBackendRegistrar::ActivateDataType(
182 syncer::ModelType type
,
183 syncer::ModelSafeGroup group
,
184 sync_driver::ChangeProcessor
* change_processor
,
185 syncer::UserShare
* user_share
) {
186 DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type
);
188 base::AutoLock
lock(lock_
);
189 // Ensure that the given data type is in the PASSIVE group.
190 syncer::ModelSafeRoutingInfo::iterator i
= routing_info_
.find(type
);
191 DCHECK(i
!= routing_info_
.end());
192 DCHECK_EQ(i
->second
, syncer::GROUP_PASSIVE
);
193 routing_info_
[type
] = group
;
195 // Add the data type's change processor to the list of change
196 // processors so it can receive updates.
197 DCHECK_EQ(processors_
.count(type
), 0U);
198 processors_
[type
] = change_processor
;
200 // Start the change processor.
201 change_processor
->Start(user_share
);
202 DCHECK(GetProcessorUnsafe(type
));
205 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type
) {
206 DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type
);
208 DCHECK(ui_thread_
->BelongsToCurrentThread() || IsControlType(type
));
209 base::AutoLock
lock(lock_
);
211 routing_info_
.erase(type
);
212 ignore_result(processors_
.erase(type
));
213 DCHECK(!GetProcessorUnsafe(type
));
216 bool SyncBackendRegistrar::IsTypeActivatedForTest(
217 syncer::ModelType type
) const {
218 return GetProcessor(type
) != NULL
;
221 void SyncBackendRegistrar::OnChangesApplied(
222 syncer::ModelType model_type
,
224 const syncer::BaseTransaction
* trans
,
225 const syncer::ImmutableChangeRecordList
& changes
) {
226 sync_driver::ChangeProcessor
* processor
= GetProcessor(model_type
);
230 processor
->ApplyChangesFromSyncModel(trans
, model_version
, changes
);
233 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type
) {
234 sync_driver::ChangeProcessor
* processor
= GetProcessor(model_type
);
238 // This call just notifies the processor that it can commit; it
239 // already buffered any changes it plans to makes so needs no
240 // further information.
241 processor
->CommitChangesFromSyncModel();
244 void SyncBackendRegistrar::GetWorkers(
245 std::vector
<scoped_refptr
<syncer::ModelSafeWorker
> >* out
) {
246 base::AutoLock
lock(lock_
);
248 for (WorkerMap::const_iterator it
= workers_
.begin();
249 it
!= workers_
.end(); ++it
) {
250 out
->push_back(it
->second
.get());
254 void SyncBackendRegistrar::GetModelSafeRoutingInfo(
255 syncer::ModelSafeRoutingInfo
* out
) {
256 base::AutoLock
lock(lock_
);
257 syncer::ModelSafeRoutingInfo
copy(routing_info_
);
261 sync_driver::ChangeProcessor
* SyncBackendRegistrar::GetProcessor(
262 syncer::ModelType type
) const {
263 base::AutoLock
lock(lock_
);
264 sync_driver::ChangeProcessor
* processor
= GetProcessorUnsafe(type
);
268 // We can only check if |processor| exists, as otherwise the type is
269 // mapped to syncer::GROUP_PASSIVE.
270 CHECK(IsCurrentThreadSafeForModel(type
));
274 sync_driver::ChangeProcessor
* SyncBackendRegistrar::GetProcessorUnsafe(
275 syncer::ModelType type
) const {
276 lock_
.AssertAcquired();
277 std::map
<syncer::ModelType
, sync_driver::ChangeProcessor
*>::const_iterator
278 it
= processors_
.find(type
);
280 // Until model association happens for a datatype, it will not
281 // appear in the processors list. During this time, it is OK to
282 // drop changes on the floor (since model association has not
283 // happened yet). When the data type is activated, model
284 // association takes place then the change processor is added to the
285 // |processors_| list.
286 if (it
== processors_
.end())
292 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
293 syncer::ModelType model_type
) const {
294 lock_
.AssertAcquired();
295 return IsOnThreadForGroup(model_type
,
296 GetGroupForModelType(model_type
, routing_info_
));
299 bool SyncBackendRegistrar::IsOnThreadForGroup(
300 syncer::ModelType type
,
301 syncer::ModelSafeGroup group
) const {
303 case syncer::GROUP_PASSIVE
:
304 return IsControlType(type
);
305 case syncer::GROUP_UI
:
306 return ui_thread_
->BelongsToCurrentThread();
307 case syncer::GROUP_DB
:
308 return db_thread_
->BelongsToCurrentThread();
309 case syncer::GROUP_FILE
:
310 return file_thread_
->BelongsToCurrentThread();
311 case syncer::GROUP_HISTORY
:
312 // TODO(sync): How to check we're on the right thread?
313 return type
== syncer::TYPED_URLS
;
314 case syncer::GROUP_PASSWORD
:
315 // TODO(sync): How to check we're on the right thread?
316 return type
== syncer::PASSWORDS
;
317 case syncer::MODEL_SAFE_GROUP_COUNT
:
323 SyncBackendRegistrar::~SyncBackendRegistrar() {
324 DCHECK(workers_
.empty());
327 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group
) {
331 void SyncBackendRegistrar::OnWorkerUnregistrationDone(
332 syncer::ModelSafeGroup group
) {
336 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group
) {
337 DVLOG(1) << "Remove " << ModelSafeGroupToString(group
) << " worker.";
339 bool last_worker
= false;
341 base::AutoLock
al(lock_
);
342 WorkerMap::iterator it
= workers_
.find(group
);
343 CHECK(it
!= workers_
.end());
344 stopped_workers_
.push_back(it
->second
);
346 last_worker
= workers_
.empty();
350 // Self-destruction after last worker.
351 DVLOG(1) << "Destroy registrar on loop of "
352 << ModelSafeGroupToString(group
);
357 scoped_ptr
<base::Thread
> SyncBackendRegistrar::ReleaseSyncThread() {
358 return sync_thread_
.Pass();
361 void SyncBackendRegistrar::Shutdown() {
362 // All data types should have been deactivated by now.
363 DCHECK(processors_
.empty());
365 // Unregister worker from observing loop destruction.
366 base::AutoLock
al(lock_
);
367 for (WorkerMap::iterator it
= workers_
.begin();
368 it
!= workers_
.end(); ++it
) {
369 it
->second
->UnregisterForLoopDestruction(
370 base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone
,
371 base::Unretained(this)));
375 base::Thread
* SyncBackendRegistrar::sync_thread() {
376 return sync_thread_
.get();
379 } // namespace browser_sync