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/autofill_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/profiles/profile.h" 12#include "chrome/browser/sync/glue/autofill_change_processor.h" 13#include "chrome/browser/sync/glue/autofill_model_associator.h" 14#include "chrome/browser/sync/profile_sync_factory.h" 15#include "chrome/browser/sync/profile_sync_service.h" 16#include "chrome/browser/webdata/web_data_service.h" 17#include "content/browser/browser_thread.h" 18#include "content/common/notification_service.h" 19 20namespace browser_sync { 21 22AutofillDataTypeController::AutofillDataTypeController( 23 ProfileSyncFactory* profile_sync_factory, 24 Profile* profile, 25 ProfileSyncService* sync_service) 26 : profile_sync_factory_(profile_sync_factory), 27 profile_(profile), 28 sync_service_(sync_service), 29 state_(NOT_RUNNING), 30 personal_data_(NULL), 31 abort_association_(false), 32 abort_association_complete_(false, false), 33 datatype_stopped_(false, false) { 34 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 35 DCHECK(profile_sync_factory); 36 DCHECK(profile); 37 DCHECK(sync_service); 38} 39 40AutofillDataTypeController::~AutofillDataTypeController() { 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 42 43 // TODO(zea): remove once crbug.com/61804 is resolved. 44 CHECK_EQ(state_, NOT_RUNNING) << "AutofillDataTypeController destroyed " 45 << "without being stopped."; 46 CHECK(!change_processor_.get()) << "AutofillDataTypeController destroyed " 47 << "while holding a change processor."; 48} 49 50void AutofillDataTypeController::Start(StartCallback* start_callback) { 51 VLOG(1) << "Starting autofill data controller."; 52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 53 DCHECK(start_callback); 54 if (state() != NOT_RUNNING) { 55 start_callback->Run(BUSY, FROM_HERE); 56 delete start_callback; 57 return; 58 } 59 60 start_callback_.reset(start_callback); 61 abort_association_ = false; 62 63 // Waiting for the personal data is subtle: we do this as the PDM resets 64 // its cache of unique IDs once it gets loaded. If we were to proceed with 65 // association, the local ids in the mappings would wind up colliding. 66 personal_data_ = profile_->GetPersonalDataManager(); 67 if (!personal_data_->IsDataLoaded()) { 68 set_state(MODEL_STARTING); 69 personal_data_->SetObserver(this); 70 return; 71 } 72 73 ContinueStartAfterPersonalDataLoaded(); 74} 75 76void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() { 77 web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); 78 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { 79 set_state(ASSOCIATING); 80 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 81 NewRunnableMethod( 82 this, 83 &AutofillDataTypeController::StartImpl)); 84 } else { 85 set_state(MODEL_STARTING); 86 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, 87 NotificationService::AllSources()); 88 } 89} 90 91void AutofillDataTypeController::OnPersonalDataLoaded() { 92 DCHECK_EQ(state_, MODEL_STARTING); 93 personal_data_->RemoveObserver(this); 94 ContinueStartAfterPersonalDataLoaded(); 95} 96 97void AutofillDataTypeController::Observe(NotificationType type, 98 const NotificationSource& source, 99 const NotificationDetails& details) { 100 VLOG(1) << "Web database loaded observed."; 101 notification_registrar_.RemoveAll(); 102 set_state(ASSOCIATING); 103 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 104 NewRunnableMethod( 105 this, 106 &AutofillDataTypeController::StartImpl)); 107} 108 109// TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of 110// distinguishing chrome shutdown from sync shutdown, we should be able to avoid 111// this (http://crbug.com/55662). Further, all this functionality should be 112// abstracted to a higher layer, where we could ensure all datatypes are doing 113// the same thing (http://crbug.com/76232). 114void AutofillDataTypeController::Stop() { 115 VLOG(1) << "Stopping autofill data type controller."; 116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 117 118 // If Stop() is called while Start() is waiting for association to 119 // complete, we need to abort the association and wait for the DB 120 // thread to finish the StartImpl() task. 121 if (state_ == ASSOCIATING) { 122 { 123 base::AutoLock lock(abort_association_lock_); 124 abort_association_ = true; 125 if (model_associator_.get()) 126 model_associator_->AbortAssociation(); 127 } 128 // Wait for the model association to abort. 129 abort_association_complete_.Wait(); 130 StartDoneImpl(ABORTED, STOPPING, FROM_HERE); 131 } 132 133 // If Stop() is called while Start() is waiting for the personal 134 // data manager or web data service to load, abort the start. 135 if (state_ == MODEL_STARTING) 136 StartDoneImpl(ABORTED, STOPPING, FROM_HERE); 137 138 DCHECK(!start_callback_.get()); 139 140 // Deactivate the change processor on the UI thread. We dont want to listen 141 // for any more changes or process them from server. 142 notification_registrar_.RemoveAll(); 143 personal_data_->RemoveObserver(this); 144 if (change_processor_ != NULL && change_processor_->IsRunning()) 145 sync_service_->DeactivateDataType(this, change_processor_.get()); 146 147 set_state(NOT_RUNNING); 148 if (BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 149 NewRunnableMethod( 150 this, 151 &AutofillDataTypeController::StopImpl))) { 152 // We need to ensure the data type has fully stoppped before continuing. In 153 // particular, during shutdown we may attempt to destroy the 154 // profile_sync_service before we've removed its observers (BUG 61804). 155 datatype_stopped_.Wait(); 156 } else if (change_processor_.get()) { 157 // TODO(zea): remove once crbug.com/61804 is resolved. 158 LOG(FATAL) << "AutofillDataTypeController::Stop() called after DB thread" 159 << " killed."; 160 } 161 CHECK(!change_processor_.get()) << "AutofillChangeProcessor not released."; 162} 163 164bool AutofillDataTypeController::enabled() { 165 return true; 166} 167 168syncable::ModelType AutofillDataTypeController::type() const { 169 return syncable::AUTOFILL; 170} 171 172browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() 173 const { 174 return browser_sync::GROUP_DB; 175} 176 177std::string AutofillDataTypeController::name() const { 178 // For logging only. 179 return "autofill"; 180} 181 182DataTypeController::State AutofillDataTypeController::state() const { 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 184 return state_; 185} 186 187ProfileSyncFactory::SyncComponents 188 AutofillDataTypeController::CreateSyncComponents( 189 ProfileSyncService* profile_sync_service, 190 WebDatabase* web_database, 191 PersonalDataManager* personal_data, 192 browser_sync::UnrecoverableErrorHandler* error_handler) { 193 return profile_sync_factory_->CreateAutofillSyncComponents( 194 profile_sync_service, 195 web_database, 196 personal_data, 197 this); 198} 199 200void AutofillDataTypeController::StartImpl() { 201 VLOG(1) << "Autofill data type controller StartImpl called."; 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 203 // No additional services need to be started before we can proceed 204 // with model association. 205 { 206 base::AutoLock lock(abort_association_lock_); 207 if (abort_association_) { 208 abort_association_complete_.Signal(); 209 return; 210 } 211 ProfileSyncFactory::SyncComponents sync_components = 212 CreateSyncComponents( 213 sync_service_, 214 web_data_service_->GetDatabase(), 215 profile_->GetPersonalDataManager(), 216 this); 217 model_associator_.reset(sync_components.model_associator); 218 change_processor_.reset(sync_components.change_processor); 219 } 220 221 if (!model_associator_->CryptoReadyIfNecessary()) { 222 StartFailed(NEEDS_CRYPTO); 223 return; 224 } 225 226 bool sync_has_nodes = false; 227 if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { 228 StartFailed(UNRECOVERABLE_ERROR); 229 return; 230 } 231 232 base::TimeTicks start_time = base::TimeTicks::Now(); 233 bool merge_success = model_associator_->AssociateModels(); 234 UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", 235 base::TimeTicks::Now() - start_time); 236 VLOG(1) << "Autofill association time: " << 237 (base::TimeTicks::Now() - start_time).InSeconds(); 238 if (!merge_success) { 239 StartFailed(ASSOCIATION_FAILED); 240 return; 241 } 242 243 sync_service_->ActivateDataType(this, change_processor_.get()); 244 StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); 245} 246 247void AutofillDataTypeController::StartDone( 248 DataTypeController::StartResult result, 249 DataTypeController::State new_state) { 250 VLOG(1) << "Autofill data type controller StartDone called."; 251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 252 253 abort_association_complete_.Signal(); 254 base::AutoLock lock(abort_association_lock_); 255 if (!abort_association_) { 256 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 257 NewRunnableMethod( 258 this, 259 &AutofillDataTypeController::StartDoneImpl, 260 result, 261 new_state, 262 FROM_HERE)); 263 } 264} 265 266void AutofillDataTypeController::StartDoneImpl( 267 DataTypeController::StartResult result, 268 DataTypeController::State new_state, 269 const tracked_objects::Location& location) { 270 VLOG(1) << "Autofill data type controller StartDoneImpl called."; 271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 272 273 set_state(new_state); 274 start_callback_->Run(result, location); 275 start_callback_.reset(); 276 277 if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) { 278 UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", 279 result, 280 MAX_START_RESULT); 281 } 282} 283 284void AutofillDataTypeController::StopImpl() { 285 VLOG(1) << "Autofill data type controller StopImpl called."; 286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 287 288 if (model_associator_ != NULL) 289 model_associator_->DisassociateModels(); 290 291 change_processor_.reset(); 292 model_associator_.reset(); 293 294 datatype_stopped_.Signal(); 295} 296 297void AutofillDataTypeController::StartFailed(StartResult result) { 298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 299 change_processor_.reset(); 300 model_associator_.reset(); 301 StartDone(result, NOT_RUNNING); 302} 303 304void AutofillDataTypeController::OnUnrecoverableError( 305 const tracked_objects::Location& from_here, 306 const std::string& message) { 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 308 BrowserThread::PostTask( 309 BrowserThread::UI, FROM_HERE, 310 NewRunnableMethod(this, 311 &AutofillDataTypeController::OnUnrecoverableErrorImpl, 312 from_here, message)); 313 UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); 314} 315 316void AutofillDataTypeController::OnUnrecoverableErrorImpl( 317 const tracked_objects::Location& from_here, 318 const std::string& message) { 319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 320 sync_service_->OnUnrecoverableError(from_here, message); 321} 322 323} // namespace browser_sync 324