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/password_data_type_controller.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "base/logging.h"
      9 #include "base/task.h"
     10 #include "base/time.h"
     11 #include "chrome/browser/password_manager/password_store.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/sync/glue/password_change_processor.h"
     14 #include "chrome/browser/sync/glue/password_model_associator.h"
     15 #include "chrome/browser/sync/profile_sync_service.h"
     16 #include "chrome/browser/sync/profile_sync_factory.h"
     17 #include "content/browser/browser_thread.h"
     18 
     19 namespace browser_sync {
     20 
     21 PasswordDataTypeController::PasswordDataTypeController(
     22     ProfileSyncFactory* profile_sync_factory,
     23     Profile* profile,
     24     ProfileSyncService* sync_service)
     25     : profile_sync_factory_(profile_sync_factory),
     26       profile_(profile),
     27       sync_service_(sync_service),
     28       state_(NOT_RUNNING),
     29       abort_association_(false),
     30       abort_association_complete_(false, false),
     31       datatype_stopped_(false, false) {
     32   DCHECK(profile_sync_factory);
     33   DCHECK(profile);
     34   DCHECK(sync_service);
     35 }
     36 
     37 PasswordDataTypeController::~PasswordDataTypeController() {
     38 }
     39 
     40 void PasswordDataTypeController::Start(StartCallback* start_callback) {
     41   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     42   DCHECK(start_callback);
     43   if (state_ != NOT_RUNNING) {
     44     start_callback->Run(BUSY, FROM_HERE);
     45     delete start_callback;
     46     return;
     47   }
     48 
     49   password_store_ = profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS);
     50   if (!password_store_.get()) {
     51     LOG(ERROR) << "PasswordStore not initialized, password datatype controller"
     52                << " aborting.";
     53     state_ = NOT_RUNNING;
     54     start_callback->Run(ABORTED, FROM_HERE);
     55     delete start_callback;
     56     return;
     57   }
     58 
     59   start_callback_.reset(start_callback);
     60   abort_association_ = false;
     61   set_state(ASSOCIATING);
     62   password_store_->ScheduleTask(
     63       NewRunnableMethod(this, &PasswordDataTypeController::StartImpl));
     64 }
     65 
     66 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
     67 // distinguishing chrome shutdown from sync shutdown, we should be able to avoid
     68 // this (http://crbug.com/55662). Further, all this functionality should be
     69 // abstracted to a higher layer, where we could ensure all datatypes are doing
     70 // the same thing (http://crbug.com/76232).
     71 void PasswordDataTypeController::Stop() {
     72   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     73 
     74   // If Stop() is called while Start() is waiting for association to
     75   // complete, we need to abort the association and wait for the PASSWORD
     76   // thread to finish the StartImpl() task.
     77   if (state_ == ASSOCIATING) {
     78     {
     79       base::AutoLock lock(abort_association_lock_);
     80       abort_association_ = true;
     81       if (model_associator_.get())
     82         model_associator_->AbortAssociation();
     83     }
     84     // Wait for the model association to abort.
     85     abort_association_complete_.Wait();
     86     StartDoneImpl(ABORTED, STOPPING);
     87   }
     88 
     89   // If Stop() is called while Start() is waiting for another service to load,
     90   // abort the start.
     91   if (state_ == MODEL_STARTING)
     92     StartDoneImpl(ABORTED, STOPPING);
     93 
     94   DCHECK(!start_callback_.get());
     95 
     96   if (change_processor_ != NULL)
     97     sync_service_->DeactivateDataType(this, change_processor_.get());
     98 
     99   set_state(NOT_RUNNING);
    100   DCHECK(password_store_.get());
    101   password_store_->ScheduleTask(
    102       NewRunnableMethod(this, &PasswordDataTypeController::StopImpl));
    103   datatype_stopped_.Wait();
    104 }
    105 
    106 bool PasswordDataTypeController::enabled() {
    107   return true;
    108 }
    109 
    110 syncable::ModelType PasswordDataTypeController::type() const {
    111   return syncable::PASSWORDS;
    112 }
    113 
    114 browser_sync::ModelSafeGroup PasswordDataTypeController::model_safe_group()
    115     const {
    116   return browser_sync::GROUP_PASSWORD;
    117 }
    118 
    119 std::string PasswordDataTypeController::name() const {
    120   // For logging only.
    121   return "password";
    122 }
    123 
    124 DataTypeController::State PasswordDataTypeController::state() const {
    125   return state_;
    126 }
    127 
    128 void PasswordDataTypeController::StartImpl() {
    129   // No additional services need to be started before we can proceed
    130   // with model association.
    131   {
    132     base::AutoLock lock(abort_association_lock_);
    133     if (abort_association_) {
    134       abort_association_complete_.Signal();
    135       return;
    136     }
    137     ProfileSyncFactory::SyncComponents sync_components =
    138         profile_sync_factory_->CreatePasswordSyncComponents(
    139             sync_service_,
    140             password_store_.get(),
    141             this);
    142     model_associator_.reset(sync_components.model_associator);
    143     change_processor_.reset(sync_components.change_processor);
    144   }
    145 
    146   if (!model_associator_->CryptoReadyIfNecessary()) {
    147     StartFailed(NEEDS_CRYPTO);
    148     return;
    149   }
    150 
    151   bool sync_has_nodes = false;
    152   if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
    153     StartFailed(UNRECOVERABLE_ERROR);
    154     return;
    155   }
    156 
    157   base::TimeTicks start_time = base::TimeTicks::Now();
    158   bool merge_success = model_associator_->AssociateModels();
    159   UMA_HISTOGRAM_TIMES("Sync.PasswordAssociationTime",
    160                       base::TimeTicks::Now() - start_time);
    161   if (!merge_success) {
    162     StartFailed(ASSOCIATION_FAILED);
    163     return;
    164   }
    165 
    166   sync_service_->ActivateDataType(this, change_processor_.get());
    167   StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING);
    168 }
    169 
    170 void PasswordDataTypeController::StartDone(
    171     DataTypeController::StartResult result,
    172     DataTypeController::State new_state) {
    173   abort_association_complete_.Signal();
    174   base::AutoLock lock(abort_association_lock_);
    175   if (!abort_association_) {
    176     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    177                             NewRunnableMethod(
    178                                 this,
    179                                 &PasswordDataTypeController::StartDoneImpl,
    180                                 result,
    181                                 new_state));
    182   }
    183 }
    184 
    185 void PasswordDataTypeController::StartDoneImpl(
    186     DataTypeController::StartResult result,
    187     DataTypeController::State new_state) {
    188   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    189   set_state(new_state);
    190   start_callback_->Run(result, FROM_HERE);
    191   start_callback_.reset();
    192 }
    193 
    194 void PasswordDataTypeController::StopImpl() {
    195   if (model_associator_ != NULL)
    196     model_associator_->DisassociateModels();
    197 
    198   change_processor_.reset();
    199   model_associator_.reset();
    200 
    201   datatype_stopped_.Signal();
    202 }
    203 
    204 void PasswordDataTypeController::StartFailed(StartResult result) {
    205   change_processor_.reset();
    206   model_associator_.reset();
    207   StartDone(result, NOT_RUNNING);
    208 }
    209 
    210 void PasswordDataTypeController::OnUnrecoverableError(
    211     const tracked_objects::Location& from_here, const std::string& message) {
    212   BrowserThread::PostTask(
    213       BrowserThread::UI, FROM_HERE,
    214       NewRunnableMethod(this,
    215                         &PasswordDataTypeController::OnUnrecoverableErrorImpl,
    216                         from_here, message));
    217 }
    218 
    219 void PasswordDataTypeController::OnUnrecoverableErrorImpl(
    220     const tracked_objects::Location& from_here,
    221     const std::string& message) {
    222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    223   sync_service_->OnUnrecoverableError(from_here, message);
    224 }
    225 
    226 }  // namespace browser_sync
    227