| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/glue/autofill_data_type_controller.h" | 5 #include "chrome/browser/sync/glue/autofill_data_type_controller.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | |
| 8 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 9 #include "base/task.h" | 8 #include "base/task.h" |
| 10 #include "base/time.h" | |
| 11 #include "chrome/browser/profiles/profile.h" | 9 #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" | 10 #include "chrome/browser/sync/profile_sync_factory.h" |
| 15 #include "chrome/browser/sync/profile_sync_service.h" | 11 #include "chrome/browser/sync/profile_sync_service.h" |
| 16 #include "chrome/browser/webdata/web_data_service.h" | 12 #include "chrome/browser/webdata/web_data_service.h" |
| 17 #include "content/browser/browser_thread.h" | 13 #include "content/browser/browser_thread.h" |
| 18 #include "content/common/notification_service.h" | 14 #include "content/common/notification_service.h" |
| 15 #include "content/common/notification_source.h" |
| 16 #include "content/common/notification_type.h" |
| 19 | 17 |
| 20 namespace browser_sync { | 18 namespace browser_sync { |
| 21 | 19 |
| 22 AutofillDataTypeController::AutofillDataTypeController( | 20 AutofillDataTypeController::AutofillDataTypeController( |
| 23 ProfileSyncFactory* profile_sync_factory, | 21 ProfileSyncFactory* profile_sync_factory, |
| 24 Profile* profile, | 22 Profile* profile, |
| 25 ProfileSyncService* sync_service) | 23 ProfileSyncService* sync_service) |
| 26 : profile_sync_factory_(profile_sync_factory), | 24 : NonFrontendDataTypeController(profile_sync_factory, |
| 27 profile_(profile), | 25 profile, |
| 28 sync_service_(sync_service), | 26 sync_service), |
| 29 state_(NOT_RUNNING), | 27 personal_data_(NULL) { |
| 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 } | 28 } |
| 39 | 29 |
| 40 AutofillDataTypeController::~AutofillDataTypeController() { | 30 AutofillDataTypeController::~AutofillDataTypeController() { |
| 41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 31 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 } | 32 } |
| 49 | 33 |
| 50 void AutofillDataTypeController::Start(StartCallback* start_callback) { | 34 bool AutofillDataTypeController::StartModels() { |
| 51 VLOG(1) << "Starting autofill data controller."; | |
| 52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 53 DCHECK(start_callback); | 36 DCHECK_EQ(state_, MODEL_STARTING); |
| 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 | 37 // 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 | 38 // 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. | 39 // association, the local ids in the mappings would wind up colliding. |
| 66 personal_data_ = profile_->GetPersonalDataManager(); | 40 personal_data_ = profile_->GetPersonalDataManager(); |
| 67 if (!personal_data_->IsDataLoaded()) { | 41 if (!personal_data_->IsDataLoaded()) { |
| 68 set_state(MODEL_STARTING); | |
| 69 personal_data_->SetObserver(this); | 42 personal_data_->SetObserver(this); |
| 70 return; | 43 return false; |
| 71 } | 44 } |
| 72 | 45 |
| 73 ContinueStartAfterPersonalDataLoaded(); | 46 web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); |
| 47 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { |
| 48 return true; |
| 49 } else { |
| 50 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, |
| 51 NotificationService::AllSources()); |
| 52 return false; |
| 53 } |
| 74 } | 54 } |
| 75 | 55 |
| 76 void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() { | 56 void AutofillDataTypeController::OnPersonalDataLoaded() { |
| 57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 58 DCHECK_EQ(state_, MODEL_STARTING); |
| 59 personal_data_->RemoveObserver(this); |
| 77 web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); | 60 web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); |
| 78 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { | 61 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { |
| 79 set_state(ASSOCIATING); | 62 state_ = ASSOCIATING; |
| 80 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, | 63 if (!KickOffAssociation()) { |
| 81 NewRunnableMethod( | 64 StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); |
| 82 this, | 65 } |
| 83 &AutofillDataTypeController::StartImpl)); | |
| 84 } else { | 66 } else { |
| 85 set_state(MODEL_STARTING); | |
| 86 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, | 67 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, |
| 87 NotificationService::AllSources()); | 68 NotificationService::AllSources()); |
| 88 } | 69 } |
| 89 } | 70 } |
| 90 | 71 |
| 91 void AutofillDataTypeController::OnPersonalDataLoaded() { | |
| 92 DCHECK_EQ(state_, MODEL_STARTING); | |
| 93 personal_data_->RemoveObserver(this); | |
| 94 ContinueStartAfterPersonalDataLoaded(); | |
| 95 } | |
| 96 | |
| 97 void AutofillDataTypeController::Observe(NotificationType type, | 72 void AutofillDataTypeController::Observe(NotificationType type, |
| 98 const NotificationSource& source, | 73 const NotificationSource& source, |
| 99 const NotificationDetails& details) { | 74 const NotificationDetails& details) { |
| 100 VLOG(1) << "Web database loaded observed."; | 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 76 DCHECK_EQ(state_, MODEL_STARTING); |
| 101 notification_registrar_.RemoveAll(); | 77 notification_registrar_.RemoveAll(); |
| 102 set_state(ASSOCIATING); | 78 state_ = ASSOCIATING; |
| 103 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, | 79 if (!KickOffAssociation()) { |
| 80 StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); |
| 81 } |
| 82 } |
| 83 |
| 84 bool AutofillDataTypeController::KickOffAssociation() { |
| 85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 86 DCHECK_EQ(state_, ASSOCIATING); |
| 87 return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 104 NewRunnableMethod( | 88 NewRunnableMethod( |
| 105 this, | 89 this, |
| 106 &AutofillDataTypeController::StartImpl)); | 90 &AutofillDataTypeController::Associate)); |
| 107 } | 91 } |
| 108 | 92 |
| 109 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of | 93 void AutofillDataTypeController::CreateSyncComponents() { |
| 110 // distinguishing chrome shutdown from sync shutdown, we should be able to avoid | 94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 111 // this (http://crbug.com/55662). Further, all this functionality should be | 95 DCHECK_EQ(state_, ASSOCIATING); |
| 112 // abstracted to a higher layer, where we could ensure all datatypes are doing | 96 ProfileSyncFactory::SyncComponents sync_components = profile_sync_factory_-> |
| 113 // the same thing (http://crbug.com/76232). | 97 CreateAutofillSyncComponents( |
| 114 void AutofillDataTypeController::Stop() { | 98 sync_service_, |
| 115 VLOG(1) << "Stopping autofill data type controller."; | 99 web_data_service_->GetDatabase(), |
| 100 personal_data_, |
| 101 this); |
| 102 model_associator_.reset(sync_components.model_associator); |
| 103 change_processor_.reset(sync_components.change_processor); |
| 104 } |
| 105 |
| 106 void AutofillDataTypeController::CleanUpState() { |
| 116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 117 | 108 DCHECK(state_ == STOPPING || state_ == NOT_RUNNING); |
| 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(); | 109 notification_registrar_.RemoveAll(); |
| 143 personal_data_->RemoveObserver(this); | 110 personal_data_->RemoveObserver(this); |
| 144 if (change_processor_ != NULL && change_processor_->IsRunning()) | 111 } |
| 145 sync_service_->DeactivateDataType(this, change_processor_.get()); | |
| 146 | 112 |
| 147 set_state(NOT_RUNNING); | 113 bool AutofillDataTypeController::KickOffDestroy() { |
| 148 if (BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, | 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 115 DCHECK_EQ(state_, STOPPING); |
| 116 return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 149 NewRunnableMethod( | 117 NewRunnableMethod( |
| 150 this, | 118 this, |
| 151 &AutofillDataTypeController::StopImpl))) { | 119 &AutofillDataTypeController::Destroy)); |
| 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 | |
| 164 bool AutofillDataTypeController::enabled() { | |
| 165 return true; | |
| 166 } | 120 } |
| 167 | 121 |
| 168 syncable::ModelType AutofillDataTypeController::type() const { | 122 syncable::ModelType AutofillDataTypeController::type() const { |
| 169 return syncable::AUTOFILL; | 123 return syncable::AUTOFILL; |
| 170 } | 124 } |
| 171 | 125 |
| 172 browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() | 126 browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() |
| 173 const { | 127 const { |
| 174 return browser_sync::GROUP_DB; | 128 return browser_sync::GROUP_DB; |
| 175 } | 129 } |
| 176 | 130 |
| 177 std::string AutofillDataTypeController::name() const { | 131 void AutofillDataTypeController::RecordUnrecoverableError( |
| 178 // For logging only. | |
| 179 return "autofill"; | |
| 180 } | |
| 181 | |
| 182 DataTypeController::State AutofillDataTypeController::state() const { | |
| 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 184 return state_; | |
| 185 } | |
| 186 | |
| 187 ProfileSyncFactory::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 | |
| 200 void 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 | |
| 247 void 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 | |
| 266 void 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 | |
| 284 void 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 | |
| 297 void 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 | |
| 304 void AutofillDataTypeController::OnUnrecoverableError( | |
| 305 const tracked_objects::Location& from_here, | 132 const tracked_objects::Location& from_here, |
| 306 const std::string& message) { | 133 const std::string& message) { |
| 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 134 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); | 135 UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); |
| 314 } | 136 } |
| 315 | 137 |
| 316 void AutofillDataTypeController::OnUnrecoverableErrorImpl( | 138 void AutofillDataTypeController::RecordAssociationTime(base::TimeDelta time) { |
| 317 const tracked_objects::Location& from_here, | 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 318 const std::string& message) { | 140 UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", time); |
| 141 } |
| 142 |
| 143 void AutofillDataTypeController::RecordStartFailure(StartResult result) { |
| 319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 320 sync_service_->OnUnrecoverableError(from_here, message); | 145 UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", |
| 146 result, |
| 147 MAX_START_RESULT); |
| 321 } | 148 } |
| 322 | 149 |
| 323 } // namespace browser_sync | 150 } // namespace browser_sync |
| OLD | NEW |