1// Copyright 2014 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 "components/sync_driver/ui_data_type_controller.h" 6 7#include "base/logging.h" 8#include "base/memory/weak_ptr.h" 9#include "components/sync_driver/generic_change_processor_factory.h" 10#include "components/sync_driver/shared_change_processor_ref.h" 11#include "sync/api/sync_error.h" 12#include "sync/api/syncable_service.h" 13#include "sync/internal_api/public/base/model_type.h" 14#include "sync/util/data_type_histogram.h" 15 16namespace sync_driver { 17 18UIDataTypeController::UIDataTypeController() 19 : DataTypeController(base::MessageLoopProxy::current(), 20 base::Closure()), 21 sync_factory_(NULL), 22 state_(NOT_RUNNING), 23 type_(syncer::UNSPECIFIED) { 24} 25 26UIDataTypeController::UIDataTypeController( 27 scoped_refptr<base::MessageLoopProxy> ui_thread, 28 const base::Closure& error_callback, 29 syncer::ModelType type, 30 SyncApiComponentFactory* sync_factory) 31 : DataTypeController(ui_thread, error_callback), 32 sync_factory_(sync_factory), 33 state_(NOT_RUNNING), 34 type_(type), 35 processor_factory_(new GenericChangeProcessorFactory()), 36 ui_thread_(ui_thread) { 37 DCHECK(ui_thread_->BelongsToCurrentThread()); 38 DCHECK(sync_factory); 39 DCHECK(syncer::IsRealDataType(type_)); 40} 41 42void UIDataTypeController::SetGenericChangeProcessorFactoryForTest( 43 scoped_ptr<GenericChangeProcessorFactory> factory) { 44 DCHECK_EQ(state_, NOT_RUNNING); 45 processor_factory_ = factory.Pass(); 46} 47 48UIDataTypeController::~UIDataTypeController() { 49 DCHECK(ui_thread_->BelongsToCurrentThread()); 50} 51 52void UIDataTypeController::LoadModels( 53 const ModelLoadCallback& model_load_callback) { 54 DCHECK(ui_thread_->BelongsToCurrentThread()); 55 DCHECK(syncer::IsRealDataType(type_)); 56 model_load_callback_ = model_load_callback; 57 if (state_ != NOT_RUNNING) { 58 model_load_callback.Run(type(), 59 syncer::SyncError(FROM_HERE, 60 syncer::SyncError::DATATYPE_ERROR, 61 "Model already loaded", 62 type())); 63 return; 64 } 65 // Since we can't be called multiple times before Stop() is called, 66 // |shared_change_processor_| must be NULL here. 67 DCHECK(!shared_change_processor_.get()); 68 shared_change_processor_ = new SharedChangeProcessor(); 69 70 state_ = MODEL_STARTING; 71 if (!StartModels()) { 72 // If we are waiting for some external service to load before associating 73 // or we failed to start the models, we exit early. state_ will control 74 // what we perform next. 75 DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING); 76 return; 77 } 78 79 state_ = MODEL_LOADED; 80 model_load_callback_.Run(type(), syncer::SyncError()); 81} 82 83void UIDataTypeController::OnModelLoaded() { 84 DCHECK(ui_thread_->BelongsToCurrentThread()); 85 DCHECK_EQ(state_, MODEL_STARTING); 86 87 state_ = MODEL_LOADED; 88 model_load_callback_.Run(type(), syncer::SyncError()); 89} 90 91void UIDataTypeController::StartAssociating( 92 const StartCallback& start_callback) { 93 DCHECK(ui_thread_->BelongsToCurrentThread()); 94 DCHECK(!start_callback.is_null()); 95 DCHECK_EQ(state_, MODEL_LOADED); 96 97 start_callback_ = start_callback; 98 state_ = ASSOCIATING; 99 Associate(); 100 // It's possible StartDone(..) resulted in a Stop() call, or that association 101 // failed, so we just verify that the state has moved foward. 102 DCHECK_NE(state_, ASSOCIATING); 103} 104 105bool UIDataTypeController::StartModels() { 106 DCHECK_EQ(state_, MODEL_STARTING); 107 // By default, no additional services need to be started before we can proceed 108 // with model association. 109 return true; 110} 111 112void UIDataTypeController::Associate() { 113 DCHECK_EQ(state_, ASSOCIATING); 114 syncer::SyncMergeResult local_merge_result(type()); 115 syncer::SyncMergeResult syncer_merge_result(type()); 116 base::WeakPtrFactory<syncer::SyncMergeResult> weak_ptr_factory( 117 &syncer_merge_result); 118 119 // Connect |shared_change_processor_| to the syncer and get the 120 // syncer::SyncableService associated with type(). 121 local_service_ = shared_change_processor_->Connect( 122 sync_factory_, 123 processor_factory_.get(), 124 user_share(), 125 this, 126 type(), 127 weak_ptr_factory.GetWeakPtr()); 128 if (!local_service_.get()) { 129 syncer::SyncError error(FROM_HERE, 130 syncer::SyncError::DATATYPE_ERROR, 131 "Failed to connect to syncer.", 132 type()); 133 local_merge_result.set_error(error); 134 StartDone(ASSOCIATION_FAILED, 135 local_merge_result, 136 syncer_merge_result); 137 return; 138 } 139 140 if (!shared_change_processor_->CryptoReadyIfNecessary()) { 141 syncer::SyncError error(FROM_HERE, 142 syncer::SyncError::CRYPTO_ERROR, 143 "", 144 type()); 145 local_merge_result.set_error(error); 146 StartDone(NEEDS_CRYPTO, 147 local_merge_result, 148 syncer_merge_result); 149 return; 150 } 151 152 bool sync_has_nodes = false; 153 if (!shared_change_processor_->SyncModelHasUserCreatedNodes( 154 &sync_has_nodes)) { 155 syncer::SyncError error(FROM_HERE, 156 syncer::SyncError::UNRECOVERABLE_ERROR, 157 "Failed to load sync nodes", 158 type()); 159 local_merge_result.set_error(error); 160 StartDone(UNRECOVERABLE_ERROR, 161 local_merge_result, 162 syncer_merge_result); 163 return; 164 } 165 166 base::TimeTicks start_time = base::TimeTicks::Now(); 167 syncer::SyncDataList initial_sync_data; 168 syncer::SyncError error = 169 shared_change_processor_->GetAllSyncDataReturnError( 170 type(), &initial_sync_data); 171 if (error.IsSet()) { 172 local_merge_result.set_error(error); 173 StartDone(ASSOCIATION_FAILED, 174 local_merge_result, 175 syncer_merge_result); 176 return; 177 } 178 179 std::string datatype_context; 180 if (shared_change_processor_->GetDataTypeContext(&datatype_context)) { 181 local_service_->UpdateDataTypeContext( 182 type(), syncer::SyncChangeProcessor::NO_REFRESH, datatype_context); 183 } 184 185 syncer_merge_result.set_num_items_before_association( 186 initial_sync_data.size()); 187 // Passes a reference to |shared_change_processor_|. 188 local_merge_result = local_service_->MergeDataAndStartSyncing( 189 type(), 190 initial_sync_data, 191 scoped_ptr<syncer::SyncChangeProcessor>( 192 new SharedChangeProcessorRef(shared_change_processor_)), 193 scoped_ptr<syncer::SyncErrorFactory>( 194 new SharedChangeProcessorRef(shared_change_processor_))); 195 RecordAssociationTime(base::TimeTicks::Now() - start_time); 196 if (local_merge_result.error().IsSet()) { 197 StartDone(ASSOCIATION_FAILED, 198 local_merge_result, 199 syncer_merge_result); 200 return; 201 } 202 203 syncer_merge_result.set_num_items_after_association( 204 shared_change_processor_->GetSyncCount()); 205 206 state_ = RUNNING; 207 StartDone(sync_has_nodes ? OK : OK_FIRST_RUN, 208 local_merge_result, 209 syncer_merge_result); 210} 211 212ChangeProcessor* UIDataTypeController::GetChangeProcessor() const { 213 DCHECK_EQ(state_, RUNNING); 214 return shared_change_processor_->generic_change_processor(); 215} 216 217void UIDataTypeController::AbortModelLoad() { 218 DCHECK(ui_thread_->BelongsToCurrentThread()); 219 state_ = NOT_RUNNING; 220 221 if (shared_change_processor_.get()) { 222 shared_change_processor_ = NULL; 223 } 224 225 // We don't want to continue loading models (e.g OnModelLoaded should never be 226 // called after we've decided to abort). 227 StopModels(); 228} 229 230void UIDataTypeController::StartDone( 231 ConfigureResult start_result, 232 const syncer::SyncMergeResult& local_merge_result, 233 const syncer::SyncMergeResult& syncer_merge_result) { 234 DCHECK(ui_thread_->BelongsToCurrentThread()); 235 236 if (!IsSuccessfulResult(start_result)) { 237 StopModels(); 238 if (start_result == ASSOCIATION_FAILED) { 239 state_ = DISABLED; 240 } else { 241 state_ = NOT_RUNNING; 242 } 243 RecordStartFailure(start_result); 244 245 if (shared_change_processor_.get()) { 246 shared_change_processor_->Disconnect(); 247 shared_change_processor_ = NULL; 248 } 249 } 250 251 start_callback_.Run(start_result, local_merge_result, syncer_merge_result); 252} 253 254void UIDataTypeController::Stop() { 255 DCHECK(ui_thread_->BelongsToCurrentThread()); 256 DCHECK(syncer::IsRealDataType(type_)); 257 258 if (state_ == NOT_RUNNING) 259 return; 260 261 State prev_state = state_; 262 state_ = STOPPING; 263 264 if (shared_change_processor_.get()) { 265 shared_change_processor_->Disconnect(); 266 shared_change_processor_ = NULL; 267 } 268 269 // If Stop() is called while Start() is waiting for the datatype model to 270 // load, abort the start. 271 if (prev_state == MODEL_STARTING) { 272 AbortModelLoad(); 273 // We can just return here since we haven't performed association if we're 274 // still in MODEL_STARTING. 275 return; 276 } 277 278 StopModels(); 279 280 if (local_service_.get()) { 281 local_service_->StopSyncing(type()); 282 } 283 284 state_ = NOT_RUNNING; 285} 286 287syncer::ModelType UIDataTypeController::type() const { 288 DCHECK(syncer::IsRealDataType(type_)); 289 return type_; 290} 291 292void UIDataTypeController::StopModels() { 293 // Do nothing by default. 294} 295 296syncer::ModelSafeGroup UIDataTypeController::model_safe_group() const { 297 DCHECK(syncer::IsRealDataType(type_)); 298 return syncer::GROUP_UI; 299} 300 301std::string UIDataTypeController::name() const { 302 // For logging only. 303 return syncer::ModelTypeToString(type()); 304} 305 306DataTypeController::State UIDataTypeController::state() const { 307 return state_; 308} 309 310void UIDataTypeController::OnSingleDataTypeUnrecoverableError( 311 const syncer::SyncError& error) { 312 DCHECK_EQ(type(), error.model_type()); 313 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", 314 ModelTypeToHistogramInt(type()), 315 syncer::MODEL_TYPE_COUNT); 316 // TODO(tim): We double-upload some errors. See bug 383480. 317 if (!error_callback_.is_null()) 318 error_callback_.Run(); 319 if (!model_load_callback_.is_null()) { 320 base::MessageLoop::current()->PostTask( 321 FROM_HERE, base::Bind(model_load_callback_, type(), error)); 322 } 323} 324 325void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) { 326 DCHECK(ui_thread_->BelongsToCurrentThread()); 327#define PER_DATA_TYPE_MACRO(type_str) \ 328 UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time); 329 SYNC_DATA_TYPE_HISTOGRAM(type()); 330#undef PER_DATA_TYPE_MACRO 331} 332 333void UIDataTypeController::RecordStartFailure(ConfigureResult result) { 334 DCHECK(ui_thread_->BelongsToCurrentThread()); 335 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", 336 ModelTypeToHistogramInt(type()), 337 syncer::MODEL_TYPE_COUNT); 338#define PER_DATA_TYPE_MACRO(type_str) \ 339 UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \ 340 MAX_START_RESULT); 341 SYNC_DATA_TYPE_HISTOGRAM(type()); 342#undef PER_DATA_TYPE_MACRO 343} 344 345} // namespace sync_driver 346