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