| Index: chrome/browser/sync/glue/non_frontend_data_type_controller.cc
|
| diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..51238f6340f043e9fc1b0434e1ef594a955b7c0f
|
| --- /dev/null
|
| +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc
|
| @@ -0,0 +1,251 @@
|
| +// Copyright (c) 2011 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/non_frontend_data_type_controller.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/sync/glue/change_processor.h"
|
| +#include "chrome/browser/sync/glue/model_associator.h"
|
| +#include "chrome/browser/sync/profile_sync_factory.h"
|
| +#include "chrome/browser/sync/profile_sync_service.h"
|
| +#include "chrome/browser/sync/syncable/model_type.h"
|
| +#include "content/browser/browser_thread.h"
|
| +
|
| +namespace browser_sync {
|
| +
|
| +NonFrontendDataTypeController::NonFrontendDataTypeController()
|
| + : profile_sync_factory_(NULL),
|
| + profile_(NULL),
|
| + sync_service_(NULL),
|
| + abort_association_(false),
|
| + abort_association_complete_(false, false),
|
| + datatype_stopped_(false, false) {}
|
| +
|
| +NonFrontendDataTypeController::NonFrontendDataTypeController(
|
| + ProfileSyncFactory* profile_sync_factory,
|
| + Profile* profile,
|
| + ProfileSyncService* sync_service)
|
| + : profile_sync_factory_(profile_sync_factory),
|
| + profile_(profile),
|
| + sync_service_(sync_service),
|
| + state_(NOT_RUNNING),
|
| + abort_association_(false),
|
| + abort_association_complete_(false, false),
|
| + datatype_stopped_(false, false) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(profile_sync_factory);
|
| + DCHECK(profile);
|
| + DCHECK(sync_service);
|
| +}
|
| +
|
| +NonFrontendDataTypeController::~NonFrontendDataTypeController() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::Start(StartCallback* start_callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(start_callback);
|
| + if (state_ != NOT_RUNNING) {
|
| + start_callback->Run(BUSY, FROM_HERE);
|
| + delete start_callback;
|
| + return;
|
| + }
|
| +
|
| + start_callback_.reset(start_callback);
|
| + abort_association_ = false;
|
| +
|
| + 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;
|
| + }
|
| +
|
| + // Kick off association on the thread the datatype resides on.
|
| + state_ = ASSOCIATING;
|
| + if (!KickOffAssociation()) {
|
| + StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE);
|
| + }
|
| +}
|
| +
|
| +bool NonFrontendDataTypeController::StartModels() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK_EQ(state_, MODEL_STARTING);
|
| + // By default, no additional services need to be started before we can proceed
|
| + // with model association, so do nothing.
|
| + return true;
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::Associate() {
|
| + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK_EQ(state_, ASSOCIATING);
|
| + {
|
| + base::AutoLock lock(abort_association_lock_);
|
| + if (abort_association_) {
|
| + abort_association_complete_.Signal();
|
| + return;
|
| + }
|
| + CreateSyncComponents();
|
| + }
|
| +
|
| + if (!model_associator_->CryptoReadyIfNecessary()) {
|
| + StartFailed(NEEDS_CRYPTO, FROM_HERE);
|
| + return;
|
| + }
|
| +
|
| + bool sync_has_nodes = false;
|
| + if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
|
| + StartFailed(UNRECOVERABLE_ERROR, FROM_HERE);
|
| + return;
|
| + }
|
| +
|
| + base::TimeTicks start_time = base::TimeTicks::Now();
|
| + bool merge_success = model_associator_->AssociateModels();
|
| + RecordAssociationTime(base::TimeTicks::Now() - start_time);
|
| + if (!merge_success) {
|
| + StartFailed(ASSOCIATION_FAILED, FROM_HERE);
|
| + return;
|
| + }
|
| +
|
| + sync_service_->ActivateDataType(this, change_processor_.get());
|
| + StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING, FROM_HERE);
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::StartFailed(StartResult result,
|
| + const tracked_objects::Location& location) {
|
| + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + model_associator_.reset();
|
| + change_processor_.reset();
|
| + StartDone(result, NOT_RUNNING, location);
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::StartDone(
|
| + DataTypeController::StartResult result,
|
| + DataTypeController::State new_state,
|
| + const tracked_objects::Location& location) {
|
| + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + abort_association_complete_.Signal();
|
| + base::AutoLock lock(abort_association_lock_);
|
| + if (!abort_association_) {
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
| + NewRunnableMethod(
|
| + this,
|
| + &NonFrontendDataTypeController::StartDoneImpl,
|
| + result,
|
| + new_state,
|
| + location));
|
| + }
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::StartDoneImpl(
|
| + DataTypeController::StartResult result,
|
| + DataTypeController::State new_state,
|
| + const tracked_objects::Location& location) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + state_ = new_state;
|
| + if (state_ != RUNNING) {
|
| + // Start failed.
|
| + CleanUpState();
|
| + RecordStartFailure(result);
|
| + }
|
| + start_callback_->Run(result, location);
|
| + start_callback_.reset();
|
| +}
|
| +
|
| +// TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
|
| +// distinguishing chrome shutdown from sync shutdown, we should be able to avoid
|
| +// this (http://crbug.com/55662).
|
| +void NonFrontendDataTypeController::Stop() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + // If Stop() is called while Start() is waiting for association to
|
| + // complete, we need to abort the association and wait for the DB
|
| + // thread to finish the StartImpl() task.
|
| + if (state_ == ASSOCIATING) {
|
| + state_ = STOPPING;
|
| + {
|
| + base::AutoLock lock(abort_association_lock_);
|
| + abort_association_ = true;
|
| + if (model_associator_.get())
|
| + model_associator_->AbortAssociation();
|
| + }
|
| + // Wait for the model association to abort.
|
| + abort_association_complete_.Wait();
|
| + StartDoneImpl(ABORTED, STOPPING, FROM_HERE);
|
| + } else if (state_ == MODEL_STARTING) {
|
| + state_ = STOPPING;
|
| + // If Stop() is called while Start() is waiting for the models to start,
|
| + // abort the start. We don't need to continue on since it means we haven't
|
| + // kicked off the association, and once we call CleanUpState, we never will.
|
| + StartDoneImpl(ABORTED, NOT_RUNNING, FROM_HERE);
|
| + return;
|
| + } else {
|
| + state_ = STOPPING;
|
| +
|
| + // Clean up any state we changed while starting (for example models we
|
| + // started).
|
| + CleanUpState();
|
| + }
|
| + DCHECK(!start_callback_.get());
|
| +
|
| + // Deactivate the change processor on the UI thread. We dont want to listen
|
| + // for any more changes or process them from server.
|
| + if (change_processor_ != NULL)
|
| + sync_service_->DeactivateDataType(this, change_processor_.get());
|
| +
|
| + if (KickOffDestroy()) {
|
| + datatype_stopped_.Wait();
|
| + } else {
|
| + // We do DFATAL here because this will eventually lead to a failed CHECK
|
| + // when the change processor gets destroyed on the wrong thread.
|
| + LOG(DFATAL) << "Failed to destroy datatype " << name();
|
| + }
|
| + state_ = NOT_RUNNING;
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::CleanUpState() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(state_ == STOPPING || state_ == NOT_RUNNING);
|
| + // Do nothing by default.
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::Destroy() {
|
| + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + if (model_associator_ != NULL)
|
| + model_associator_->DisassociateModels();
|
| + change_processor_.reset();
|
| + model_associator_.reset();
|
| + datatype_stopped_.Signal();
|
| +}
|
| +
|
| +std::string NonFrontendDataTypeController::name() const {
|
| + // For logging only.
|
| + return syncable::ModelTypeToString(type());
|
| +}
|
| +
|
| +DataTypeController::State NonFrontendDataTypeController::state() const {
|
| + return state_;
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::OnUnrecoverableError(
|
| + const tracked_objects::Location& from_here,
|
| + const std::string& message) {
|
| + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + RecordUnrecoverableError(from_here, message);
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(this,
|
| + &NonFrontendDataTypeController::OnUnrecoverableErrorImpl, from_here,
|
| + message));
|
| +}
|
| +
|
| +void NonFrontendDataTypeController::OnUnrecoverableErrorImpl(
|
| + const tracked_objects::Location& from_here,
|
| + const std::string& message) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + sync_service_->OnUnrecoverableError(from_here, message);
|
| +}
|
| +
|
| +
|
| +} // namespace browser_sync
|
|
|