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/non_frontend_data_type_controller.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/logging.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
     12 #include "chrome/browser/sync/profile_sync_components_factory.h"
     13 #include "chrome/browser/sync/profile_sync_service.h"
     14 #include "components/sync_driver/change_processor.h"
     15 #include "components/sync_driver/model_associator.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "sync/api/sync_error.h"
     18 #include "sync/internal_api/public/base/model_type.h"
     19 #include "sync/internal_api/public/util/weak_handle.h"
     20 #include "sync/util/data_type_histogram.h"
     21 
     22 using content::BrowserThread;
     23 
     24 namespace browser_sync {
     25 
     26 class NonFrontendDataTypeController::BackendComponentsContainer {
     27  public:
     28   explicit BackendComponentsContainer(
     29       NonFrontendDataTypeController* controller);
     30   ~BackendComponentsContainer();
     31   void Run();
     32   void Disconnect();
     33 
     34  private:
     35   bool CreateComponents();
     36   void Associate();
     37 
     38   // For creating components.
     39   NonFrontendDataTypeController* controller_;
     40   base::Lock controller_lock_;
     41 
     42   syncer::ModelType type_;
     43 
     44   // For returning association results to controller on UI.
     45   syncer::WeakHandle<NonFrontendDataTypeController> controller_handle_;
     46 
     47   scoped_ptr<sync_driver::AssociatorInterface> model_associator_;
     48   scoped_ptr<sync_driver::ChangeProcessor> change_processor_;
     49 };
     50 
     51 NonFrontendDataTypeController::
     52 BackendComponentsContainer::BackendComponentsContainer(
     53     NonFrontendDataTypeController* controller)
     54     : controller_(controller),
     55       type_(controller->type()) {
     56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     57   controller_handle_ =
     58       syncer::MakeWeakHandle(controller_->weak_ptr_factory_.GetWeakPtr());
     59 }
     60 
     61 NonFrontendDataTypeController::
     62 BackendComponentsContainer::~BackendComponentsContainer() {
     63   if (model_associator_)
     64     model_associator_->DisassociateModels();
     65 }
     66 
     67 void NonFrontendDataTypeController::BackendComponentsContainer::Run() {
     68   DCHECK(controller_->IsOnBackendThread());
     69   if (CreateComponents())
     70     Associate();
     71 }
     72 
     73 bool
     74 NonFrontendDataTypeController::BackendComponentsContainer::CreateComponents() {
     75   base::AutoLock al(controller_lock_);
     76   if (!controller_) {
     77     DVLOG(1) << "Controller was stopped before sync components are created.";
     78     return false;
     79   }
     80 
     81   ProfileSyncComponentsFactory::SyncComponents sync_components =
     82       controller_->CreateSyncComponents();
     83   model_associator_.reset(sync_components.model_associator);
     84   change_processor_.reset(sync_components.change_processor);
     85   return true;
     86 }
     87 
     88 void NonFrontendDataTypeController::BackendComponentsContainer::Associate() {
     89   CHECK(model_associator_);
     90 
     91   bool succeeded = false;
     92 
     93   browser_sync::NonFrontendDataTypeController::AssociationResult result(type_);
     94   if (!model_associator_->CryptoReadyIfNecessary()) {
     95     result.needs_crypto = true;
     96   } else {
     97     base::TimeTicks start_time = base::TimeTicks::Now();
     98 
     99     if (!model_associator_->SyncModelHasUserCreatedNodes(
    100         &result.sync_has_nodes)) {
    101       result.unrecoverable_error = true;
    102       result.error = syncer::SyncError(FROM_HERE,
    103                                        syncer::SyncError::UNRECOVERABLE_ERROR,
    104                                        "Failed to load sync nodes",
    105                                        type_);
    106     } else {
    107       result.error = model_associator_->AssociateModels(
    108           &result.local_merge_result, &result.syncer_merge_result);
    109 
    110       // Return components to frontend when no error.
    111       if (!result.error.IsSet()) {
    112         succeeded = true;
    113         result.change_processor = change_processor_.get();
    114         result.model_associator = model_associator_.get();
    115       }
    116     }
    117     result.association_time = base::TimeTicks::Now() - start_time;
    118   }
    119   result.local_merge_result.set_error(result.error);
    120 
    121   // Destroy processor/associator on backend on failure.
    122   if (!succeeded) {
    123     base::AutoLock al(controller_lock_);
    124     model_associator_->DisassociateModels();
    125     change_processor_.reset();
    126     model_associator_.reset();
    127   }
    128 
    129   controller_handle_.Call(
    130       FROM_HERE,
    131       &browser_sync::NonFrontendDataTypeController::AssociationCallback,
    132       result);
    133 }
    134 
    135 void NonFrontendDataTypeController::BackendComponentsContainer::Disconnect() {
    136   base::AutoLock al(controller_lock_);
    137   CHECK(controller_);
    138 
    139   if (change_processor_)
    140     controller_->DisconnectProcessor(change_processor_.get());
    141   if (model_associator_)
    142     model_associator_->AbortAssociation();
    143 
    144   controller_ = NULL;
    145 }
    146 
    147 NonFrontendDataTypeController::AssociationResult::AssociationResult(
    148     syncer::ModelType type)
    149     : needs_crypto(false),
    150       unrecoverable_error(false),
    151       sync_has_nodes(false),
    152       local_merge_result(type),
    153       syncer_merge_result(type),
    154       change_processor(NULL),
    155       model_associator(NULL) {}
    156 
    157 NonFrontendDataTypeController::AssociationResult::~AssociationResult() {}
    158 
    159 // TODO(tim): Legacy controllers are being left behind in componentization
    160 // effort for now, hence  still having a dependency on ProfileSyncService.
    161 // That dep can probably be removed without too much work.
    162 NonFrontendDataTypeController::NonFrontendDataTypeController(
    163     scoped_refptr<base::MessageLoopProxy> ui_thread,
    164     const base::Closure& error_callback,
    165     ProfileSyncComponentsFactory* profile_sync_factory,
    166     Profile* profile,
    167     ProfileSyncService* sync_service)
    168     : DataTypeController(ui_thread, error_callback),
    169       state_(NOT_RUNNING),
    170       profile_sync_factory_(profile_sync_factory),
    171       profile_(profile),
    172       profile_sync_service_(sync_service),
    173       model_associator_(NULL),
    174       change_processor_(NULL),
    175       weak_ptr_factory_(this) {
    176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    177   DCHECK(profile_sync_factory_);
    178   DCHECK(profile_);
    179   DCHECK(profile_sync_service_);
    180 }
    181 
    182 void NonFrontendDataTypeController::LoadModels(
    183     const ModelLoadCallback& model_load_callback) {
    184   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    185   model_load_callback_ = model_load_callback;
    186   if (state_ != NOT_RUNNING) {
    187     model_load_callback.Run(type(),
    188                             syncer::SyncError(FROM_HERE,
    189                                               syncer::SyncError::DATATYPE_ERROR,
    190                                               "Model already loaded",
    191                                               type()));
    192     return;
    193   }
    194 
    195   state_ = MODEL_STARTING;
    196   if (!StartModels()) {
    197     // We failed to start the models. There is no point in waiting.
    198     // Note: This code is deprecated. The only 2 datatypes here,
    199     // passwords and typed urls, dont have any special loading. So if we
    200     // get a false it means they failed.
    201     DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING
    202            || state_ == DISABLED);
    203     model_load_callback.Run(type(),
    204                             syncer::SyncError(FROM_HERE,
    205                                               syncer::SyncError::DATATYPE_ERROR,
    206                                               "Failed loading",
    207                                               type()));
    208     return;
    209   }
    210   state_ = MODEL_LOADED;
    211 
    212   model_load_callback.Run(type(), syncer::SyncError());
    213 }
    214 
    215 void NonFrontendDataTypeController::OnModelLoaded() {
    216   NOTREACHED();
    217 }
    218 
    219 void NonFrontendDataTypeController::StartAssociating(
    220     const StartCallback& start_callback) {
    221   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    222   DCHECK(!start_callback.is_null());
    223   DCHECK(!components_container_);
    224   DCHECK_EQ(state_, MODEL_LOADED);
    225 
    226   // Kick off association on the thread the datatype resides on.
    227   state_ = ASSOCIATING;
    228   start_callback_ = start_callback;
    229 
    230   components_container_.reset(new BackendComponentsContainer(this));
    231 
    232   if (!PostTaskOnBackendThread(
    233       FROM_HERE,
    234       base::Bind(&BackendComponentsContainer::Run,
    235                  base::Unretained(components_container_.get())))) {
    236     syncer::SyncError error(
    237         FROM_HERE,
    238         syncer::SyncError::DATATYPE_ERROR,
    239         "Failed to post StartAssociation", type());
    240     syncer::SyncMergeResult local_merge_result(type());
    241     local_merge_result.set_error(error);
    242     StartDone(ASSOCIATION_FAILED,
    243               local_merge_result,
    244               syncer::SyncMergeResult(type()));
    245   }
    246 }
    247 
    248 void DestroyComponentsInBackend(
    249     NonFrontendDataTypeController::BackendComponentsContainer *container) {
    250   delete container;
    251 }
    252 
    253 void NonFrontendDataTypeController::Stop() {
    254   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    255 
    256   if (state_ == NOT_RUNNING)
    257     return;
    258 
    259   // Deactivate the date type on the UI thread first to stop processing
    260   // sync server changes. This needs to happen before posting task to destroy
    261   // processor and associator on backend. Otherwise it could crash if syncer
    262   // post work to backend after destruction task and that work is run before
    263   // deactivation.
    264   profile_sync_service()->DeactivateDataType(type());
    265 
    266   // Ignore association callback.
    267   weak_ptr_factory_.InvalidateWeakPtrs();
    268 
    269   // Disconnect on UI and post task to destroy on backend.
    270   if (components_container_) {
    271     components_container_->Disconnect();
    272     PostTaskOnBackendThread(
    273           FROM_HERE,
    274           base::Bind(&DestroyComponentsInBackend,
    275                      components_container_.release()));
    276     model_associator_ = NULL;
    277     change_processor_ = NULL;
    278   }
    279 
    280   state_ = NOT_RUNNING;
    281 }
    282 
    283 std::string NonFrontendDataTypeController::name() const {
    284   // For logging only.
    285   return syncer::ModelTypeToString(type());
    286 }
    287 
    288 sync_driver::DataTypeController::State NonFrontendDataTypeController::state()
    289     const {
    290   return state_;
    291 }
    292 
    293 void NonFrontendDataTypeController::OnSingleDataTypeUnrecoverableError(
    294     const syncer::SyncError& error) {
    295   DCHECK(IsOnBackendThread());
    296   DCHECK_EQ(type(), error.model_type());
    297   RecordUnrecoverableError(error.location(), error.message());
    298   BrowserThread::PostTask(BrowserThread::UI, error.location(),
    299       base::Bind(&NonFrontendDataTypeController::DisableImpl,
    300                  this,
    301                  error));
    302 }
    303 
    304 NonFrontendDataTypeController::NonFrontendDataTypeController()
    305     : DataTypeController(base::MessageLoopProxy::current(), base::Closure()),
    306       state_(NOT_RUNNING),
    307       profile_sync_factory_(NULL),
    308       profile_(NULL),
    309       profile_sync_service_(NULL),
    310       model_associator_(NULL),
    311       change_processor_(NULL),
    312       weak_ptr_factory_(this) {
    313 }
    314 
    315 NonFrontendDataTypeController::~NonFrontendDataTypeController() {
    316   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    317   DCHECK(!change_processor_);
    318   DCHECK(!model_associator_);
    319 }
    320 
    321 bool NonFrontendDataTypeController::StartModels() {
    322   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    323   DCHECK_EQ(state_, MODEL_STARTING);
    324   // By default, no additional services need to be started before we can proceed
    325   // with model association, so do nothing.
    326   return true;
    327 }
    328 
    329 bool NonFrontendDataTypeController::IsOnBackendThread() {
    330   return !BrowserThread::CurrentlyOn(BrowserThread::UI);
    331 }
    332 
    333 void NonFrontendDataTypeController::StartDone(
    334     DataTypeController::ConfigureResult start_result,
    335     const syncer::SyncMergeResult& local_merge_result,
    336     const syncer::SyncMergeResult& syncer_merge_result) {
    337   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    338   DataTypeController::State new_state;
    339 
    340   if (IsSuccessfulResult(start_result)) {
    341     new_state = RUNNING;
    342   } else {
    343     new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
    344     if (IsUnrecoverableResult(start_result))
    345       RecordUnrecoverableError(FROM_HERE, "StartFailed");
    346   }
    347 
    348   StartDoneImpl(start_result, new_state, local_merge_result,
    349                 syncer_merge_result);
    350 }
    351 
    352 void NonFrontendDataTypeController::StartDoneImpl(
    353     DataTypeController::ConfigureResult start_result,
    354     DataTypeController::State new_state,
    355     const syncer::SyncMergeResult& local_merge_result,
    356     const syncer::SyncMergeResult& syncer_merge_result) {
    357   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    358 
    359   state_ = new_state;
    360   if (state_ != RUNNING) {
    361     // Start failed.
    362     RecordStartFailure(start_result);
    363   }
    364 
    365   start_callback_.Run(start_result, local_merge_result, syncer_merge_result);
    366 }
    367 
    368 void NonFrontendDataTypeController::DisableImpl(
    369     const syncer::SyncError& error) {
    370   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    371   if (!model_load_callback_.is_null()) {
    372     model_load_callback_.Run(type(), error);
    373   }
    374 }
    375 
    376 void NonFrontendDataTypeController::RecordAssociationTime(
    377     base::TimeDelta time) {
    378   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    379 #define PER_DATA_TYPE_MACRO(type_str) \
    380     UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
    381   SYNC_DATA_TYPE_HISTOGRAM(type());
    382 #undef PER_DATA_TYPE_MACRO
    383 }
    384 
    385 void NonFrontendDataTypeController::RecordStartFailure(ConfigureResult result) {
    386   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    387   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures",
    388                             ModelTypeToHistogramInt(type()),
    389                             syncer::MODEL_TYPE_COUNT);
    390 #define PER_DATA_TYPE_MACRO(type_str) \
    391     UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \
    392                               MAX_START_RESULT);
    393   SYNC_DATA_TYPE_HISTOGRAM(type());
    394 #undef PER_DATA_TYPE_MACRO
    395 }
    396 
    397 void NonFrontendDataTypeController::RecordUnrecoverableError(
    398     const tracked_objects::Location& from_here,
    399     const std::string& message) {
    400   DVLOG(1) << "Datatype Controller failed for type "
    401            << ModelTypeToString(type()) << "  "
    402            << message << " at location "
    403            << from_here.ToString();
    404   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures",
    405                             ModelTypeToHistogramInt(type()),
    406                             syncer::MODEL_TYPE_COUNT);
    407 
    408   if (!error_callback_.is_null())
    409     error_callback_.Run();
    410 }
    411 
    412 
    413 ProfileSyncComponentsFactory*
    414     NonFrontendDataTypeController::profile_sync_factory() const {
    415   return profile_sync_factory_;
    416 }
    417 
    418 Profile* NonFrontendDataTypeController::profile() const {
    419   return profile_;
    420 }
    421 
    422 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service()
    423     const {
    424   return profile_sync_service_;
    425 }
    426 
    427 void NonFrontendDataTypeController::set_start_callback(
    428     const StartCallback& callback) {
    429   start_callback_ = callback;
    430 }
    431 
    432 void NonFrontendDataTypeController::set_state(State state) {
    433   state_ = state;
    434 }
    435 
    436 sync_driver::AssociatorInterface* NonFrontendDataTypeController::associator()
    437     const {
    438   return model_associator_;
    439 }
    440 
    441 sync_driver::ChangeProcessor*
    442 NonFrontendDataTypeController::GetChangeProcessor() const {
    443   return change_processor_;
    444 }
    445 
    446 void NonFrontendDataTypeController::AssociationCallback(
    447     AssociationResult result) {
    448   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    449 
    450   if (result.needs_crypto) {
    451     StartDone(NEEDS_CRYPTO,
    452               result.local_merge_result,
    453               result.syncer_merge_result);
    454     return;
    455   }
    456 
    457   if (result.unrecoverable_error) {
    458     StartDone(UNRECOVERABLE_ERROR,
    459               result.local_merge_result,
    460               result.syncer_merge_result);
    461     return;
    462   }
    463 
    464   RecordAssociationTime(result.association_time);
    465   if (result.error.IsSet()) {
    466     StartDone(ASSOCIATION_FAILED,
    467               result.local_merge_result,
    468               result.syncer_merge_result);
    469     return;
    470   }
    471 
    472   CHECK(result.change_processor);
    473   CHECK(result.model_associator);
    474   change_processor_ = result.change_processor;
    475   model_associator_ = result.model_associator;
    476 
    477   StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK,
    478             result.local_merge_result,
    479             result.syncer_merge_result);
    480 }
    481 
    482 }  // namespace browser_sync
    483