autofill_data_type_controller.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 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/browser_thread.h" 12#include "chrome/browser/profile.h" 13#include "chrome/browser/sync/glue/autofill_change_processor.h" 14#include "chrome/browser/sync/glue/autofill_model_associator.h" 15#include "chrome/browser/sync/profile_sync_service.h" 16#include "chrome/browser/sync/profile_sync_factory.h" 17#include "chrome/browser/webdata/web_data_service.h" 18#include "chrome/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 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 34 DCHECK(profile_sync_factory); 35 DCHECK(profile); 36 DCHECK(sync_service); 37} 38 39AutofillDataTypeController::~AutofillDataTypeController() { 40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 41} 42 43void AutofillDataTypeController::Start(StartCallback* start_callback) { 44 VLOG(1) << "Starting autofill data controller."; 45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 46 DCHECK(start_callback); 47 if (state() != NOT_RUNNING) { 48 start_callback->Run(BUSY); 49 delete start_callback; 50 return; 51 } 52 53 start_callback_.reset(start_callback); 54 abort_association_ = false; 55 56 // Waiting for the personal data is subtle: we do this as the PDM resets 57 // its cache of unique IDs once it gets loaded. If we were to proceed with 58 // association, the local ids in the mappings would wind up colliding. 59 personal_data_ = profile_->GetPersonalDataManager(); 60 if (!personal_data_->IsDataLoaded()) { 61 set_state(MODEL_STARTING); 62 personal_data_->SetObserver(this); 63 return; 64 } 65 66 ContinueStartAfterPersonalDataLoaded(); 67} 68 69void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() { 70 web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); 71 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { 72 set_state(ASSOCIATING); 73 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 74 NewRunnableMethod( 75 this, 76 &AutofillDataTypeController::StartImpl)); 77 } else { 78 set_state(MODEL_STARTING); 79 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, 80 NotificationService::AllSources()); 81 } 82} 83 84void AutofillDataTypeController::OnPersonalDataLoaded() { 85 DCHECK_EQ(state_, MODEL_STARTING); 86 personal_data_->RemoveObserver(this); 87 ContinueStartAfterPersonalDataLoaded(); 88} 89 90void AutofillDataTypeController::Observe(NotificationType type, 91 const NotificationSource& source, 92 const NotificationDetails& details) { 93 VLOG(1) << "Web database loaded observed."; 94 notification_registrar_.RemoveAll(); 95 set_state(ASSOCIATING); 96 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 97 NewRunnableMethod( 98 this, 99 &AutofillDataTypeController::StartImpl)); 100} 101 102void AutofillDataTypeController::Stop() { 103 VLOG(1) << "Stopping autofill data type controller."; 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 105 106 // If Stop() is called while Start() is waiting for association to 107 // complete, we need to abort the association and wait for the DB 108 // thread to finish the StartImpl() task. 109 if (state_ == ASSOCIATING) { 110 { 111 AutoLock lock(abort_association_lock_); 112 abort_association_ = true; 113 if (model_associator_.get()) 114 model_associator_->AbortAssociation(); 115 } 116 // Wait for the model association to abort. 117 abort_association_complete_.Wait(); 118 StartDoneImpl(ABORTED, STOPPING); 119 } 120 121 // If Stop() is called while Start() is waiting for the personal 122 // data manager or web data service to load, abort the start. 123 if (state_ == MODEL_STARTING) 124 StartDoneImpl(ABORTED, STOPPING); 125 126 // Note that we are doing most of the stop work here (deactivate and 127 // disassociate) on the UI thread even though the associate & 128 // activate were done on the DB thread. This is because Stop() must 129 // be synchronous. 130 notification_registrar_.RemoveAll(); 131 personal_data_->RemoveObserver(this); 132 if (change_processor_ != NULL && change_processor_->IsRunning()) 133 sync_service_->DeactivateDataType(this, change_processor_.get()); 134 135 if (model_associator_ != NULL) 136 model_associator_->DisassociateModels(); 137 138 set_state(NOT_RUNNING); 139 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 140 NewRunnableMethod( 141 this, 142 &AutofillDataTypeController::StopImpl)); 143} 144 145void AutofillDataTypeController::StartImpl() { 146 VLOG(1) << "Autofill data type controller StartImpl called."; 147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 148 // No additional services need to be started before we can proceed 149 // with model association. 150 { 151 AutoLock lock(abort_association_lock_); 152 if (abort_association_) { 153 abort_association_complete_.Signal(); 154 return; 155 } 156 ProfileSyncFactory::SyncComponents sync_components = 157 profile_sync_factory_->CreateAutofillSyncComponents( 158 sync_service_, 159 web_data_service_->GetDatabase(), 160 profile_->GetPersonalDataManager(), 161 this); 162 model_associator_.reset(sync_components.model_associator); 163 change_processor_.reset(sync_components.change_processor); 164 } 165 166 bool sync_has_nodes = false; 167 if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { 168 StartFailed(UNRECOVERABLE_ERROR); 169 return; 170 } 171 172 base::TimeTicks start_time = base::TimeTicks::Now(); 173 bool merge_success = model_associator_->AssociateModels(); 174 UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", 175 base::TimeTicks::Now() - start_time); 176 if (!merge_success) { 177 StartFailed(ASSOCIATION_FAILED); 178 return; 179 } 180 181 sync_service_->ActivateDataType(this, change_processor_.get()); 182 StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); 183} 184 185void AutofillDataTypeController::StartDone( 186 DataTypeController::StartResult result, 187 DataTypeController::State new_state) { 188 VLOG(1) << "Autofill data type controller StartDone called."; 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 190 191 abort_association_complete_.Signal(); 192 AutoLock lock(abort_association_lock_); 193 if (!abort_association_) { 194 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 195 NewRunnableMethod( 196 this, 197 &AutofillDataTypeController::StartDoneImpl, 198 result, 199 new_state)); 200 } 201} 202 203void AutofillDataTypeController::StartDoneImpl( 204 DataTypeController::StartResult result, 205 DataTypeController::State new_state) { 206 VLOG(1) << "Autofill data type controller StartDoneImpl called."; 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 208 209 set_state(new_state); 210 start_callback_->Run(result); 211 start_callback_.reset(); 212 213 if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) { 214 UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", 215 result, 216 MAX_START_RESULT); 217 } 218} 219 220void AutofillDataTypeController::StopImpl() { 221 VLOG(1) << "Autofill data type controller StopImpl called."; 222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 223 224 change_processor_.reset(); 225 model_associator_.reset(); 226} 227 228void AutofillDataTypeController::StartFailed(StartResult result) { 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 230 change_processor_.reset(); 231 model_associator_.reset(); 232 StartDone(result, NOT_RUNNING); 233} 234 235void AutofillDataTypeController::OnUnrecoverableError( 236 const tracked_objects::Location& from_here, 237 const std::string& message) { 238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 239 BrowserThread::PostTask( 240 BrowserThread::UI, FROM_HERE, 241 NewRunnableMethod(this, 242 &AutofillDataTypeController::OnUnrecoverableErrorImpl, 243 from_here, message)); 244 UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); 245} 246 247void AutofillDataTypeController::OnUnrecoverableErrorImpl( 248 const tracked_objects::Location& from_here, 249 const std::string& message) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 251 sync_service_->OnUnrecoverableError(from_here, message); 252} 253 254} // namespace browser_sync 255