• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     base::Thread::Options options;
70     options.timer_slack = base::TIMER_SLACK_MAXIMUM;
71     CHECK(sync_thread_->StartWithOptions(options));
72   }
73 
74   workers_[syncer::GROUP_DB] = new DatabaseModelWorker(this);
75   workers_[syncer::GROUP_DB]->RegisterForLoopDestruction();
76 
77   workers_[syncer::GROUP_FILE] = new FileModelWorker(this);
78   workers_[syncer::GROUP_FILE]->RegisterForLoopDestruction();
79 
80   workers_[syncer::GROUP_UI] = new UIModelWorker(this);
81   workers_[syncer::GROUP_UI]->RegisterForLoopDestruction();
82 
83   // GROUP_PASSIVE worker does work on sync_loop_. But sync_loop_ is not
84   // stopped until all workers have stopped. To break the cycle, use UI loop
85   // instead.
86   workers_[syncer::GROUP_PASSIVE] =
87       new syncer::PassiveModelWorker(sync_thread_->message_loop(), this);
88   workers_[syncer::GROUP_PASSIVE]->RegisterForLoopDestruction();
89 
90   HistoryService* history_service =
91       HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
92   if (history_service) {
93     workers_[syncer::GROUP_HISTORY] =
94         new HistoryModelWorker(history_service->AsWeakPtr(), this);
95     workers_[syncer::GROUP_HISTORY]->RegisterForLoopDestruction();
96 
97   }
98 
99   scoped_refptr<password_manager::PasswordStore> password_store =
100       PasswordStoreFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
101   if (password_store.get()) {
102     workers_[syncer::GROUP_PASSWORD] =
103         new PasswordModelWorker(password_store, this);
104     workers_[syncer::GROUP_PASSWORD]->RegisterForLoopDestruction();
105   }
106 }
107 
SetInitialTypes(syncer::ModelTypeSet initial_types)108 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types) {
109   base::AutoLock lock(lock_);
110 
111   // This function should be called only once, shortly after construction. The
112   // routing info at that point is expected to be empty.
113   DCHECK(routing_info_.empty());
114 
115   // Set our initial state to reflect the current status of the sync directory.
116   // This will ensure that our calculations in ConfigureDataTypes() will always
117   // return correct results.
118   for (syncer::ModelTypeSet::Iterator it = initial_types.First();
119        it.Good(); it.Inc()) {
120     routing_info_[it.Get()] = syncer::GROUP_PASSIVE;
121   }
122 
123   if (!workers_.count(syncer::GROUP_HISTORY)) {
124     LOG_IF(WARNING, initial_types.Has(syncer::TYPED_URLS))
125         << "History store disabled, cannot sync Omnibox History";
126     routing_info_.erase(syncer::TYPED_URLS);
127   }
128 
129   if (!workers_.count(syncer::GROUP_PASSWORD)) {
130     LOG_IF(WARNING, initial_types.Has(syncer::PASSWORDS))
131         << "Password store not initialized, cannot sync passwords";
132     routing_info_.erase(syncer::PASSWORDS);
133   }
134 
135   last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_);
136 }
137 
IsNigoriEnabled() const138 bool SyncBackendRegistrar::IsNigoriEnabled() const {
139   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140   base::AutoLock lock(lock_);
141   return routing_info_.find(syncer::NIGORI) != routing_info_.end();
142 }
143 
ConfigureDataTypes(syncer::ModelTypeSet types_to_add,syncer::ModelTypeSet types_to_remove)144 syncer::ModelTypeSet SyncBackendRegistrar::ConfigureDataTypes(
145     syncer::ModelTypeSet types_to_add,
146     syncer::ModelTypeSet types_to_remove) {
147   DCHECK(Intersection(types_to_add, types_to_remove).Empty());
148   syncer::ModelTypeSet filtered_types_to_add = types_to_add;
149   if (workers_.count(syncer::GROUP_HISTORY) == 0) {
150     LOG(WARNING) << "No history worker -- removing TYPED_URLS";
151     filtered_types_to_add.Remove(syncer::TYPED_URLS);
152   }
153   if (workers_.count(syncer::GROUP_PASSWORD) == 0) {
154     LOG(WARNING) << "No password worker -- removing PASSWORDS";
155     filtered_types_to_add.Remove(syncer::PASSWORDS);
156   }
157 
158   base::AutoLock lock(lock_);
159   syncer::ModelTypeSet newly_added_types;
160   for (syncer::ModelTypeSet::Iterator it =
161            filtered_types_to_add.First();
162        it.Good(); it.Inc()) {
163     // Add a newly specified data type as syncer::GROUP_PASSIVE into the
164     // routing_info, if it does not already exist.
165     if (routing_info_.count(it.Get()) == 0) {
166       routing_info_[it.Get()] = syncer::GROUP_PASSIVE;
167       newly_added_types.Put(it.Get());
168     }
169   }
170   for (syncer::ModelTypeSet::Iterator it = types_to_remove.First();
171        it.Good(); it.Inc()) {
172     routing_info_.erase(it.Get());
173   }
174 
175   // TODO(akalin): Use SVLOG/SLOG if we add any more logging.
176   DVLOG(1) << name_ << ": Adding types "
177            << syncer::ModelTypeSetToString(types_to_add)
178            << " (with newly-added types "
179            << syncer::ModelTypeSetToString(newly_added_types)
180            << ") and removing types "
181            << syncer::ModelTypeSetToString(types_to_remove)
182            << " to get new routing info "
183            <<syncer::ModelSafeRoutingInfoToString(routing_info_);
184   last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_);
185 
186   return newly_added_types;
187 }
188 
GetLastConfiguredTypes() const189 syncer::ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const {
190   return last_configured_types_;
191 }
192 
RequestWorkerStopOnUIThread()193 void SyncBackendRegistrar::RequestWorkerStopOnUIThread() {
194   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195   base::AutoLock lock(lock_);
196   for (WorkerMap::const_iterator it = workers_.begin();
197        it != workers_.end(); ++it) {
198     it->second->RequestStop();
199   }
200 }
201 
ActivateDataType(syncer::ModelType type,syncer::ModelSafeGroup group,sync_driver::ChangeProcessor * change_processor,syncer::UserShare * user_share)202 void SyncBackendRegistrar::ActivateDataType(
203     syncer::ModelType type,
204     syncer::ModelSafeGroup group,
205     sync_driver::ChangeProcessor* change_processor,
206     syncer::UserShare* user_share) {
207   DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type);
208 
209   base::AutoLock lock(lock_);
210   // Ensure that the given data type is in the PASSIVE group.
211   syncer::ModelSafeRoutingInfo::iterator i = routing_info_.find(type);
212   DCHECK(i != routing_info_.end());
213   DCHECK_EQ(i->second, syncer::GROUP_PASSIVE);
214   routing_info_[type] = group;
215 
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;
220 
221   // Start the change processor.
222   change_processor->Start(user_share);
223   DCHECK(GetProcessorUnsafe(type));
224 }
225 
DeactivateDataType(syncer::ModelType type)226 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type) {
227   DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type);
228 
229   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsControlType(type));
230   base::AutoLock lock(lock_);
231 
232   routing_info_.erase(type);
233   ignore_result(processors_.erase(type));
234   DCHECK(!GetProcessorUnsafe(type));
235 }
236 
IsTypeActivatedForTest(syncer::ModelType type) const237 bool SyncBackendRegistrar::IsTypeActivatedForTest(
238     syncer::ModelType type) const {
239   return GetProcessor(type) != NULL;
240 }
241 
OnChangesApplied(syncer::ModelType model_type,int64 model_version,const syncer::BaseTransaction * trans,const syncer::ImmutableChangeRecordList & changes)242 void SyncBackendRegistrar::OnChangesApplied(
243     syncer::ModelType model_type,
244     int64 model_version,
245     const syncer::BaseTransaction* trans,
246     const syncer::ImmutableChangeRecordList& changes) {
247   sync_driver::ChangeProcessor* processor = GetProcessor(model_type);
248   if (!processor)
249     return;
250 
251   processor->ApplyChangesFromSyncModel(trans, model_version, changes);
252 }
253 
OnChangesComplete(syncer::ModelType model_type)254 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type) {
255   sync_driver::ChangeProcessor* processor = GetProcessor(model_type);
256   if (!processor)
257     return;
258 
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();
263 }
264 
GetWorkers(std::vector<scoped_refptr<syncer::ModelSafeWorker>> * out)265 void SyncBackendRegistrar::GetWorkers(
266     std::vector<scoped_refptr<syncer::ModelSafeWorker> >* out) {
267   base::AutoLock lock(lock_);
268   out->clear();
269   for (WorkerMap::const_iterator it = workers_.begin();
270        it != workers_.end(); ++it) {
271     out->push_back(it->second.get());
272   }
273 }
274 
GetModelSafeRoutingInfo(syncer::ModelSafeRoutingInfo * out)275 void SyncBackendRegistrar::GetModelSafeRoutingInfo(
276     syncer::ModelSafeRoutingInfo* out) {
277   base::AutoLock lock(lock_);
278   syncer::ModelSafeRoutingInfo copy(routing_info_);
279   out->swap(copy);
280 }
281 
GetProcessor(syncer::ModelType type) const282 sync_driver::ChangeProcessor* SyncBackendRegistrar::GetProcessor(
283     syncer::ModelType type) const {
284   base::AutoLock lock(lock_);
285   sync_driver::ChangeProcessor* processor = GetProcessorUnsafe(type);
286   if (!processor)
287     return NULL;
288 
289   // We can only check if |processor| exists, as otherwise the type is
290   // mapped to syncer::GROUP_PASSIVE.
291   CHECK(IsCurrentThreadSafeForModel(type));
292   return processor;
293 }
294 
GetProcessorUnsafe(syncer::ModelType type) const295 sync_driver::ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe(
296     syncer::ModelType type) const {
297   lock_.AssertAcquired();
298   std::map<syncer::ModelType, sync_driver::ChangeProcessor*>::const_iterator
299       it = processors_.find(type);
300 
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())
308     return NULL;
309 
310   return it->second;
311 }
312 
IsCurrentThreadSafeForModel(syncer::ModelType model_type) const313 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
314     syncer::ModelType model_type) const {
315   lock_.AssertAcquired();
316   return IsOnThreadForGroup(model_type,
317                             GetGroupForModelType(model_type, routing_info_));
318 }
319 
~SyncBackendRegistrar()320 SyncBackendRegistrar::~SyncBackendRegistrar() {
321   DCHECK(workers_.empty());
322 }
323 
OnWorkerLoopDestroyed(syncer::ModelSafeGroup group)324 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) {
325   RemoveWorker(group);
326 }
327 
OnWorkerUnregistrationDone(syncer::ModelSafeGroup group)328 void SyncBackendRegistrar::OnWorkerUnregistrationDone(
329     syncer::ModelSafeGroup group) {
330   RemoveWorker(group);
331 }
332 
RemoveWorker(syncer::ModelSafeGroup group)333 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group) {
334   DVLOG(1) << "Remove " << ModelSafeGroupToString(group) << " worker.";
335 
336   bool last_worker = false;
337   {
338     base::AutoLock al(lock_);
339     WorkerMap::iterator it = workers_.find(group);
340     CHECK(it != workers_.end());
341     stopped_workers_.push_back(it->second);
342     workers_.erase(it);
343     last_worker = workers_.empty();
344   }
345 
346   if (last_worker) {
347     // Self-destruction after last worker.
348     DVLOG(1) << "Destroy registrar on loop of "
349         << ModelSafeGroupToString(group);
350     delete this;
351   }
352 }
353 
ReleaseSyncThread()354 scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() {
355   return sync_thread_.Pass();
356 }
357 
Shutdown()358 void SyncBackendRegistrar::Shutdown() {
359   // All data types should have been deactivated by now.
360   DCHECK(processors_.empty());
361 
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)));
369   }
370 }
371 
sync_thread()372 base::Thread* SyncBackendRegistrar::sync_thread() {
373   return sync_thread_.get();
374 }
375 
376 }  // namespace browser_sync
377