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 |