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<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(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 passing null DisableTypeCallback and still having
    161 // a dependency on ProfileSyncService.  That dep can probably be removed
    162 // without too much work.
    163 NonFrontendDataTypeController::NonFrontendDataTypeController(
    164     scoped_refptr<base::MessageLoopProxy> ui_thread,
    165     const base::Closure& error_callback,
    166     ProfileSyncComponentsFactory* profile_sync_factory,
    167     Profile* profile,
    168     ProfileSyncService* sync_service)
    169     : DataTypeController(ui_thread, error_callback, DisableTypeCallback()),
    170       state_(NOT_RUNNING),
    171       profile_sync_factory_(profile_sync_factory),
    172       profile_(profile),
    173       profile_sync_service_(sync_service),
    174       model_associator_(NULL),
    175       change_processor_(NULL),
    176       weak_ptr_factory_(this) {
    177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    178   DCHECK(profile_sync_factory_);
    179   DCHECK(profile_);
    180   DCHECK(profile_sync_service_);
    181 }
    182 
    183 void NonFrontendDataTypeController::LoadModels(
    184     const ModelLoadCallback& model_load_callback) {
    185   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    186   DCHECK(!model_load_callback.is_null());
    187   if (state_ != NOT_RUNNING) {
    188     model_load_callback.Run(type(),
    189                             syncer::SyncError(FROM_HERE,
    190                                               syncer::SyncError::DATATYPE_ERROR,
    191                                               "Model already loaded",
    192                                               type()));
    193     return;
    194   }
    195 
    196   state_ = MODEL_STARTING;
    197   if (!StartModels()) {
    198     // We failed to start the models. There is no point in waiting.
    199     // Note: This code is deprecated. The only 2 datatypes here,
    200     // passwords and typed urls, dont have any special loading. So if we
    201     // get a false it means they failed.
    202     DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING
    203            || state_ == DISABLED);
    204     model_load_callback.Run(type(),
    205                             syncer::SyncError(FROM_HERE,
    206                                               syncer::SyncError::DATATYPE_ERROR,
    207                                               "Failed loading",
    208                                               type()));
    209     return;
    210   }
    211   state_ = MODEL_LOADED;
    212 
    213   model_load_callback.Run(type(), syncer::SyncError());
    214 }
    215 
    216 void NonFrontendDataTypeController::OnModelLoaded() {
    217   NOTREACHED();
    218 }
    219 
    220 void NonFrontendDataTypeController::StartAssociating(
    221     const StartCallback& start_callback) {
    222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    223   DCHECK(!start_callback.is_null());
    224   DCHECK(!components_container_);
    225   DCHECK_EQ(state_, MODEL_LOADED);
    226 
    227   // Kick off association on the thread the datatype resides on.
    228   state_ = ASSOCIATING;
    229   start_callback_ = start_callback;
    230 
    231   components_container_.reset(new BackendComponentsContainer(this));
    232 
    233   if (!PostTaskOnBackendThread(
    234       FROM_HERE,
    235       base::Bind(&BackendComponentsContainer::Run,
    236                  base::Unretained(components_container_.get())))) {
    237     syncer::SyncError error(
    238         FROM_HERE,
    239         syncer::SyncError::DATATYPE_ERROR,
    240         "Failed to post StartAssociation", type());
    241     syncer::SyncMergeResult local_merge_result(type());
    242     local_merge_result.set_error(error);
    243     StartDone(ASSOCIATION_FAILED,
    244               local_merge_result,
    245               syncer::SyncMergeResult(type()));
    246   }
    247 }
    248 
    249 void DestroyComponentsInBackend(
    250     NonFrontendDataTypeController::BackendComponentsContainer *container) {
    251   delete container;
    252 }
    253 
    254 void NonFrontendDataTypeController::Stop() {
    255   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    256   DCHECK_NE(state_, NOT_RUNNING);
    257 
    258   // Deactivate the date type on the UI thread first to stop processing
    259   // sync server changes. This needs to happen before posting task to destroy
    260   // processor and associator on backend. Otherwise it could crash if syncer
    261   // post work to backend after destruction task and that work is run before
    262   // deactivation.
    263   profile_sync_service()->DeactivateDataType(type());
    264 
    265   // Ignore association callback.
    266   weak_ptr_factory_.InvalidateWeakPtrs();
    267 
    268   // Disconnect on UI and post task to destroy on backend.
    269   if (components_container_) {
    270     components_container_->Disconnect();
    271     PostTaskOnBackendThread(
    272           FROM_HERE,
    273           base::Bind(&DestroyComponentsInBackend,
    274                      components_container_.release()));
    275     model_associator_ = NULL;
    276     change_processor_ = NULL;
    277   }
    278 
    279   // Call start callback if waiting for association.
    280   if (state_ == ASSOCIATING) {
    281     StartDone(ABORTED,
    282               syncer::SyncMergeResult(type()),
    283               syncer::SyncMergeResult(type()));
    284 
    285   }
    286 
    287   state_ = NOT_RUNNING;
    288 }
    289 
    290 std::string NonFrontendDataTypeController::name() const {
    291   // For logging only.
    292   return syncer::ModelTypeToString(type());
    293 }
    294 
    295 DataTypeController::State NonFrontendDataTypeController::state() const {
    296   return state_;
    297 }
    298 
    299 void NonFrontendDataTypeController::OnSingleDatatypeUnrecoverableError(
    300     const tracked_objects::Location& from_here,
    301     const std::string& message) {
    302   DCHECK(IsOnBackendThread());
    303   RecordUnrecoverableError(from_here, message);
    304   BrowserThread::PostTask(BrowserThread::UI, from_here,
    305       base::Bind(&NonFrontendDataTypeController::DisableImpl,
    306                  this,
    307                  from_here,
    308                  message));
    309 }
    310 
    311 NonFrontendDataTypeController::NonFrontendDataTypeController()
    312     : DataTypeController(base::MessageLoopProxy::current(), base::Closure(),
    313                          DisableTypeCallback()),
    314       state_(NOT_RUNNING),
    315       profile_sync_factory_(NULL),
    316       profile_(NULL),
    317       profile_sync_service_(NULL),
    318       model_associator_(NULL),
    319       change_processor_(NULL),
    320       weak_ptr_factory_(this) {
    321 }
    322 
    323 NonFrontendDataTypeController::~NonFrontendDataTypeController() {
    324   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    325   DCHECK(!change_processor_);
    326   DCHECK(!model_associator_);
    327 }
    328 
    329 bool NonFrontendDataTypeController::StartModels() {
    330   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    331   DCHECK_EQ(state_, MODEL_STARTING);
    332   // By default, no additional services need to be started before we can proceed
    333   // with model association, so do nothing.
    334   return true;
    335 }
    336 
    337 bool NonFrontendDataTypeController::IsOnBackendThread() {
    338   return !BrowserThread::CurrentlyOn(BrowserThread::UI);
    339 }
    340 
    341 void NonFrontendDataTypeController::StartDone(
    342     DataTypeController::StartResult start_result,
    343     const syncer::SyncMergeResult& local_merge_result,
    344     const syncer::SyncMergeResult& syncer_merge_result) {
    345   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    346   DataTypeController::State new_state;
    347 
    348   if (IsSuccessfulResult(start_result)) {
    349     new_state = RUNNING;
    350   } else {
    351     new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
    352     if (IsUnrecoverableResult(start_result))
    353       RecordUnrecoverableError(FROM_HERE, "StartFailed");
    354   }
    355 
    356   StartDoneImpl(start_result, new_state, local_merge_result,
    357                 syncer_merge_result);
    358 }
    359 
    360 void NonFrontendDataTypeController::StartDoneImpl(
    361     DataTypeController::StartResult start_result,
    362     DataTypeController::State new_state,
    363     const syncer::SyncMergeResult& local_merge_result,
    364     const syncer::SyncMergeResult& syncer_merge_result) {
    365   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    366 
    367   state_ = new_state;
    368   if (state_ != RUNNING) {
    369     // Start failed.
    370     RecordStartFailure(start_result);
    371   }
    372 
    373   DCHECK(!start_callback_.is_null());
    374   // We have to release the callback before we call it, since it's possible
    375   // invoking the callback will trigger a call to STOP(), which will get
    376   // confused by the non-NULL start_callback_.
    377   StartCallback callback = start_callback_;
    378   start_callback_.Reset();
    379   callback.Run(start_result, local_merge_result, syncer_merge_result);
    380 }
    381 
    382 void NonFrontendDataTypeController::DisableImpl(
    383     const tracked_objects::Location& from_here,
    384     const std::string& message) {
    385   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    386   profile_sync_service_->DisableDatatype(type(), from_here, message);
    387 }
    388 
    389 void NonFrontendDataTypeController::RecordAssociationTime(
    390     base::TimeDelta time) {
    391   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    392 #define PER_DATA_TYPE_MACRO(type_str) \
    393     UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
    394   SYNC_DATA_TYPE_HISTOGRAM(type());
    395 #undef PER_DATA_TYPE_MACRO
    396 }
    397 
    398 void NonFrontendDataTypeController::RecordStartFailure(StartResult result) {
    399   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    400   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures",
    401                             ModelTypeToHistogramInt(type()),
    402                             syncer::MODEL_TYPE_COUNT);
    403 #define PER_DATA_TYPE_MACRO(type_str) \
    404     UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \
    405                               MAX_START_RESULT);
    406   SYNC_DATA_TYPE_HISTOGRAM(type());
    407 #undef PER_DATA_TYPE_MACRO
    408 }
    409 
    410 void NonFrontendDataTypeController::RecordUnrecoverableError(
    411     const tracked_objects::Location& from_here,
    412     const std::string& message) {
    413   DVLOG(1) << "Datatype Controller failed for type "
    414            << ModelTypeToString(type()) << "  "
    415            << message << " at location "
    416            << from_here.ToString();
    417   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures",
    418                             ModelTypeToHistogramInt(type()),
    419                             syncer::MODEL_TYPE_COUNT);
    420 
    421   if (!error_callback_.is_null())
    422     error_callback_.Run();
    423 }
    424 
    425 
    426 ProfileSyncComponentsFactory*
    427     NonFrontendDataTypeController::profile_sync_factory() const {
    428   return profile_sync_factory_;
    429 }
    430 
    431 Profile* NonFrontendDataTypeController::profile() const {
    432   return profile_;
    433 }
    434 
    435 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service()
    436     const {
    437   return profile_sync_service_;
    438 }
    439 
    440 void NonFrontendDataTypeController::set_start_callback(
    441     const StartCallback& callback) {
    442   start_callback_ = callback;
    443 }
    444 
    445 void NonFrontendDataTypeController::set_state(State state) {
    446   state_ = state;
    447 }
    448 
    449 AssociatorInterface* NonFrontendDataTypeController::associator() const {
    450   return model_associator_;
    451 }
    452 
    453 ChangeProcessor* NonFrontendDataTypeController::GetChangeProcessor() const {
    454   return change_processor_;
    455 }
    456 
    457 void NonFrontendDataTypeController::AssociationCallback(
    458     AssociationResult result) {
    459   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    460 
    461   if (result.needs_crypto) {
    462     StartDone(NEEDS_CRYPTO,
    463               result.local_merge_result,
    464               result.syncer_merge_result);
    465     return;
    466   }
    467 
    468   if (result.unrecoverable_error) {
    469     StartDone(UNRECOVERABLE_ERROR,
    470               result.local_merge_result,
    471               result.syncer_merge_result);
    472     return;
    473   }
    474 
    475   RecordAssociationTime(result.association_time);
    476   if (result.error.IsSet()) {
    477     StartDone(ASSOCIATION_FAILED,
    478               result.local_merge_result,
    479               result.syncer_merge_result);
    480     return;
    481   }
    482 
    483   CHECK(result.change_processor);
    484   CHECK(result.model_associator);
    485   change_processor_ = result.change_processor;
    486   model_associator_ = result.model_associator;
    487 
    488   StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK,
    489             result.local_merge_result,
    490             result.syncer_merge_result);
    491 }
    492 
    493 }  // namespace browser_sync
    494