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 "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"
25 using content::BrowserThread
;
27 namespace browser_sync
{
31 // Returns true if the current thread is the native thread for the
32 // given group (or if it is undeterminable).
33 bool IsOnThreadForGroup(syncer::ModelType type
, syncer::ModelSafeGroup 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
:
57 SyncBackendRegistrar::SyncBackendRegistrar(
58 const std::string
& name
,
60 scoped_ptr
<base::Thread
> sync_thread
) :
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
66 sync_thread_
= sync_thread
.Pass();
68 sync_thread_
.reset(new base::Thread("Chrome_SyncThread"));
69 CHECK(sync_thread_
->Start());
72 workers_
[syncer::GROUP_DB
] = new DatabaseModelWorker(this);
73 workers_
[syncer::GROUP_DB
]->RegisterForLoopDestruction();
75 workers_
[syncer::GROUP_FILE
] = new FileModelWorker(this);
76 workers_
[syncer::GROUP_FILE
]->RegisterForLoopDestruction();
78 workers_
[syncer::GROUP_UI
] = new UIModelWorker(this);
79 workers_
[syncer::GROUP_UI
]->RegisterForLoopDestruction();
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
84 workers_
[syncer::GROUP_PASSIVE
] =
85 new syncer::PassiveModelWorker(sync_thread_
->message_loop(), this);
86 workers_
[syncer::GROUP_PASSIVE
]->RegisterForLoopDestruction();
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();
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();
106 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types
) {
107 base::AutoLock
lock(lock_
);
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());
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
;
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
);
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
);
133 last_configured_types_
= syncer::GetRoutingInfoTypes(routing_info_
);
136 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();
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
);
151 if (workers_
.count(syncer::GROUP_PASSWORD
) == 0) {
152 LOG(WARNING
) << "No password worker -- removing PASSWORDS";
153 filtered_types_to_add
.Remove(syncer::PASSWORDS
);
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());
168 for (syncer::ModelTypeSet::Iterator it
= types_to_remove
.First();
169 it
.Good(); it
.Inc()) {
170 routing_info_
.erase(it
.Get());
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_
);
184 return newly_added_types
;
187 syncer::ModelTypeSet
SyncBackendRegistrar::GetLastConfiguredTypes() const {
188 return last_configured_types_
;
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();
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
);
207 CHECK(IsOnThreadForGroup(type
, group
));
208 base::AutoLock
lock(lock_
);
209 // Ensure that the given data type is in the PASSIVE group.
210 syncer::ModelSafeRoutingInfo::iterator i
= routing_info_
.find(type
);
211 DCHECK(i
!= routing_info_
.end());
212 DCHECK_EQ(i
->second
, syncer::GROUP_PASSIVE
);
213 routing_info_
[type
] = group
;
214 CHECK(IsCurrentThreadSafeForModel(type
));
216 // Add the data type's change processor to the list of change
217 // processors so it can receive updates.
218 DCHECK_EQ(processors_
.count(type
), 0U);
219 processors_
[type
] = change_processor
;
221 // Start the change processor.
222 change_processor
->Start(user_share
);
223 DCHECK(GetProcessorUnsafe(type
));
226 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type
) {
227 DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type
);
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
) || IsControlType(type
));
230 base::AutoLock
lock(lock_
);
232 routing_info_
.erase(type
);
233 ignore_result(processors_
.erase(type
));
234 DCHECK(!GetProcessorUnsafe(type
));
237 bool SyncBackendRegistrar::IsTypeActivatedForTest(
238 syncer::ModelType type
) const {
239 return GetProcessor(type
) != NULL
;
242 void SyncBackendRegistrar::OnChangesApplied(
243 syncer::ModelType model_type
,
245 const syncer::BaseTransaction
* trans
,
246 const syncer::ImmutableChangeRecordList
& changes
) {
247 ChangeProcessor
* processor
= GetProcessor(model_type
);
251 processor
->ApplyChangesFromSyncModel(trans
, model_version
, changes
);
254 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type
) {
255 ChangeProcessor
* processor
= GetProcessor(model_type
);
259 // This call just notifies the processor that it can commit; it
260 // already buffered any changes it plans to makes so needs no
261 // further information.
262 processor
->CommitChangesFromSyncModel();
265 void SyncBackendRegistrar::GetWorkers(
266 std::vector
<scoped_refptr
<syncer::ModelSafeWorker
> >* out
) {
267 base::AutoLock
lock(lock_
);
269 for (WorkerMap::const_iterator it
= workers_
.begin();
270 it
!= workers_
.end(); ++it
) {
271 out
->push_back(it
->second
.get());
275 void SyncBackendRegistrar::GetModelSafeRoutingInfo(
276 syncer::ModelSafeRoutingInfo
* out
) {
277 base::AutoLock
lock(lock_
);
278 syncer::ModelSafeRoutingInfo
copy(routing_info_
);
282 ChangeProcessor
* SyncBackendRegistrar::GetProcessor(
283 syncer::ModelType type
) const {
284 base::AutoLock
lock(lock_
);
285 ChangeProcessor
* processor
= GetProcessorUnsafe(type
);
289 // We can only check if |processor| exists, as otherwise the type is
290 // mapped to syncer::GROUP_PASSIVE.
291 CHECK(IsCurrentThreadSafeForModel(type
));
295 ChangeProcessor
* SyncBackendRegistrar::GetProcessorUnsafe(
296 syncer::ModelType type
) const {
297 lock_
.AssertAcquired();
298 std::map
<syncer::ModelType
, ChangeProcessor
*>::const_iterator
299 it
= processors_
.find(type
);
301 // Until model association happens for a datatype, it will not
302 // appear in the processors list. During this time, it is OK to
303 // drop changes on the floor (since model association has not
304 // happened yet). When the data type is activated, model
305 // association takes place then the change processor is added to the
306 // |processors_| list.
307 if (it
== processors_
.end())
313 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
314 syncer::ModelType model_type
) const {
315 lock_
.AssertAcquired();
316 return IsOnThreadForGroup(model_type
,
317 GetGroupForModelType(model_type
, routing_info_
));
320 SyncBackendRegistrar::~SyncBackendRegistrar() {
321 DCHECK(workers_
.empty());
324 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group
) {
328 void SyncBackendRegistrar::OnWorkerUnregistrationDone(
329 syncer::ModelSafeGroup group
) {
333 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group
) {
334 DVLOG(1) << "Remove " << ModelSafeGroupToString(group
) << " worker.";
336 bool last_worker
= false;
338 base::AutoLock
al(lock_
);
339 WorkerMap::iterator it
= workers_
.find(group
);
340 CHECK(it
!= workers_
.end());
341 stopped_workers_
.push_back(it
->second
);
343 last_worker
= workers_
.empty();
347 // Self-destruction after last worker.
348 DVLOG(1) << "Destroy registrar on loop of "
349 << ModelSafeGroupToString(group
);
354 scoped_ptr
<base::Thread
> SyncBackendRegistrar::ReleaseSyncThread() {
355 return sync_thread_
.Pass();
358 void SyncBackendRegistrar::Shutdown() {
359 // All data types should have been deactivated by now.
360 DCHECK(processors_
.empty());
362 // Unregister worker from observing loop destruction.
363 base::AutoLock
al(lock_
);
364 for (WorkerMap::iterator it
= workers_
.begin();
365 it
!= workers_
.end(); ++it
) {
366 it
->second
->UnregisterForLoopDestruction(
367 base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone
,
368 base::Unretained(this)));
372 base::Thread
* SyncBackendRegistrar::sync_thread() {
373 return sync_thread_
.get();
376 } // namespace browser_sync