Chromium Code Reviews| Index: chrome/browser/sync/glue/ui_data_type_controller.cc |
| diff --git a/chrome/browser/sync/glue/ui_data_type_controller.cc b/chrome/browser/sync/glue/ui_data_type_controller.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..309e2e6b3ec93a585e440985c62d5a84fc05f77b |
| --- /dev/null |
| +++ b/chrome/browser/sync/glue/ui_data_type_controller.cc |
| @@ -0,0 +1,248 @@ |
| +// Copyright (c) 2012 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 "chrome/browser/sync/glue/ui_data_type_controller.h" |
| + |
| +#include "base/logging.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/sync/api/sync_error.h" |
| +#include "chrome/browser/sync/glue/generic_change_processor.h" |
| +#include "chrome/browser/sync/profile_sync_components_factory.h" |
| +#include "chrome/browser/sync/profile_sync_service.h" |
| +#include "chrome/browser/sync/syncable/model_type.h" |
| +#include "chrome/browser/sync/util/data_type_histogram.h" |
| +#include "content/public/browser/browser_thread.h" |
| + |
| +using content::BrowserThread; |
| + |
| +namespace browser_sync { |
| + |
| +UIDataTypeController::UIDataTypeController() |
| + : profile_sync_factory_(NULL), |
| + profile_(NULL), |
| + sync_service_(NULL), |
| + state_(NOT_RUNNING) { |
| +} |
| + |
| +UIDataTypeController::UIDataTypeController( |
| + ProfileSyncComponentsFactory* profile_sync_factory, |
| + Profile* profile, |
| + ProfileSyncService* sync_service) |
| + : profile_sync_factory_(profile_sync_factory), |
| + profile_(profile), |
| + sync_service_(sync_service), |
| + state_(NOT_RUNNING) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + DCHECK(profile_sync_factory); |
| + DCHECK(profile); |
| + DCHECK(sync_service); |
| +} |
| + |
| +UIDataTypeController::~UIDataTypeController() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| +} |
| + |
| +void UIDataTypeController::Start(const StartCallback& start_callback) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + DCHECK(!start_callback.is_null()); |
| + if (state_ != NOT_RUNNING) { |
| + start_callback.Run(BUSY, SyncError()); |
| + return; |
| + } |
| + |
| + start_callback_ = start_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_ = 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); |
| + local_service_ = profile_sync_factory_->GetSyncableServiceForType(type()); |
| + if (!local_service_.get()) { |
| + SyncError error(FROM_HERE, "Failed to connect to syncable service.", |
| + type()); |
| + StartFailed(UNRECOVERABLE_ERROR, error); |
| + return; |
| + } |
| + |
| + // We maintain ownership until MergeDataAndStartSyncing is called. |
| + scoped_ptr<GenericChangeProcessor> sync_processor_( |
|
akalin
2012/02/24 23:54:02
remove trailing _
Nicolas Zea
2012/03/01 20:50:26
Done.
|
| + profile_sync_factory_->CreateGenericChangeProcessor( |
| + sync_service_, this, local_service_)); |
| + |
| + if (!sync_processor_->CryptoReadyIfNecessary(type())) { |
| + StartFailed(NEEDS_CRYPTO, SyncError()); |
| + return; |
| + } |
| + |
| + bool sync_has_nodes = false; |
| + if (!sync_processor_->SyncModelHasUserCreatedNodes(type(), &sync_has_nodes)) { |
| + SyncError error(FROM_HERE, "Failed to load sync nodes", type()); |
| + StartFailed(UNRECOVERABLE_ERROR, error); |
| + return; |
| + } |
| + |
| + base::TimeTicks start_time = base::TimeTicks::Now(); |
| + SyncDataList initial_sync_data; |
| + SyncError error =sync_processor_->GetSyncDataForType( |
|
akalin
2012/02/24 23:54:02
space after =
Nicolas Zea
2012/03/01 20:50:26
Done.
|
| + type(), &initial_sync_data); |
| + if (error.IsSet()) { |
| + StartFailed(ASSOCIATION_FAILED, error); |
| + return; |
| + } |
| + |
| + // Takes ownership of sync_processor. |
|
akalin
2012/02/24 23:54:02
this should be right above the MDASS call
Nicolas Zea
2012/03/01 20:50:26
Done.
|
| + // TODO(zea): this should use scoped_ptr<T>::Pass semantics. |
| + GenericChangeProcessor* saved_sync_processor = sync_processor_.get(); |
| + error = local_service_->MergeDataAndStartSyncing(type(), |
| + initial_sync_data, |
| + sync_processor_.release()); |
| + if (error.IsSet()) { |
| + StartFailed(ASSOCIATION_FAILED, error); |
| + return; |
| + } |
| + RecordAssociationTime(base::TimeTicks::Now() - start_time); |
| + |
| + sync_service_->ActivateDataType(type(), model_safe_group(), |
| + saved_sync_processor); |
| + |
| + // StartDone(..) invokes the DataTypeManager callback, which can lead to a |
| + // call to Stop() if one of the other data types being started generates an |
| + // error. |
| + state_ = RUNNING; |
| + StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK); |
|
akalin
2012/02/24 23:54:02
can you invert the condition? (and the last two pa
Nicolas Zea
2012/03/01 20:50:26
Done.
|
| +} |
| + |
| +void UIDataTypeController::StartFailed(StartResult result, |
| + const SyncError& error) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + StopModels(); |
| + if (result == ASSOCIATION_FAILED) { |
| + state_ = DISABLED; |
| + } else { |
| + state_ = NOT_RUNNING; |
| + } |
| + RecordStartFailure(result); |
| + |
| + // 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(result, error); |
| +} |
| + |
| +void UIDataTypeController::StartDone(StartResult result) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + // 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 |
|
akalin
2012/02/24 23:54:02
STOP() -> Stop()
Nicolas Zea
2012/03/01 20:50:26
Done.
|
| + // confused by the non-NULL start_callback_. |
| + StartCallback callback = start_callback_; |
| + start_callback_.Reset(); |
| + callback.Run(result, SyncError()); |
| +} |
| + |
| +void UIDataTypeController::Stop() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + // If Stop() is called while Start() is waiting for the datatype model to |
| + // load, abort the start. |
| + if (state_ == MODEL_STARTING) { |
| + StartFailed(ABORTED, SyncError()); |
| + // 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(); |
| + |
| + sync_service_->DeactivateDataType(type()); |
| + |
| + if (local_service_.get()) { |
| + local_service_->StopSyncing(type()); |
| + } |
| + |
| + state_ = NOT_RUNNING; |
| +} |
| + |
| +void UIDataTypeController::StopModels() { |
| + // Do nothing by default. |
| +} |
| + |
| +browser_sync::ModelSafeGroup UIDataTypeController::model_safe_group() const { |
| + return browser_sync::GROUP_UI; |
| +} |
| + |
| +std::string UIDataTypeController::name() const { |
| + // For logging only. |
| + return syncable::ModelTypeToString(type()); |
| +} |
| + |
| +DataTypeController::State UIDataTypeController::state() const { |
| + return state_; |
| +} |
| + |
| +void UIDataTypeController::OnUnrecoverableError( |
| + const tracked_objects::Location& from_here, const std::string& message) { |
| + RecordUnrecoverableError(from_here, message); |
| + |
| + // The ProfileSyncService will invoke our Stop() method in response to this. |
| + // We dont know the current state of the caller. Posting a task will allow |
| + // the caller to unwind the stack before we process unrecoverable error. |
| + MessageLoop::current()->PostTask(from_here, |
| + base::Bind(&ProfileSyncService::OnUnrecoverableError, |
| + sync_service_->AsWeakPtr(), |
| + from_here, |
| + message)); |
| +} |
| + |
| +void UIDataTypeController::RecordUnrecoverableError( |
| + const tracked_objects::Location& from_here, |
| + const std::string& message) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", type(), |
| + syncable::MODEL_TYPE_COUNT); |
| +} |
| + |
| +void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| +#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(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", type(), |
| + syncable::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 |