Home | History | Annotate | Download | only in sync_driver
      1 // Copyright 2014 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 "components/sync_driver/ui_data_type_controller.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/weak_ptr.h"
      9 #include "components/sync_driver/generic_change_processor_factory.h"
     10 #include "components/sync_driver/shared_change_processor_ref.h"
     11 #include "sync/api/sync_error.h"
     12 #include "sync/api/syncable_service.h"
     13 #include "sync/internal_api/public/base/model_type.h"
     14 #include "sync/util/data_type_histogram.h"
     15 
     16 namespace browser_sync {
     17 
     18 UIDataTypeController::UIDataTypeController()
     19     : DataTypeController(base::MessageLoopProxy::current(),
     20                          base::Closure(),
     21                          DisableTypeCallback()),
     22       sync_factory_(NULL),
     23       state_(NOT_RUNNING),
     24       type_(syncer::UNSPECIFIED) {
     25 }
     26 
     27 UIDataTypeController::UIDataTypeController(
     28     scoped_refptr<base::MessageLoopProxy> ui_thread,
     29     const base::Closure& error_callback,
     30     const DisableTypeCallback& disable_callback,
     31     syncer::ModelType type,
     32     SyncApiComponentFactory* sync_factory)
     33     : DataTypeController(ui_thread, error_callback, disable_callback),
     34       sync_factory_(sync_factory),
     35       state_(NOT_RUNNING),
     36       type_(type),
     37       processor_factory_(new GenericChangeProcessorFactory()),
     38       ui_thread_(ui_thread) {
     39   DCHECK(ui_thread_->BelongsToCurrentThread());
     40   DCHECK(sync_factory);
     41   DCHECK(syncer::IsRealDataType(type_));
     42 }
     43 
     44 void UIDataTypeController::SetGenericChangeProcessorFactoryForTest(
     45       scoped_ptr<GenericChangeProcessorFactory> factory) {
     46   DCHECK_EQ(state_, NOT_RUNNING);
     47   processor_factory_ = factory.Pass();
     48 }
     49 
     50 UIDataTypeController::~UIDataTypeController() {
     51   DCHECK(ui_thread_->BelongsToCurrentThread());
     52 }
     53 
     54 void UIDataTypeController::LoadModels(
     55     const ModelLoadCallback& model_load_callback) {
     56   DCHECK(ui_thread_->BelongsToCurrentThread());
     57   DCHECK(!model_load_callback.is_null());
     58   DCHECK(syncer::IsRealDataType(type_));
     59   if (state_ != NOT_RUNNING) {
     60     model_load_callback.Run(type(),
     61                             syncer::SyncError(FROM_HERE,
     62                                               syncer::SyncError::DATATYPE_ERROR,
     63                                               "Model already loaded",
     64                                               type()));
     65     return;
     66   }
     67   // Since we can't be called multiple times before Stop() is called,
     68   // |shared_change_processor_| must be NULL here.
     69   DCHECK(!shared_change_processor_.get());
     70   shared_change_processor_ = new SharedChangeProcessor();
     71 
     72   model_load_callback_ = model_load_callback;
     73   state_ = MODEL_STARTING;
     74   if (!StartModels()) {
     75     // If we are waiting for some external service to load before associating
     76     // or we failed to start the models, we exit early. state_ will control
     77     // what we perform next.
     78     DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING);
     79     return;
     80   }
     81 
     82   state_ = MODEL_LOADED;
     83   model_load_callback_.Reset();
     84   model_load_callback.Run(type(), syncer::SyncError());
     85 }
     86 
     87 void UIDataTypeController::OnModelLoaded() {
     88   DCHECK(ui_thread_->BelongsToCurrentThread());
     89   DCHECK(!model_load_callback_.is_null());
     90   DCHECK_EQ(state_, MODEL_STARTING);
     91 
     92   state_ = MODEL_LOADED;
     93   ModelLoadCallback model_load_callback = model_load_callback_;
     94   model_load_callback_.Reset();
     95   model_load_callback.Run(type(), syncer::SyncError());
     96 }
     97 
     98 void UIDataTypeController::StartAssociating(
     99     const StartCallback& start_callback) {
    100   DCHECK(ui_thread_->BelongsToCurrentThread());
    101   DCHECK(!start_callback.is_null());
    102   DCHECK_EQ(state_, MODEL_LOADED);
    103 
    104   start_callback_ = start_callback;
    105   state_ = ASSOCIATING;
    106   Associate();
    107   // It's possible StartDone(..) resulted in a Stop() call, or that association
    108   // failed, so we just verify that the state has moved foward.
    109   DCHECK_NE(state_, ASSOCIATING);
    110 }
    111 
    112 bool UIDataTypeController::StartModels() {
    113   DCHECK_EQ(state_, MODEL_STARTING);
    114   // By default, no additional services need to be started before we can proceed
    115   // with model association.
    116   return true;
    117 }
    118 
    119 void UIDataTypeController::Associate() {
    120   DCHECK_EQ(state_, ASSOCIATING);
    121   syncer::SyncMergeResult local_merge_result(type());
    122   syncer::SyncMergeResult syncer_merge_result(type());
    123   base::WeakPtrFactory<syncer::SyncMergeResult> weak_ptr_factory(
    124       &syncer_merge_result);
    125 
    126   // Connect |shared_change_processor_| to the syncer and get the
    127   // syncer::SyncableService associated with type().
    128   local_service_ = shared_change_processor_->Connect(
    129       sync_factory_,
    130       processor_factory_.get(),
    131       user_share(),
    132       this,
    133       type(),
    134       weak_ptr_factory.GetWeakPtr());
    135   if (!local_service_.get()) {
    136     syncer::SyncError error(FROM_HERE,
    137                             syncer::SyncError::DATATYPE_ERROR,
    138                             "Failed to connect to syncer.",
    139                             type());
    140     local_merge_result.set_error(error);
    141     StartDone(ASSOCIATION_FAILED,
    142               local_merge_result,
    143               syncer_merge_result);
    144     return;
    145   }
    146 
    147   if (!shared_change_processor_->CryptoReadyIfNecessary()) {
    148     StartDone(NEEDS_CRYPTO,
    149               local_merge_result,
    150               syncer_merge_result);
    151     return;
    152   }
    153 
    154   bool sync_has_nodes = false;
    155   if (!shared_change_processor_->SyncModelHasUserCreatedNodes(
    156           &sync_has_nodes)) {
    157     syncer::SyncError error(FROM_HERE,
    158                             syncer::SyncError::UNRECOVERABLE_ERROR,
    159                             "Failed to load sync nodes",
    160                             type());
    161     local_merge_result.set_error(error);
    162     StartDone(UNRECOVERABLE_ERROR,
    163               local_merge_result,
    164               syncer_merge_result);
    165     return;
    166   }
    167 
    168   base::TimeTicks start_time = base::TimeTicks::Now();
    169   syncer::SyncDataList initial_sync_data;
    170   syncer::SyncError error =
    171       shared_change_processor_->GetAllSyncDataReturnError(
    172           type(), &initial_sync_data);
    173   if (error.IsSet()) {
    174     local_merge_result.set_error(error);
    175     StartDone(ASSOCIATION_FAILED,
    176               local_merge_result,
    177               syncer_merge_result);
    178     return;
    179   }
    180 
    181   std::string datatype_context;
    182   if (shared_change_processor_->GetDataTypeContext(&datatype_context)) {
    183     local_service_->UpdateDataTypeContext(
    184         type(), syncer::SyncChangeProcessor::NO_REFRESH, datatype_context);
    185   }
    186 
    187   syncer_merge_result.set_num_items_before_association(
    188       initial_sync_data.size());
    189   // Passes a reference to |shared_change_processor_|.
    190   local_merge_result = local_service_->MergeDataAndStartSyncing(
    191       type(),
    192       initial_sync_data,
    193       scoped_ptr<syncer::SyncChangeProcessor>(
    194           new SharedChangeProcessorRef(shared_change_processor_)),
    195       scoped_ptr<syncer::SyncErrorFactory>(
    196           new SharedChangeProcessorRef(shared_change_processor_)));
    197   RecordAssociationTime(base::TimeTicks::Now() - start_time);
    198   if (local_merge_result.error().IsSet()) {
    199     StartDone(ASSOCIATION_FAILED,
    200               local_merge_result,
    201               syncer_merge_result);
    202     return;
    203   }
    204 
    205   syncer_merge_result.set_num_items_after_association(
    206       shared_change_processor_->GetSyncCount());
    207 
    208   state_ = RUNNING;
    209   StartDone(sync_has_nodes ? OK : OK_FIRST_RUN,
    210             local_merge_result,
    211             syncer_merge_result);
    212 }
    213 
    214 ChangeProcessor* UIDataTypeController::GetChangeProcessor() const {
    215   DCHECK_EQ(state_, RUNNING);
    216   return shared_change_processor_->generic_change_processor();
    217 }
    218 
    219 void UIDataTypeController::AbortModelLoad() {
    220   DCHECK(ui_thread_->BelongsToCurrentThread());
    221   state_ = NOT_RUNNING;
    222 
    223   if (shared_change_processor_.get()) {
    224     shared_change_processor_ = NULL;
    225   }
    226 
    227   ModelLoadCallback model_load_callback = model_load_callback_;
    228   model_load_callback_.Reset();
    229   model_load_callback.Run(type(),
    230                           syncer::SyncError(FROM_HERE,
    231                                             syncer::SyncError::DATATYPE_ERROR,
    232                                             "Aborted",
    233                                             type()));
    234   // We don't want to continue loading models (e.g OnModelLoaded should never be
    235   // called after we've decided to abort).
    236   StopModels();
    237 }
    238 
    239 void UIDataTypeController::StartDone(
    240     StartResult start_result,
    241     const syncer::SyncMergeResult& local_merge_result,
    242     const syncer::SyncMergeResult& syncer_merge_result) {
    243   DCHECK(ui_thread_->BelongsToCurrentThread());
    244 
    245   if (!IsSuccessfulResult(start_result)) {
    246     StopModels();
    247     if (start_result == ASSOCIATION_FAILED) {
    248       state_ = DISABLED;
    249     } else {
    250       state_ = NOT_RUNNING;
    251     }
    252     RecordStartFailure(start_result);
    253 
    254     if (shared_change_processor_.get()) {
    255       shared_change_processor_->Disconnect();
    256       shared_change_processor_ = NULL;
    257     }
    258   }
    259 
    260   // We have to release the callback before we call it, since it's possible
    261   // invoking the callback will trigger a call to Stop(), which will get
    262   // confused by the non-NULL start_callback_.
    263   StartCallback callback = start_callback_;
    264   start_callback_.Reset();
    265   callback.Run(start_result, local_merge_result, syncer_merge_result);
    266 }
    267 
    268 void UIDataTypeController::Stop() {
    269   DCHECK(ui_thread_->BelongsToCurrentThread());
    270   DCHECK(syncer::IsRealDataType(type_));
    271 
    272   State prev_state = state_;
    273   state_ = STOPPING;
    274 
    275   if (shared_change_processor_.get()) {
    276     shared_change_processor_->Disconnect();
    277     shared_change_processor_ = NULL;
    278   }
    279 
    280   // If Stop() is called while Start() is waiting for the datatype model to
    281   // load, abort the start.
    282   if (prev_state == MODEL_STARTING) {
    283     AbortModelLoad();
    284     // We can just return here since we haven't performed association if we're
    285     // still in MODEL_STARTING.
    286     return;
    287   }
    288   DCHECK(start_callback_.is_null());
    289 
    290   StopModels();
    291 
    292   if (local_service_.get()) {
    293     local_service_->StopSyncing(type());
    294   }
    295 
    296   state_ = NOT_RUNNING;
    297 }
    298 
    299 syncer::ModelType UIDataTypeController::type() const {
    300   DCHECK(syncer::IsRealDataType(type_));
    301   return type_;
    302 }
    303 
    304 void UIDataTypeController::StopModels() {
    305   // Do nothing by default.
    306 }
    307 
    308 syncer::ModelSafeGroup UIDataTypeController::model_safe_group() const {
    309   DCHECK(syncer::IsRealDataType(type_));
    310   return syncer::GROUP_UI;
    311 }
    312 
    313 std::string UIDataTypeController::name() const {
    314   // For logging only.
    315   return syncer::ModelTypeToString(type());
    316 }
    317 
    318 DataTypeController::State UIDataTypeController::state() const {
    319   return state_;
    320 }
    321 
    322 void UIDataTypeController::OnSingleDatatypeUnrecoverableError(
    323     const tracked_objects::Location& from_here, const std::string& message) {
    324   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures",
    325                             ModelTypeToHistogramInt(type()),
    326                             syncer::MODEL_TYPE_COUNT);
    327   // TODO(tim): We double-upload some errors.  See bug 383480.
    328   if (!error_callback_.is_null())
    329     error_callback_.Run();
    330   if (!disable_callback().is_null())
    331     disable_callback().Run(from_here, message);
    332 }
    333 
    334 void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) {
    335   DCHECK(ui_thread_->BelongsToCurrentThread());
    336 #define PER_DATA_TYPE_MACRO(type_str) \
    337     UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
    338   SYNC_DATA_TYPE_HISTOGRAM(type());
    339 #undef PER_DATA_TYPE_MACRO
    340 }
    341 
    342 void UIDataTypeController::RecordStartFailure(StartResult result) {
    343   DCHECK(ui_thread_->BelongsToCurrentThread());
    344   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures",
    345                             ModelTypeToHistogramInt(type()),
    346                             syncer::MODEL_TYPE_COUNT);
    347 #define PER_DATA_TYPE_MACRO(type_str) \
    348     UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \
    349                               MAX_START_RESULT);
    350   SYNC_DATA_TYPE_HISTOGRAM(type());
    351 #undef PER_DATA_TYPE_MACRO
    352 }
    353 
    354 }  // namespace browser_sync
    355