Home | History | Annotate | Download | only in glue
      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).
     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 
     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 
    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 
    138 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 
    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 
    189 syncer::ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const {
    190   return last_configured_types_;
    191 }
    192 
    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 
    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 
    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 
    237 bool SyncBackendRegistrar::IsTypeActivatedForTest(
    238     syncer::ModelType type) const {
    239   return GetProcessor(type) != NULL;
    240 }
    241 
    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 
    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 
    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 
    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 
    282 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 
    295 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 
    313 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 
    320 SyncBackendRegistrar::~SyncBackendRegistrar() {
    321   DCHECK(workers_.empty());
    322 }
    323 
    324 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) {
    325   RemoveWorker(group);
    326 }
    327 
    328 void SyncBackendRegistrar::OnWorkerUnregistrationDone(
    329     syncer::ModelSafeGroup group) {
    330   RemoveWorker(group);
    331 }
    332 
    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 
    354 scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() {
    355   return sync_thread_.Pass();
    356 }
    357 
    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 
    372 base::Thread* SyncBackendRegistrar::sync_thread() {
    373   return sync_thread_.get();
    374 }
    375 
    376 }  // namespace browser_sync
    377