From 21ac7cd3468fd3052be197202057db0084bb0774 Mon Sep 17 00:00:00 2001 From: "tim@chromium.org" Date: Sun, 15 Jun 2014 00:23:43 +0000 Subject: sync: move [Non]UIDataTypeController to components/sync_driver BUG=339726 Review URL: https://codereview.chromium.org/338493006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@277286 0039d316-1c4b-4281-b951-d872f2087c98 --- components/sync_driver/ui_data_type_controller.cc | 354 ++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 components/sync_driver/ui_data_type_controller.cc (limited to 'components/sync_driver/ui_data_type_controller.cc') diff --git a/components/sync_driver/ui_data_type_controller.cc b/components/sync_driver/ui_data_type_controller.cc new file mode 100644 index 0000000..4caa59c --- /dev/null +++ b/components/sync_driver/ui_data_type_controller.cc @@ -0,0 +1,354 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync_driver/ui_data_type_controller.h" + +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "components/sync_driver/generic_change_processor_factory.h" +#include "components/sync_driver/shared_change_processor_ref.h" +#include "sync/api/sync_error.h" +#include "sync/api/syncable_service.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/util/data_type_histogram.h" + +namespace browser_sync { + +UIDataTypeController::UIDataTypeController() + : DataTypeController(base::MessageLoopProxy::current(), + base::Closure(), + DisableTypeCallback()), + sync_factory_(NULL), + state_(NOT_RUNNING), + type_(syncer::UNSPECIFIED) { +} + +UIDataTypeController::UIDataTypeController( + scoped_refptr ui_thread, + const base::Closure& error_callback, + const DisableTypeCallback& disable_callback, + syncer::ModelType type, + SyncApiComponentFactory* sync_factory) + : DataTypeController(ui_thread, error_callback, disable_callback), + sync_factory_(sync_factory), + state_(NOT_RUNNING), + type_(type), + processor_factory_(new GenericChangeProcessorFactory()), + ui_thread_(ui_thread) { + DCHECK(ui_thread_->BelongsToCurrentThread()); + DCHECK(sync_factory); + DCHECK(syncer::IsRealDataType(type_)); +} + +void UIDataTypeController::SetGenericChangeProcessorFactoryForTest( + scoped_ptr factory) { + DCHECK_EQ(state_, NOT_RUNNING); + processor_factory_ = factory.Pass(); +} + +UIDataTypeController::~UIDataTypeController() { + DCHECK(ui_thread_->BelongsToCurrentThread()); +} + +void UIDataTypeController::LoadModels( + const ModelLoadCallback& model_load_callback) { + DCHECK(ui_thread_->BelongsToCurrentThread()); + DCHECK(!model_load_callback.is_null()); + DCHECK(syncer::IsRealDataType(type_)); + if (state_ != NOT_RUNNING) { + model_load_callback.Run(type(), + syncer::SyncError(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "Model already loaded", + type())); + return; + } + // Since we can't be called multiple times before Stop() is called, + // |shared_change_processor_| must be NULL here. + DCHECK(!shared_change_processor_.get()); + shared_change_processor_ = new SharedChangeProcessor(); + + model_load_callback_ = model_load_callback; + state_ = MODEL_STARTING; + if (!StartModels()) { + // If we are waiting for some external service to load before associating + // or we failed to start the models, we exit early. state_ will control + // what we perform next. + DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING); + return; + } + + state_ = MODEL_LOADED; + model_load_callback_.Reset(); + model_load_callback.Run(type(), syncer::SyncError()); +} + +void UIDataTypeController::OnModelLoaded() { + DCHECK(ui_thread_->BelongsToCurrentThread()); + DCHECK(!model_load_callback_.is_null()); + DCHECK_EQ(state_, MODEL_STARTING); + + state_ = MODEL_LOADED; + ModelLoadCallback model_load_callback = model_load_callback_; + model_load_callback_.Reset(); + model_load_callback.Run(type(), syncer::SyncError()); +} + +void UIDataTypeController::StartAssociating( + const StartCallback& start_callback) { + DCHECK(ui_thread_->BelongsToCurrentThread()); + DCHECK(!start_callback.is_null()); + DCHECK_EQ(state_, MODEL_LOADED); + + start_callback_ = start_callback; + state_ = ASSOCIATING; + Associate(); + // It's possible StartDone(..) resulted in a Stop() call, or that association + // failed, so we just verify that the state has moved foward. + DCHECK_NE(state_, ASSOCIATING); +} + +bool UIDataTypeController::StartModels() { + DCHECK_EQ(state_, MODEL_STARTING); + // By default, no additional services need to be started before we can proceed + // with model association. + return true; +} + +void UIDataTypeController::Associate() { + DCHECK_EQ(state_, ASSOCIATING); + syncer::SyncMergeResult local_merge_result(type()); + syncer::SyncMergeResult syncer_merge_result(type()); + base::WeakPtrFactory weak_ptr_factory( + &syncer_merge_result); + + // Connect |shared_change_processor_| to the syncer and get the + // syncer::SyncableService associated with type(). + local_service_ = shared_change_processor_->Connect( + sync_factory_, + processor_factory_.get(), + user_share(), + this, + type(), + weak_ptr_factory.GetWeakPtr()); + if (!local_service_.get()) { + syncer::SyncError error(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "Failed to connect to syncer.", + type()); + local_merge_result.set_error(error); + StartDone(ASSOCIATION_FAILED, + local_merge_result, + syncer_merge_result); + return; + } + + if (!shared_change_processor_->CryptoReadyIfNecessary()) { + StartDone(NEEDS_CRYPTO, + local_merge_result, + syncer_merge_result); + return; + } + + bool sync_has_nodes = false; + if (!shared_change_processor_->SyncModelHasUserCreatedNodes( + &sync_has_nodes)) { + syncer::SyncError error(FROM_HERE, + syncer::SyncError::UNRECOVERABLE_ERROR, + "Failed to load sync nodes", + type()); + local_merge_result.set_error(error); + StartDone(UNRECOVERABLE_ERROR, + local_merge_result, + syncer_merge_result); + return; + } + + base::TimeTicks start_time = base::TimeTicks::Now(); + syncer::SyncDataList initial_sync_data; + syncer::SyncError error = + shared_change_processor_->GetAllSyncDataReturnError( + type(), &initial_sync_data); + if (error.IsSet()) { + local_merge_result.set_error(error); + StartDone(ASSOCIATION_FAILED, + local_merge_result, + syncer_merge_result); + return; + } + + std::string datatype_context; + if (shared_change_processor_->GetDataTypeContext(&datatype_context)) { + local_service_->UpdateDataTypeContext( + type(), syncer::SyncChangeProcessor::NO_REFRESH, datatype_context); + } + + syncer_merge_result.set_num_items_before_association( + initial_sync_data.size()); + // Passes a reference to |shared_change_processor_|. + local_merge_result = local_service_->MergeDataAndStartSyncing( + type(), + initial_sync_data, + scoped_ptr( + new SharedChangeProcessorRef(shared_change_processor_)), + scoped_ptr( + new SharedChangeProcessorRef(shared_change_processor_))); + RecordAssociationTime(base::TimeTicks::Now() - start_time); + if (local_merge_result.error().IsSet()) { + StartDone(ASSOCIATION_FAILED, + local_merge_result, + syncer_merge_result); + return; + } + + syncer_merge_result.set_num_items_after_association( + shared_change_processor_->GetSyncCount()); + + state_ = RUNNING; + StartDone(sync_has_nodes ? OK : OK_FIRST_RUN, + local_merge_result, + syncer_merge_result); +} + +ChangeProcessor* UIDataTypeController::GetChangeProcessor() const { + DCHECK_EQ(state_, RUNNING); + return shared_change_processor_->generic_change_processor(); +} + +void UIDataTypeController::AbortModelLoad() { + DCHECK(ui_thread_->BelongsToCurrentThread()); + state_ = NOT_RUNNING; + + if (shared_change_processor_.get()) { + shared_change_processor_ = NULL; + } + + ModelLoadCallback model_load_callback = model_load_callback_; + model_load_callback_.Reset(); + model_load_callback.Run(type(), + syncer::SyncError(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "Aborted", + type())); + // We don't want to continue loading models (e.g OnModelLoaded should never be + // called after we've decided to abort). + StopModels(); +} + +void UIDataTypeController::StartDone( + StartResult start_result, + const syncer::SyncMergeResult& local_merge_result, + const syncer::SyncMergeResult& syncer_merge_result) { + DCHECK(ui_thread_->BelongsToCurrentThread()); + + if (!IsSuccessfulResult(start_result)) { + StopModels(); + if (start_result == ASSOCIATION_FAILED) { + state_ = DISABLED; + } else { + state_ = NOT_RUNNING; + } + RecordStartFailure(start_result); + + if (shared_change_processor_.get()) { + shared_change_processor_->Disconnect(); + shared_change_processor_ = NULL; + } + } + + // We have to release the callback before we call it, since it's possible + // invoking the callback will trigger a call to Stop(), which will get + // confused by the non-NULL start_callback_. + StartCallback callback = start_callback_; + start_callback_.Reset(); + callback.Run(start_result, local_merge_result, syncer_merge_result); +} + +void UIDataTypeController::Stop() { + DCHECK(ui_thread_->BelongsToCurrentThread()); + DCHECK(syncer::IsRealDataType(type_)); + + State prev_state = state_; + state_ = STOPPING; + + if (shared_change_processor_.get()) { + shared_change_processor_->Disconnect(); + shared_change_processor_ = NULL; + } + + // If Stop() is called while Start() is waiting for the datatype model to + // load, abort the start. + if (prev_state == MODEL_STARTING) { + AbortModelLoad(); + // We can just return here since we haven't performed association if we're + // still in MODEL_STARTING. + return; + } + DCHECK(start_callback_.is_null()); + + StopModels(); + + if (local_service_.get()) { + local_service_->StopSyncing(type()); + } + + state_ = NOT_RUNNING; +} + +syncer::ModelType UIDataTypeController::type() const { + DCHECK(syncer::IsRealDataType(type_)); + return type_; +} + +void UIDataTypeController::StopModels() { + // Do nothing by default. +} + +syncer::ModelSafeGroup UIDataTypeController::model_safe_group() const { + DCHECK(syncer::IsRealDataType(type_)); + return syncer::GROUP_UI; +} + +std::string UIDataTypeController::name() const { + // For logging only. + return syncer::ModelTypeToString(type()); +} + +DataTypeController::State UIDataTypeController::state() const { + return state_; +} + +void UIDataTypeController::OnSingleDatatypeUnrecoverableError( + const tracked_objects::Location& from_here, const std::string& message) { + UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", + ModelTypeToHistogramInt(type()), + syncer::MODEL_TYPE_COUNT); + // TODO(tim): We double-upload some errors. See bug 383480. + if (!error_callback_.is_null()) + error_callback_.Run(); + if (!disable_callback().is_null()) + disable_callback().Run(from_here, message); +} + +void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) { + DCHECK(ui_thread_->BelongsToCurrentThread()); +#define PER_DATA_TYPE_MACRO(type_str) \ + UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time); + SYNC_DATA_TYPE_HISTOGRAM(type()); +#undef PER_DATA_TYPE_MACRO +} + +void UIDataTypeController::RecordStartFailure(StartResult result) { + DCHECK(ui_thread_->BelongsToCurrentThread()); + UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", + ModelTypeToHistogramInt(type()), + syncer::MODEL_TYPE_COUNT); +#define PER_DATA_TYPE_MACRO(type_str) \ + UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \ + MAX_START_RESULT); + SYNC_DATA_TYPE_HISTOGRAM(type()); +#undef PER_DATA_TYPE_MACRO +} + +} // namespace browser_sync -- cgit v1.1