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/chrome_report_unrecoverable_error.h" 12 #include "chrome/browser/sync/profile_sync_components_factory.h" 13 #include "chrome/browser/sync/profile_sync_service.h" 14 #include "components/sync_driver/change_processor.h" 15 #include "components/sync_driver/model_associator.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(controller_->IsOnBackendThread()); 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 // TODO(tim): Legacy controllers are being left behind in componentization 160 // effort for now, hence passing null DisableTypeCallback and still having 161 // a dependency on ProfileSyncService. That dep can probably be removed 162 // without too much work. 163 NonFrontendDataTypeController::NonFrontendDataTypeController( 164 scoped_refptr<base::MessageLoopProxy> ui_thread, 165 const base::Closure& error_callback, 166 ProfileSyncComponentsFactory* profile_sync_factory, 167 Profile* profile, 168 ProfileSyncService* sync_service) 169 : DataTypeController(ui_thread, error_callback, DisableTypeCallback()), 170 state_(NOT_RUNNING), 171 profile_sync_factory_(profile_sync_factory), 172 profile_(profile), 173 profile_sync_service_(sync_service), 174 model_associator_(NULL), 175 change_processor_(NULL), 176 weak_ptr_factory_(this) { 177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 178 DCHECK(profile_sync_factory_); 179 DCHECK(profile_); 180 DCHECK(profile_sync_service_); 181 } 182 183 void NonFrontendDataTypeController::LoadModels( 184 const ModelLoadCallback& model_load_callback) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 DCHECK(!model_load_callback.is_null()); 187 if (state_ != NOT_RUNNING) { 188 model_load_callback.Run(type(), 189 syncer::SyncError(FROM_HERE, 190 syncer::SyncError::DATATYPE_ERROR, 191 "Model already loaded", 192 type())); 193 return; 194 } 195 196 state_ = MODEL_STARTING; 197 if (!StartModels()) { 198 // We failed to start the models. There is no point in waiting. 199 // Note: This code is deprecated. The only 2 datatypes here, 200 // passwords and typed urls, dont have any special loading. So if we 201 // get a false it means they failed. 202 DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING 203 || state_ == DISABLED); 204 model_load_callback.Run(type(), 205 syncer::SyncError(FROM_HERE, 206 syncer::SyncError::DATATYPE_ERROR, 207 "Failed loading", 208 type())); 209 return; 210 } 211 state_ = MODEL_LOADED; 212 213 model_load_callback.Run(type(), syncer::SyncError()); 214 } 215 216 void NonFrontendDataTypeController::OnModelLoaded() { 217 NOTREACHED(); 218 } 219 220 void NonFrontendDataTypeController::StartAssociating( 221 const StartCallback& start_callback) { 222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 223 DCHECK(!start_callback.is_null()); 224 DCHECK(!components_container_); 225 DCHECK_EQ(state_, MODEL_LOADED); 226 227 // Kick off association on the thread the datatype resides on. 228 state_ = ASSOCIATING; 229 start_callback_ = start_callback; 230 231 components_container_.reset(new BackendComponentsContainer(this)); 232 233 if (!PostTaskOnBackendThread( 234 FROM_HERE, 235 base::Bind(&BackendComponentsContainer::Run, 236 base::Unretained(components_container_.get())))) { 237 syncer::SyncError error( 238 FROM_HERE, 239 syncer::SyncError::DATATYPE_ERROR, 240 "Failed to post StartAssociation", type()); 241 syncer::SyncMergeResult local_merge_result(type()); 242 local_merge_result.set_error(error); 243 StartDone(ASSOCIATION_FAILED, 244 local_merge_result, 245 syncer::SyncMergeResult(type())); 246 } 247 } 248 249 void DestroyComponentsInBackend( 250 NonFrontendDataTypeController::BackendComponentsContainer *container) { 251 delete container; 252 } 253 254 void NonFrontendDataTypeController::Stop() { 255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 256 DCHECK_NE(state_, NOT_RUNNING); 257 258 // Deactivate the date type on the UI thread first to stop processing 259 // sync server changes. This needs to happen before posting task to destroy 260 // processor and associator on backend. Otherwise it could crash if syncer 261 // post work to backend after destruction task and that work is run before 262 // deactivation. 263 profile_sync_service()->DeactivateDataType(type()); 264 265 // Ignore association callback. 266 weak_ptr_factory_.InvalidateWeakPtrs(); 267 268 // Disconnect on UI and post task to destroy on backend. 269 if (components_container_) { 270 components_container_->Disconnect(); 271 PostTaskOnBackendThread( 272 FROM_HERE, 273 base::Bind(&DestroyComponentsInBackend, 274 components_container_.release())); 275 model_associator_ = NULL; 276 change_processor_ = NULL; 277 } 278 279 // Call start callback if waiting for association. 280 if (state_ == ASSOCIATING) { 281 StartDone(ABORTED, 282 syncer::SyncMergeResult(type()), 283 syncer::SyncMergeResult(type())); 284 285 } 286 287 state_ = NOT_RUNNING; 288 } 289 290 std::string NonFrontendDataTypeController::name() const { 291 // For logging only. 292 return syncer::ModelTypeToString(type()); 293 } 294 295 DataTypeController::State NonFrontendDataTypeController::state() const { 296 return state_; 297 } 298 299 void NonFrontendDataTypeController::OnSingleDatatypeUnrecoverableError( 300 const tracked_objects::Location& from_here, 301 const std::string& message) { 302 DCHECK(IsOnBackendThread()); 303 RecordUnrecoverableError(from_here, message); 304 BrowserThread::PostTask(BrowserThread::UI, from_here, 305 base::Bind(&NonFrontendDataTypeController::DisableImpl, 306 this, 307 from_here, 308 message)); 309 } 310 311 NonFrontendDataTypeController::NonFrontendDataTypeController() 312 : DataTypeController(base::MessageLoopProxy::current(), base::Closure(), 313 DisableTypeCallback()), 314 state_(NOT_RUNNING), 315 profile_sync_factory_(NULL), 316 profile_(NULL), 317 profile_sync_service_(NULL), 318 model_associator_(NULL), 319 change_processor_(NULL), 320 weak_ptr_factory_(this) { 321 } 322 323 NonFrontendDataTypeController::~NonFrontendDataTypeController() { 324 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 325 DCHECK(!change_processor_); 326 DCHECK(!model_associator_); 327 } 328 329 bool NonFrontendDataTypeController::StartModels() { 330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 331 DCHECK_EQ(state_, MODEL_STARTING); 332 // By default, no additional services need to be started before we can proceed 333 // with model association, so do nothing. 334 return true; 335 } 336 337 bool NonFrontendDataTypeController::IsOnBackendThread() { 338 return !BrowserThread::CurrentlyOn(BrowserThread::UI); 339 } 340 341 void NonFrontendDataTypeController::StartDone( 342 DataTypeController::StartResult start_result, 343 const syncer::SyncMergeResult& local_merge_result, 344 const syncer::SyncMergeResult& syncer_merge_result) { 345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 346 DataTypeController::State new_state; 347 348 if (IsSuccessfulResult(start_result)) { 349 new_state = RUNNING; 350 } else { 351 new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING); 352 if (IsUnrecoverableResult(start_result)) 353 RecordUnrecoverableError(FROM_HERE, "StartFailed"); 354 } 355 356 StartDoneImpl(start_result, new_state, local_merge_result, 357 syncer_merge_result); 358 } 359 360 void NonFrontendDataTypeController::StartDoneImpl( 361 DataTypeController::StartResult start_result, 362 DataTypeController::State new_state, 363 const syncer::SyncMergeResult& local_merge_result, 364 const syncer::SyncMergeResult& syncer_merge_result) { 365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 366 367 state_ = new_state; 368 if (state_ != RUNNING) { 369 // Start failed. 370 RecordStartFailure(start_result); 371 } 372 373 DCHECK(!start_callback_.is_null()); 374 // We have to release the callback before we call it, since it's possible 375 // invoking the callback will trigger a call to STOP(), which will get 376 // confused by the non-NULL start_callback_. 377 StartCallback callback = start_callback_; 378 start_callback_.Reset(); 379 callback.Run(start_result, local_merge_result, syncer_merge_result); 380 } 381 382 void NonFrontendDataTypeController::DisableImpl( 383 const tracked_objects::Location& from_here, 384 const std::string& message) { 385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 386 profile_sync_service_->DisableDatatype(type(), from_here, message); 387 } 388 389 void NonFrontendDataTypeController::RecordAssociationTime( 390 base::TimeDelta time) { 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 392 #define PER_DATA_TYPE_MACRO(type_str) \ 393 UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time); 394 SYNC_DATA_TYPE_HISTOGRAM(type()); 395 #undef PER_DATA_TYPE_MACRO 396 } 397 398 void NonFrontendDataTypeController::RecordStartFailure(StartResult result) { 399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 400 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", 401 ModelTypeToHistogramInt(type()), 402 syncer::MODEL_TYPE_COUNT); 403 #define PER_DATA_TYPE_MACRO(type_str) \ 404 UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \ 405 MAX_START_RESULT); 406 SYNC_DATA_TYPE_HISTOGRAM(type()); 407 #undef PER_DATA_TYPE_MACRO 408 } 409 410 void NonFrontendDataTypeController::RecordUnrecoverableError( 411 const tracked_objects::Location& from_here, 412 const std::string& message) { 413 DVLOG(1) << "Datatype Controller failed for type " 414 << ModelTypeToString(type()) << " " 415 << message << " at location " 416 << from_here.ToString(); 417 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", 418 ModelTypeToHistogramInt(type()), 419 syncer::MODEL_TYPE_COUNT); 420 421 if (!error_callback_.is_null()) 422 error_callback_.Run(); 423 } 424 425 426 ProfileSyncComponentsFactory* 427 NonFrontendDataTypeController::profile_sync_factory() const { 428 return profile_sync_factory_; 429 } 430 431 Profile* NonFrontendDataTypeController::profile() const { 432 return profile_; 433 } 434 435 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service() 436 const { 437 return profile_sync_service_; 438 } 439 440 void NonFrontendDataTypeController::set_start_callback( 441 const StartCallback& callback) { 442 start_callback_ = callback; 443 } 444 445 void NonFrontendDataTypeController::set_state(State state) { 446 state_ = state; 447 } 448 449 AssociatorInterface* NonFrontendDataTypeController::associator() const { 450 return model_associator_; 451 } 452 453 ChangeProcessor* NonFrontendDataTypeController::GetChangeProcessor() const { 454 return change_processor_; 455 } 456 457 void NonFrontendDataTypeController::AssociationCallback( 458 AssociationResult result) { 459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 460 461 if (result.needs_crypto) { 462 StartDone(NEEDS_CRYPTO, 463 result.local_merge_result, 464 result.syncer_merge_result); 465 return; 466 } 467 468 if (result.unrecoverable_error) { 469 StartDone(UNRECOVERABLE_ERROR, 470 result.local_merge_result, 471 result.syncer_merge_result); 472 return; 473 } 474 475 RecordAssociationTime(result.association_time); 476 if (result.error.IsSet()) { 477 StartDone(ASSOCIATION_FAILED, 478 result.local_merge_result, 479 result.syncer_merge_result); 480 return; 481 } 482 483 CHECK(result.change_processor); 484 CHECK(result.model_associator); 485 change_processor_ = result.change_processor; 486 model_associator_ = result.model_associator; 487 488 StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK, 489 result.local_merge_result, 490 result.syncer_merge_result); 491 } 492 493 } // namespace browser_sync 494