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
25using content::BrowserThread;
26
27namespace browser_sync {
28
29namespace {
30
31// Returns true if the current thread is the native thread for the
32// given group (or if it is undeterminable).
33bool 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
57SyncBackendRegistrar::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
108void 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
138bool 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
144syncer::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
189syncer::ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const {
190  return last_configured_types_;
191}
192
193void 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
202void 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
226void 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
237bool SyncBackendRegistrar::IsTypeActivatedForTest(
238    syncer::ModelType type) const {
239  return GetProcessor(type) != NULL;
240}
241
242void 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
254void 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
265void 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
275void SyncBackendRegistrar::GetModelSafeRoutingInfo(
276    syncer::ModelSafeRoutingInfo* out) {
277  base::AutoLock lock(lock_);
278  syncer::ModelSafeRoutingInfo copy(routing_info_);
279  out->swap(copy);
280}
281
282sync_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
295sync_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
313bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
314    syncer::ModelType model_type) const {
315  lock_.AssertAcquired();
316  return IsOnThreadForGroup(model_type,
317                            GetGroupForModelType(model_type, routing_info_));
318}
319
320SyncBackendRegistrar::~SyncBackendRegistrar() {
321  DCHECK(workers_.empty());
322}
323
324void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) {
325  RemoveWorker(group);
326}
327
328void SyncBackendRegistrar::OnWorkerUnregistrationDone(
329    syncer::ModelSafeGroup group) {
330  RemoveWorker(group);
331}
332
333void 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
354scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() {
355  return sync_thread_.Pass();
356}
357
358void 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
372base::Thread* SyncBackendRegistrar::sync_thread() {
373  return sync_thread_.get();
374}
375
376}  // namespace browser_sync
377