1 // Copyright (c) 2012 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/non_frontend_data_type_controller.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/logging.h" 10 #include "chrome/browser/profiles/profile.h" 11 #include "chrome/browser/sync/glue/change_processor.h" 12 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h" 13 #include "chrome/browser/sync/glue/model_associator.h" 14 #include "chrome/browser/sync/profile_sync_components_factory.h" 15 #include "chrome/browser/sync/profile_sync_service.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "sync/api/sync_error.h" 18 #include "sync/internal_api/public/base/model_type.h" 19 #include "sync/internal_api/public/util/weak_handle.h" 20 #include "sync/util/data_type_histogram.h" 21 22 using content::BrowserThread; 23 24 namespace browser_sync { 25 26 class NonFrontendDataTypeController::BackendComponentsContainer { 27 public: 28 explicit BackendComponentsContainer( 29 NonFrontendDataTypeController* controller); 30 ~BackendComponentsContainer(); 31 void Run(); 32 void Disconnect(); 33 34 private: 35 bool CreateComponents(); 36 void Associate(); 37 38 // For creating components. 39 NonFrontendDataTypeController* controller_; 40 base::Lock controller_lock_; 41 42 syncer::ModelType type_; 43 44 // For returning association results to controller on UI. 45 syncer::WeakHandle<NonFrontendDataTypeController> controller_handle_; 46 47 scoped_ptr<AssociatorInterface> model_associator_; 48 scoped_ptr<ChangeProcessor> change_processor_; 49 }; 50 51 NonFrontendDataTypeController:: 52 BackendComponentsContainer::BackendComponentsContainer( 53 NonFrontendDataTypeController* controller) 54 : controller_(controller), 55 type_(controller->type()) { 56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 57 controller_handle_ = 58 syncer::MakeWeakHandle(controller_->weak_ptr_factory_.GetWeakPtr()); 59 } 60 61 NonFrontendDataTypeController:: 62 BackendComponentsContainer::~BackendComponentsContainer() { 63 if (model_associator_) 64 model_associator_->DisassociateModels(); 65 } 66 67 void NonFrontendDataTypeController::BackendComponentsContainer::Run() { 68 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 69 if (CreateComponents()) 70 Associate(); 71 } 72 73 bool 74 NonFrontendDataTypeController::BackendComponentsContainer::CreateComponents() { 75 base::AutoLock al(controller_lock_); 76 if (!controller_) { 77 DVLOG(1) << "Controller was stopped before sync components are created."; 78 return false; 79 } 80 81 ProfileSyncComponentsFactory::SyncComponents sync_components = 82 controller_->CreateSyncComponents(); 83 model_associator_.reset(sync_components.model_associator); 84 change_processor_.reset(sync_components.change_processor); 85 return true; 86 } 87 88 void NonFrontendDataTypeController::BackendComponentsContainer::Associate() { 89 CHECK(model_associator_); 90 91 bool succeeded = false; 92 93 browser_sync::NonFrontendDataTypeController::AssociationResult result(type_); 94 if (!model_associator_->CryptoReadyIfNecessary()) { 95 result.needs_crypto = true; 96 } else { 97 base::TimeTicks start_time = base::TimeTicks::Now(); 98 99 if (!model_associator_->SyncModelHasUserCreatedNodes( 100 &result.sync_has_nodes)) { 101 result.unrecoverable_error = true; 102 result.error = syncer::SyncError(FROM_HERE, 103 syncer::SyncError::UNRECOVERABLE_ERROR, 104 "Failed to load sync nodes", 105 type_); 106 } else { 107 result.error = model_associator_->AssociateModels( 108 &result.local_merge_result, &result.syncer_merge_result); 109 110 // Return components to frontend when no error. 111 if (!result.error.IsSet()) { 112 succeeded = true; 113 result.change_processor = change_processor_.get(); 114 result.model_associator = model_associator_.get(); 115 } 116 } 117 result.association_time = base::TimeTicks::Now() - start_time; 118 } 119 result.local_merge_result.set_error(result.error); 120 121 // Destroy processor/associator on backend on failure. 122 if (!succeeded) { 123 base::AutoLock al(controller_lock_); 124 model_associator_->DisassociateModels(); 125 change_processor_.reset(); 126 model_associator_.reset(); 127 } 128 129 controller_handle_.Call( 130 FROM_HERE, 131 &browser_sync::NonFrontendDataTypeController::AssociationCallback, 132 result); 133 } 134 135 void NonFrontendDataTypeController::BackendComponentsContainer::Disconnect() { 136 base::AutoLock al(controller_lock_); 137 CHECK(controller_); 138 139 if (change_processor_) 140 controller_->DisconnectProcessor(change_processor_.get()); 141 if (model_associator_) 142 model_associator_->AbortAssociation(); 143 144 controller_ = NULL; 145 } 146 147 NonFrontendDataTypeController::AssociationResult::AssociationResult( 148 syncer::ModelType type) 149 : needs_crypto(false), 150 unrecoverable_error(false), 151 sync_has_nodes(false), 152 local_merge_result(type), 153 syncer_merge_result(type), 154 change_processor(NULL), 155 model_associator(NULL) {} 156 157 NonFrontendDataTypeController::AssociationResult::~AssociationResult() {} 158 159 NonFrontendDataTypeController::NonFrontendDataTypeController( 160 ProfileSyncComponentsFactory* profile_sync_factory, 161 Profile* profile, 162 ProfileSyncService* sync_service) 163 : state_(NOT_RUNNING), 164 profile_sync_factory_(profile_sync_factory), 165 profile_(profile), 166 profile_sync_service_(sync_service), 167 model_associator_(NULL), 168 change_processor_(NULL), 169 weak_ptr_factory_(this) { 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 171 DCHECK(profile_sync_factory_); 172 DCHECK(profile_); 173 DCHECK(profile_sync_service_); 174 } 175 176 void NonFrontendDataTypeController::LoadModels( 177 const ModelLoadCallback& model_load_callback) { 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 179 DCHECK(!model_load_callback.is_null()); 180 if (state_ != NOT_RUNNING) { 181 model_load_callback.Run(type(), 182 syncer::SyncError(FROM_HERE, 183 syncer::SyncError::DATATYPE_ERROR, 184 "Model already loaded", 185 type())); 186 return; 187 } 188 189 state_ = MODEL_STARTING; 190 if (!StartModels()) { 191 // We failed to start the models. There is no point in waiting. 192 // Note: This code is deprecated. The only 2 datatypes here, 193 // passwords and typed urls, dont have any special loading. So if we 194 // get a false it means they failed. 195 DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING 196 || state_ == DISABLED); 197 model_load_callback.Run(type(), 198 syncer::SyncError(FROM_HERE, 199 syncer::SyncError::DATATYPE_ERROR, 200 "Failed loading", 201 type())); 202 return; 203 } 204 state_ = MODEL_LOADED; 205 206 model_load_callback.Run(type(), syncer::SyncError()); 207 } 208 209 void NonFrontendDataTypeController::OnModelLoaded() { 210 NOTREACHED(); 211 } 212 213 void NonFrontendDataTypeController::StartAssociating( 214 const StartCallback& start_callback) { 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 216 DCHECK(!start_callback.is_null()); 217 DCHECK(!components_container_); 218 DCHECK_EQ(state_, MODEL_LOADED); 219 220 // Kick off association on the thread the datatype resides on. 221 state_ = ASSOCIATING; 222 start_callback_ = start_callback; 223 224 components_container_.reset(new BackendComponentsContainer(this)); 225 226 if (!PostTaskOnBackendThread( 227 FROM_HERE, 228 base::Bind(&BackendComponentsContainer::Run, 229 base::Unretained(components_container_.get())))) { 230 syncer::SyncError error( 231 FROM_HERE, 232 syncer::SyncError::DATATYPE_ERROR, 233 "Failed to post StartAssociation", type()); 234 syncer::SyncMergeResult local_merge_result(type()); 235 local_merge_result.set_error(error); 236 StartDone(ASSOCIATION_FAILED, 237 local_merge_result, 238 syncer::SyncMergeResult(type())); 239 } 240 } 241 242 void DestroyComponentsInBackend( 243 NonFrontendDataTypeController::BackendComponentsContainer *containter) { 244 delete containter; 245 } 246 247 void NonFrontendDataTypeController::Stop() { 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 249 DCHECK_NE(state_, NOT_RUNNING); 250 251 // Deactivate the date type on the UI thread first to stop processing 252 // sync server changes. This needs to happen before posting task to destroy 253 // processor and associator on backend. Otherwise it could crash if syncer 254 // post work to backend after destruction task and that work is run before 255 // deactivation. 256 profile_sync_service()->DeactivateDataType(type()); 257 258 // Ignore association callback. 259 weak_ptr_factory_.InvalidateWeakPtrs(); 260 261 // Disconnect on UI and post task to destroy on backend. 262 if (components_container_) { 263 components_container_->Disconnect(); 264 PostTaskOnBackendThread( 265 FROM_HERE, 266 base::Bind(&DestroyComponentsInBackend, 267 components_container_.release())); 268 model_associator_ = NULL; 269 change_processor_ = NULL; 270 } 271 272 // Call start callback if waiting for association. 273 if (state_ == ASSOCIATING) { 274 StartDone(ABORTED, 275 syncer::SyncMergeResult(type()), 276 syncer::SyncMergeResult(type())); 277 278 } 279 280 state_ = NOT_RUNNING; 281 } 282 283 std::string NonFrontendDataTypeController::name() const { 284 // For logging only. 285 return syncer::ModelTypeToString(type()); 286 } 287 288 DataTypeController::State NonFrontendDataTypeController::state() const { 289 return state_; 290 } 291 292 void NonFrontendDataTypeController::OnSingleDatatypeUnrecoverableError( 293 const tracked_objects::Location& from_here, 294 const std::string& message) { 295 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 296 RecordUnrecoverableError(from_here, message); 297 BrowserThread::PostTask(BrowserThread::UI, from_here, 298 base::Bind(&NonFrontendDataTypeController::DisableImpl, 299 this, 300 from_here, 301 message)); 302 } 303 304 NonFrontendDataTypeController::NonFrontendDataTypeController() 305 : state_(NOT_RUNNING), 306 profile_sync_factory_(NULL), 307 profile_(NULL), 308 profile_sync_service_(NULL), 309 model_associator_(NULL), 310 change_processor_(NULL), 311 weak_ptr_factory_(this) { 312 } 313 314 NonFrontendDataTypeController::~NonFrontendDataTypeController() { 315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 316 DCHECK(!change_processor_); 317 DCHECK(!model_associator_); 318 } 319 320 bool NonFrontendDataTypeController::StartModels() { 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 322 DCHECK_EQ(state_, MODEL_STARTING); 323 // By default, no additional services need to be started before we can proceed 324 // with model association, so do nothing. 325 return true; 326 } 327 328 void NonFrontendDataTypeController::StartDone( 329 DataTypeController::StartResult start_result, 330 const syncer::SyncMergeResult& local_merge_result, 331 const syncer::SyncMergeResult& syncer_merge_result) { 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 333 DataTypeController::State new_state; 334 335 if (IsSuccessfulResult(start_result)) { 336 new_state = RUNNING; 337 } else { 338 new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING); 339 if (IsUnrecoverableResult(start_result)) 340 RecordUnrecoverableError(FROM_HERE, "StartFailed"); 341 } 342 343 StartDoneImpl(start_result, new_state, local_merge_result, 344 syncer_merge_result); 345 } 346 347 void NonFrontendDataTypeController::StartDoneImpl( 348 DataTypeController::StartResult start_result, 349 DataTypeController::State new_state, 350 const syncer::SyncMergeResult& local_merge_result, 351 const syncer::SyncMergeResult& syncer_merge_result) { 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 353 354 state_ = new_state; 355 if (state_ != RUNNING) { 356 // Start failed. 357 RecordStartFailure(start_result); 358 } 359 360 DCHECK(!start_callback_.is_null()); 361 // We have to release the callback before we call it, since it's possible 362 // invoking the callback will trigger a call to STOP(), which will get 363 // confused by the non-NULL start_callback_. 364 StartCallback callback = start_callback_; 365 start_callback_.Reset(); 366 callback.Run(start_result, local_merge_result, syncer_merge_result); 367 } 368 369 void NonFrontendDataTypeController::DisableImpl( 370 const tracked_objects::Location& from_here, 371 const std::string& message) { 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 373 profile_sync_service_->DisableBrokenDatatype(type(), from_here, message); 374 } 375 376 void NonFrontendDataTypeController::RecordAssociationTime( 377 base::TimeDelta time) { 378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 379 #define PER_DATA_TYPE_MACRO(type_str) \ 380 UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time); 381 SYNC_DATA_TYPE_HISTOGRAM(type()); 382 #undef PER_DATA_TYPE_MACRO 383 } 384 385 void NonFrontendDataTypeController::RecordStartFailure(StartResult result) { 386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 387 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", 388 ModelTypeToHistogramInt(type()), 389 syncer::MODEL_TYPE_COUNT); 390 #define PER_DATA_TYPE_MACRO(type_str) \ 391 UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \ 392 MAX_START_RESULT); 393 SYNC_DATA_TYPE_HISTOGRAM(type()); 394 #undef PER_DATA_TYPE_MACRO 395 } 396 397 ProfileSyncComponentsFactory* 398 NonFrontendDataTypeController::profile_sync_factory() const { 399 return profile_sync_factory_; 400 } 401 402 Profile* NonFrontendDataTypeController::profile() const { 403 return profile_; 404 } 405 406 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service() 407 const { 408 return profile_sync_service_; 409 } 410 411 void NonFrontendDataTypeController::set_start_callback( 412 const StartCallback& callback) { 413 start_callback_ = callback; 414 } 415 416 void NonFrontendDataTypeController::set_state(State state) { 417 state_ = state; 418 } 419 420 AssociatorInterface* NonFrontendDataTypeController::associator() const { 421 return model_associator_; 422 } 423 424 ChangeProcessor* NonFrontendDataTypeController::change_processor() const { 425 return change_processor_; 426 } 427 428 void NonFrontendDataTypeController::AssociationCallback( 429 AssociationResult result) { 430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 431 432 if (result.needs_crypto) { 433 StartDone(NEEDS_CRYPTO, 434 result.local_merge_result, 435 result.syncer_merge_result); 436 return; 437 } 438 439 if (result.unrecoverable_error) { 440 StartDone(UNRECOVERABLE_ERROR, 441 result.local_merge_result, 442 result.syncer_merge_result); 443 return; 444 } 445 446 RecordAssociationTime(result.association_time); 447 if (result.error.IsSet()) { 448 StartDone(ASSOCIATION_FAILED, 449 result.local_merge_result, 450 result.syncer_merge_result); 451 return; 452 } 453 454 CHECK(result.change_processor); 455 CHECK(result.model_associator); 456 change_processor_ = result.change_processor; 457 model_associator_ = result.model_associator; 458 459 profile_sync_service_->ActivateDataType(type(), model_safe_group(), 460 change_processor()); 461 StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK, 462 result.local_merge_result, 463 result.syncer_merge_result); 464 } 465 466 } // namespace browser_sync 467