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/change_processor.h"
     12 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
     13 #include "chrome/browser/sync/glue/model_associator.h"
     14 #include "chrome/browser/sync/profile_sync_components_factory.h"
     15 #include "chrome/browser/sync/profile_sync_service.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<AssociatorInterface> model_associator_;
     48   scoped_ptr<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(!BrowserThread::CurrentlyOn(BrowserThread::UI));
     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 NonFrontendDataTypeController::NonFrontendDataTypeController(
    160     ProfileSyncComponentsFactory* profile_sync_factory,
    161     Profile* profile,
    162     ProfileSyncService* sync_service)
    163     : state_(NOT_RUNNING),
    164       profile_sync_factory_(profile_sync_factory),
    165       profile_(profile),
    166       profile_sync_service_(sync_service),
    167       model_associator_(NULL),
    168       change_processor_(NULL),
    169       weak_ptr_factory_(this) {
    170   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    171   DCHECK(profile_sync_factory_);
    172   DCHECK(profile_);
    173   DCHECK(profile_sync_service_);
    174 }
    175 
    176 void NonFrontendDataTypeController::LoadModels(
    177     const ModelLoadCallback& model_load_callback) {
    178   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    179   DCHECK(!model_load_callback.is_null());
    180   if (state_ != NOT_RUNNING) {
    181     model_load_callback.Run(type(),
    182                             syncer::SyncError(FROM_HERE,
    183                                               syncer::SyncError::DATATYPE_ERROR,
    184                                               "Model already loaded",
    185                                               type()));
    186     return;
    187   }
    188 
    189   state_ = MODEL_STARTING;
    190   if (!StartModels()) {
    191     // We failed to start the models. There is no point in waiting.
    192     // Note: This code is deprecated. The only 2 datatypes here,
    193     // passwords and typed urls, dont have any special loading. So if we
    194     // get a false it means they failed.
    195     DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING
    196            || state_ == DISABLED);
    197     model_load_callback.Run(type(),
    198                             syncer::SyncError(FROM_HERE,
    199                                               syncer::SyncError::DATATYPE_ERROR,
    200                                               "Failed loading",
    201                                               type()));
    202     return;
    203   }
    204   state_ = MODEL_LOADED;
    205 
    206   model_load_callback.Run(type(), syncer::SyncError());
    207 }
    208 
    209 void NonFrontendDataTypeController::OnModelLoaded() {
    210   NOTREACHED();
    211 }
    212 
    213 void NonFrontendDataTypeController::StartAssociating(
    214     const StartCallback& start_callback) {
    215   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    216   DCHECK(!start_callback.is_null());
    217   DCHECK(!components_container_);
    218   DCHECK_EQ(state_, MODEL_LOADED);
    219 
    220   // Kick off association on the thread the datatype resides on.
    221   state_ = ASSOCIATING;
    222   start_callback_ = start_callback;
    223 
    224   components_container_.reset(new BackendComponentsContainer(this));
    225 
    226   if (!PostTaskOnBackendThread(
    227       FROM_HERE,
    228       base::Bind(&BackendComponentsContainer::Run,
    229                  base::Unretained(components_container_.get())))) {
    230     syncer::SyncError error(
    231         FROM_HERE,
    232         syncer::SyncError::DATATYPE_ERROR,
    233         "Failed to post StartAssociation", type());
    234     syncer::SyncMergeResult local_merge_result(type());
    235     local_merge_result.set_error(error);
    236     StartDone(ASSOCIATION_FAILED,
    237               local_merge_result,
    238               syncer::SyncMergeResult(type()));
    239   }
    240 }
    241 
    242 void DestroyComponentsInBackend(
    243     NonFrontendDataTypeController::BackendComponentsContainer *containter) {
    244   delete containter;
    245 }
    246 
    247 void NonFrontendDataTypeController::Stop() {
    248   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    249   DCHECK_NE(state_, NOT_RUNNING);
    250 
    251   // Deactivate the date type on the UI thread first to stop processing
    252   // sync server changes. This needs to happen before posting task to destroy
    253   // processor and associator on backend. Otherwise it could crash if syncer
    254   // post work to backend after destruction task and that work is run before
    255   // deactivation.
    256   profile_sync_service()->DeactivateDataType(type());
    257 
    258   // Ignore association callback.
    259   weak_ptr_factory_.InvalidateWeakPtrs();
    260 
    261   // Disconnect on UI and post task to destroy on backend.
    262   if (components_container_) {
    263     components_container_->Disconnect();
    264     PostTaskOnBackendThread(
    265           FROM_HERE,
    266           base::Bind(&DestroyComponentsInBackend,
    267                      components_container_.release()));
    268     model_associator_ = NULL;
    269     change_processor_ = NULL;
    270   }
    271 
    272   // Call start callback if waiting for association.
    273   if (state_ == ASSOCIATING) {
    274     StartDone(ABORTED,
    275               syncer::SyncMergeResult(type()),
    276               syncer::SyncMergeResult(type()));
    277 
    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 DataTypeController::State NonFrontendDataTypeController::state() const {
    289   return state_;
    290 }
    291 
    292 void NonFrontendDataTypeController::OnSingleDatatypeUnrecoverableError(
    293     const tracked_objects::Location& from_here,
    294     const std::string& message) {
    295   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
    296   RecordUnrecoverableError(from_here, message);
    297   BrowserThread::PostTask(BrowserThread::UI, from_here,
    298       base::Bind(&NonFrontendDataTypeController::DisableImpl,
    299                  this,
    300                  from_here,
    301                  message));
    302 }
    303 
    304 NonFrontendDataTypeController::NonFrontendDataTypeController()
    305     : state_(NOT_RUNNING),
    306       profile_sync_factory_(NULL),
    307       profile_(NULL),
    308       profile_sync_service_(NULL),
    309       model_associator_(NULL),
    310       change_processor_(NULL),
    311       weak_ptr_factory_(this) {
    312 }
    313 
    314 NonFrontendDataTypeController::~NonFrontendDataTypeController() {
    315   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    316   DCHECK(!change_processor_);
    317   DCHECK(!model_associator_);
    318 }
    319 
    320 bool NonFrontendDataTypeController::StartModels() {
    321   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    322   DCHECK_EQ(state_, MODEL_STARTING);
    323   // By default, no additional services need to be started before we can proceed
    324   // with model association, so do nothing.
    325   return true;
    326 }
    327 
    328 void NonFrontendDataTypeController::StartDone(
    329     DataTypeController::StartResult start_result,
    330     const syncer::SyncMergeResult& local_merge_result,
    331     const syncer::SyncMergeResult& syncer_merge_result) {
    332   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    333   DataTypeController::State new_state;
    334 
    335   if (IsSuccessfulResult(start_result)) {
    336     new_state = RUNNING;
    337   } else {
    338     new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
    339     if (IsUnrecoverableResult(start_result))
    340       RecordUnrecoverableError(FROM_HERE, "StartFailed");
    341   }
    342 
    343   StartDoneImpl(start_result, new_state, local_merge_result,
    344                 syncer_merge_result);
    345 }
    346 
    347 void NonFrontendDataTypeController::StartDoneImpl(
    348     DataTypeController::StartResult start_result,
    349     DataTypeController::State new_state,
    350     const syncer::SyncMergeResult& local_merge_result,
    351     const syncer::SyncMergeResult& syncer_merge_result) {
    352   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    353 
    354   state_ = new_state;
    355   if (state_ != RUNNING) {
    356     // Start failed.
    357     RecordStartFailure(start_result);
    358   }
    359 
    360   DCHECK(!start_callback_.is_null());
    361   // We have to release the callback before we call it, since it's possible
    362   // invoking the callback will trigger a call to STOP(), which will get
    363   // confused by the non-NULL start_callback_.
    364   StartCallback callback = start_callback_;
    365   start_callback_.Reset();
    366   callback.Run(start_result, local_merge_result, syncer_merge_result);
    367 }
    368 
    369 void NonFrontendDataTypeController::DisableImpl(
    370     const tracked_objects::Location& from_here,
    371     const std::string& message) {
    372   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    373   profile_sync_service_->DisableBrokenDatatype(type(), from_here, message);
    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(StartResult 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 ProfileSyncComponentsFactory*
    398     NonFrontendDataTypeController::profile_sync_factory() const {
    399   return profile_sync_factory_;
    400 }
    401 
    402 Profile* NonFrontendDataTypeController::profile() const {
    403   return profile_;
    404 }
    405 
    406 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service()
    407     const {
    408   return profile_sync_service_;
    409 }
    410 
    411 void NonFrontendDataTypeController::set_start_callback(
    412     const StartCallback& callback) {
    413   start_callback_ = callback;
    414 }
    415 
    416 void NonFrontendDataTypeController::set_state(State state) {
    417   state_ = state;
    418 }
    419 
    420 AssociatorInterface* NonFrontendDataTypeController::associator() const {
    421   return model_associator_;
    422 }
    423 
    424 ChangeProcessor* NonFrontendDataTypeController::change_processor() const {
    425   return change_processor_;
    426 }
    427 
    428 void NonFrontendDataTypeController::AssociationCallback(
    429     AssociationResult result) {
    430   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    431 
    432   if (result.needs_crypto) {
    433     StartDone(NEEDS_CRYPTO,
    434               result.local_merge_result,
    435               result.syncer_merge_result);
    436     return;
    437   }
    438 
    439   if (result.unrecoverable_error) {
    440     StartDone(UNRECOVERABLE_ERROR,
    441               result.local_merge_result,
    442               result.syncer_merge_result);
    443     return;
    444   }
    445 
    446   RecordAssociationTime(result.association_time);
    447   if (result.error.IsSet()) {
    448     StartDone(ASSOCIATION_FAILED,
    449               result.local_merge_result,
    450               result.syncer_merge_result);
    451     return;
    452   }
    453 
    454   CHECK(result.change_processor);
    455   CHECK(result.model_associator);
    456   change_processor_ = result.change_processor;
    457   model_associator_ = result.model_associator;
    458 
    459   profile_sync_service_->ActivateDataType(type(), model_safe_group(),
    460                                           change_processor());
    461   StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK,
    462             result.local_merge_result,
    463             result.syncer_merge_result);
    464 }
    465 
    466 }  // namespace browser_sync
    467