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