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