Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2011 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/typed_url_data_type_controller.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/task.h"
     10 #include "base/time.h"
     11 #include "chrome/browser/history/history.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/sync/glue/typed_url_change_processor.h"
     14 #include "chrome/browser/sync/glue/typed_url_model_associator.h"
     15 #include "chrome/browser/sync/profile_sync_factory.h"
     16 #include "chrome/browser/sync/profile_sync_service.h"
     17 #include "content/browser/browser_thread.h"
     18 #include "content/common/notification_service.h"
     19 
     20 namespace browser_sync {
     21 
     22 class ControlTask : public HistoryDBTask {
     23  public:
     24   ControlTask(TypedUrlDataTypeController* controller, bool start)
     25     : controller_(controller), start_(start) {}
     26 
     27   virtual bool RunOnDBThread(history::HistoryBackend* backend,
     28                              history::HistoryDatabase* db) {
     29     if (start_) {
     30       controller_->StartImpl(backend);
     31     } else {
     32       controller_->StopImpl();
     33     }
     34 
     35     // Release the reference to the controller.  This ensures that
     36     // the controller isn't held past its lifetime in unit tests.
     37     controller_ = NULL;
     38     return true;
     39   }
     40 
     41   virtual void DoneRunOnMainThread() {}
     42 
     43  protected:
     44   scoped_refptr<TypedUrlDataTypeController> controller_;
     45   bool start_;
     46 };
     47 
     48 TypedUrlDataTypeController::TypedUrlDataTypeController(
     49     ProfileSyncFactory* profile_sync_factory,
     50     Profile* profile,
     51     ProfileSyncService* sync_service)
     52     : profile_sync_factory_(profile_sync_factory),
     53       profile_(profile),
     54       sync_service_(sync_service),
     55       state_(NOT_RUNNING),
     56       abort_association_(false),
     57       abort_association_complete_(false, false),
     58       datatype_stopped_(false, false) {
     59   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     60   DCHECK(profile_sync_factory);
     61   DCHECK(profile);
     62   DCHECK(sync_service);
     63 }
     64 
     65 TypedUrlDataTypeController::~TypedUrlDataTypeController() {
     66   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     67 }
     68 
     69 void TypedUrlDataTypeController::Start(StartCallback* start_callback) {
     70   VLOG(1) << "Starting typed_url data controller.";
     71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     72   DCHECK(start_callback);
     73   if (state_ != NOT_RUNNING || start_callback_.get()) {
     74     start_callback->Run(BUSY, FROM_HERE);
     75     delete start_callback;
     76     return;
     77   }
     78 
     79   start_callback_.reset(start_callback);
     80   abort_association_ = false;
     81 
     82   HistoryService* history = profile_->GetHistoryServiceWithoutCreating();
     83   if (history) {
     84     set_state(ASSOCIATING);
     85     history_service_ = history;
     86     history_service_->ScheduleDBTask(new ControlTask(this, true), this);
     87   } else {
     88     set_state(MODEL_STARTING);
     89     notification_registrar_.Add(this, NotificationType::HISTORY_LOADED,
     90                                 NotificationService::AllSources());
     91   }
     92 }
     93 
     94 void TypedUrlDataTypeController::Observe(NotificationType type,
     95                                          const NotificationSource& source,
     96                                          const NotificationDetails& details) {
     97   VLOG(1) << "History loaded observed.";
     98   notification_registrar_.Remove(this,
     99                                  NotificationType::HISTORY_LOADED,
    100                                  NotificationService::AllSources());
    101 
    102   history_service_ = profile_->GetHistoryServiceWithoutCreating();
    103   DCHECK(history_service_.get());
    104   history_service_->ScheduleDBTask(new ControlTask(this, true), this);
    105 }
    106 
    107 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
    108 // distinguishing chrome shutdown from sync shutdown, we should be able to avoid
    109 // this (http://crbug.com/55662). Further, all this functionality should be
    110 // abstracted to a higher layer, where we could ensure all datatypes are doing
    111 // the same thing (http://crbug.com/76232).
    112 void TypedUrlDataTypeController::Stop() {
    113   VLOG(1) << "Stopping typed_url data type controller.";
    114   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    115 
    116   // If Stop() is called while Start() is waiting for association to
    117   // complete, we need to abort the association and wait for the DB
    118   // thread to finish the StartImpl() task.
    119   if (state_ == ASSOCIATING) {
    120     {
    121       base::AutoLock lock(abort_association_lock_);
    122       abort_association_ = true;
    123       if (model_associator_.get())
    124         model_associator_->AbortAssociation();
    125     }
    126     // Wait for the model association to abort.
    127     abort_association_complete_.Wait();
    128     StartDoneImpl(ABORTED, STOPPING);
    129   }
    130 
    131   // If Stop() is called while Start() is waiting for the history service to
    132   // load, abort the start.
    133   if (state_ == MODEL_STARTING)
    134     StartDoneImpl(ABORTED, STOPPING);
    135 
    136   DCHECK(!start_callback_.get());
    137 
    138   if (change_processor_ != NULL)
    139     sync_service_->DeactivateDataType(this, change_processor_.get());
    140 
    141   set_state(NOT_RUNNING);
    142   DCHECK(history_service_.get());
    143   history_service_->ScheduleDBTask(new ControlTask(this, false), this);
    144   datatype_stopped_.Wait();
    145 }
    146 
    147 bool TypedUrlDataTypeController::enabled() {
    148   return true;
    149 }
    150 
    151 syncable::ModelType TypedUrlDataTypeController::type() const {
    152   return syncable::TYPED_URLS;
    153 }
    154 
    155 browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group()
    156     const {
    157   return browser_sync::GROUP_HISTORY;
    158 }
    159 
    160 std::string TypedUrlDataTypeController::name() const {
    161   // For logging only.
    162   return "typed_url";
    163 }
    164 
    165 DataTypeController::State TypedUrlDataTypeController::state() const {
    166   return state_;
    167 }
    168 
    169 void TypedUrlDataTypeController::StartImpl(history::HistoryBackend* backend) {
    170   VLOG(1) << "TypedUrl data type controller StartImpl called.";
    171   // No additional services need to be started before we can proceed
    172   // with model association.
    173   {
    174     base::AutoLock lock(abort_association_lock_);
    175     if (abort_association_) {
    176       abort_association_complete_.Signal();
    177       return;
    178     }
    179     ProfileSyncFactory::SyncComponents sync_components =
    180         profile_sync_factory_->CreateTypedUrlSyncComponents(
    181             sync_service_,
    182             backend,
    183             this);
    184     model_associator_.reset(sync_components.model_associator);
    185     change_processor_.reset(sync_components.change_processor);
    186   }
    187 
    188   if (!model_associator_->CryptoReadyIfNecessary()) {
    189     StartFailed(NEEDS_CRYPTO);
    190     return;
    191   }
    192 
    193   bool sync_has_nodes = false;
    194   if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
    195     StartFailed(UNRECOVERABLE_ERROR);
    196     return;
    197   }
    198 
    199   base::TimeTicks start_time = base::TimeTicks::Now();
    200   bool merge_success = model_associator_->AssociateModels();
    201   UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime",
    202                       base::TimeTicks::Now() - start_time);
    203   if (!merge_success) {
    204     StartFailed(ASSOCIATION_FAILED);
    205     return;
    206   }
    207 
    208   sync_service_->ActivateDataType(this, change_processor_.get());
    209   StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING);
    210 }
    211 
    212 void TypedUrlDataTypeController::StartDone(
    213     DataTypeController::StartResult result,
    214     DataTypeController::State new_state) {
    215   VLOG(1) << "TypedUrl data type controller StartDone called.";
    216 
    217   abort_association_complete_.Signal();
    218   base::AutoLock lock(abort_association_lock_);
    219   if (!abort_association_) {
    220     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    221                            NewRunnableMethod(
    222                                this,
    223                                &TypedUrlDataTypeController::StartDoneImpl,
    224                                result,
    225                                new_state));
    226   }
    227 }
    228 
    229 void TypedUrlDataTypeController::StartDoneImpl(
    230     DataTypeController::StartResult result,
    231     DataTypeController::State new_state) {
    232   VLOG(1) << "TypedUrl data type controller StartDoneImpl called.";
    233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    234   set_state(new_state);
    235   start_callback_->Run(result, FROM_HERE);
    236   start_callback_.reset();
    237 
    238   if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) {
    239     UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures",
    240                               result,
    241                               MAX_START_RESULT);
    242   }
    243 }
    244 
    245 void TypedUrlDataTypeController::StopImpl() {
    246   VLOG(1) << "TypedUrl data type controller StopImpl called.";
    247 
    248   if (model_associator_ != NULL)
    249     model_associator_->DisassociateModels();
    250 
    251   change_processor_.reset();
    252   model_associator_.reset();
    253 
    254   datatype_stopped_.Signal();
    255 }
    256 
    257 void TypedUrlDataTypeController::StartFailed(StartResult result) {
    258   change_processor_.reset();
    259   model_associator_.reset();
    260   StartDone(result, NOT_RUNNING);
    261 }
    262 
    263 void TypedUrlDataTypeController::OnUnrecoverableError(
    264     const tracked_objects::Location& from_here,
    265     const std::string& message) {
    266   BrowserThread::PostTask(
    267     BrowserThread::UI, FROM_HERE,
    268     NewRunnableMethod(this,
    269                       &TypedUrlDataTypeController::OnUnrecoverableErrorImpl,
    270                       from_here, message));
    271 }
    272 
    273 void TypedUrlDataTypeController::OnUnrecoverableErrorImpl(
    274   const tracked_objects::Location& from_here,
    275   const std::string& message) {
    276   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    277   UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1);
    278   sync_service_->OnUnrecoverableError(from_here, message);
    279 }
    280 
    281 }  // namespace browser_sync
    282