diff options
author | zea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-19 00:25:40 +0000 |
---|---|---|
committer | zea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-19 00:25:40 +0000 |
commit | 0e194cc6261463902915398c3d975d4de64852ff (patch) | |
tree | 67494192dd9f87a25334d5e5ab4eeaa00b1e1d4a /chrome | |
parent | 4d9a7a854d510f9261b5a918ea1f3ac8ea73c78d (diff) | |
download | chromium_src-0e194cc6261463902915398c3d975d4de64852ff.zip chromium_src-0e194cc6261463902915398c3d975d4de64852ff.tar.gz chromium_src-0e194cc6261463902915398c3d975d4de64852ff.tar.bz2 |
Revert 82028 - Reland r81454 (sync non-frontend datatype controllers) with longer timeouts.Original codereview: http://codereview.chromium.org/6811003BUG=77964TEST=unitReview URL: http://codereview.chromium.org/6869016
TBR=zea@chromium.org
Review URL: http://codereview.chromium.org/6882032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82036 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
31 files changed, 898 insertions, 1316 deletions
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index b34fff7..8ed41f2 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -1327,7 +1327,6 @@ void ProfileImpl::InitSyncService(const std::string& cros_user) { new ProfileSyncFactoryImpl(this, CommandLine::ForCurrentProcess())); sync_service_.reset( profile_sync_factory_->CreateProfileSyncService(cros_user)); - profile_sync_factory_->RegisterDataTypes(sync_service_.get()); sync_service_->Initialize(); } diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.cc b/chrome/browser/sync/glue/autofill_data_type_controller.cc index a4f0e66..fa5dde4 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller.cc @@ -4,118 +4,165 @@ #include "chrome/browser/sync/glue/autofill_data_type_controller.h" +#include "base/logging.h" #include "base/metrics/histogram.h" #include "base/task.h" +#include "base/time.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/glue/autofill_change_processor.h" +#include "chrome/browser/sync/glue/autofill_model_associator.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/webdata/web_data_service.h" #include "content/browser/browser_thread.h" #include "content/common/notification_service.h" -#include "content/common/notification_source.h" -#include "content/common/notification_type.h" namespace browser_sync { AutofillDataTypeController::AutofillDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile) - : NonFrontendDataTypeController(profile_sync_factory, - profile), - personal_data_(NULL) { + Profile* profile, + ProfileSyncService* sync_service) + : profile_sync_factory_(profile_sync_factory), + profile_(profile), + sync_service_(sync_service), + state_(NOT_RUNNING), + personal_data_(NULL), + 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); } AutofillDataTypeController::~AutofillDataTypeController() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TODO(zea): remove once crbug.com/61804 is resolved. + CHECK_EQ(state_, NOT_RUNNING) << "AutofillDataTypeController destroyed " + << "without being stopped."; + CHECK(!change_processor_.get()) << "AutofillDataTypeController destroyed " + << "while holding a change processor."; } -bool AutofillDataTypeController::StartModels() { +void AutofillDataTypeController::Start(StartCallback* start_callback) { + VLOG(1) << "Starting autofill data controller."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), MODEL_STARTING); + 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; + // Waiting for the personal data is subtle: we do this as the PDM resets // its cache of unique IDs once it gets loaded. If we were to proceed with // association, the local ids in the mappings would wind up colliding. - personal_data_ = profile()->GetPersonalDataManager(); + personal_data_ = profile_->GetPersonalDataManager(); if (!personal_data_->IsDataLoaded()) { + set_state(MODEL_STARTING); personal_data_->SetObserver(this); - return false; + return; } - web_data_service_ = profile()->GetWebDataService(Profile::IMPLICIT_ACCESS); + ContinueStartAfterPersonalDataLoaded(); +} + +void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() { + web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { - return true; + set_state(ASSOCIATING); + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartImpl)); } else { + set_state(MODEL_STARTING); notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, NotificationService::AllSources()); - return false; } } void AutofillDataTypeController::OnPersonalDataLoaded() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), MODEL_STARTING); + DCHECK_EQ(state_, MODEL_STARTING); personal_data_->RemoveObserver(this); - web_data_service_ = profile()->GetWebDataService(Profile::IMPLICIT_ACCESS); - if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { - set_state(ASSOCIATING); - if (!StartAssociationAsync()) { - StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); - } - } else { - notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, - NotificationService::AllSources()); - } + ContinueStartAfterPersonalDataLoaded(); } void AutofillDataTypeController::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), MODEL_STARTING); + VLOG(1) << "Web database loaded observed."; notification_registrar_.RemoveAll(); set_state(ASSOCIATING); - if (!StartAssociationAsync()) { - StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); - } + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartImpl)); } -bool AutofillDataTypeController::StartAssociationAsync() { +// 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). Further, all this functionality should be +// abstracted to a higher layer, where we could ensure all datatypes are doing +// the same thing (http://crbug.com/76232). +void AutofillDataTypeController::Stop() { + VLOG(1) << "Stopping autofill data type controller."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), ASSOCIATING); - return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - this, - &AutofillDataTypeController::StartAssociation)); -} -void AutofillDataTypeController::CreateSyncComponents() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - DCHECK_EQ(state(), ASSOCIATING); - ProfileSyncFactory::SyncComponents sync_components = - profile_sync_factory()-> - CreateAutofillSyncComponents( - profile_sync_service(), - web_data_service_->GetDatabase(), - personal_data_, - this); - set_model_associator(sync_components.model_associator); - set_change_processor(sync_components.change_processor); -} - -void AutofillDataTypeController::StopModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(state() == STOPPING || state() == NOT_RUNNING); + // 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) { + { + 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); + } + + // If Stop() is called while Start() is waiting for the personal + // data manager or web data service to load, abort the start. + if (state_ == MODEL_STARTING) + StartDoneImpl(ABORTED, STOPPING, FROM_HERE); + + 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. notification_registrar_.RemoveAll(); personal_data_->RemoveObserver(this); + if (change_processor_ != NULL && change_processor_->IsRunning()) + sync_service_->DeactivateDataType(this, change_processor_.get()); + + set_state(NOT_RUNNING); + if (BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StopImpl))) { + // We need to ensure the data type has fully stoppped before continuing. In + // particular, during shutdown we may attempt to destroy the + // profile_sync_service before we've removed its observers (BUG 61804). + datatype_stopped_.Wait(); + } else if (change_processor_.get()) { + // TODO(zea): remove once crbug.com/61804 is resolved. + LOG(FATAL) << "AutofillDataTypeController::Stop() called after DB thread" + << " killed."; + } + CHECK(!change_processor_.get()) << "AutofillChangeProcessor not released."; } -bool AutofillDataTypeController::StopAssociationAsync() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), STOPPING); - return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - this, - &AutofillDataTypeController::StopAssociation)); +bool AutofillDataTypeController::enabled() { + return true; } syncable::ModelType AutofillDataTypeController::type() const { @@ -127,31 +174,150 @@ browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() return browser_sync::GROUP_DB; } -void AutofillDataTypeController::RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) { +std::string AutofillDataTypeController::name() const { + // For logging only. + return "autofill"; +} + +DataTypeController::State AutofillDataTypeController::state() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return state_; +} + +ProfileSyncFactory::SyncComponents + AutofillDataTypeController::CreateSyncComponents( + ProfileSyncService* profile_sync_service, + WebDatabase* web_database, + PersonalDataManager* personal_data, + browser_sync::UnrecoverableErrorHandler* error_handler) { + return profile_sync_factory_->CreateAutofillSyncComponents( + profile_sync_service, + web_database, + personal_data, + this); +} + +void AutofillDataTypeController::StartImpl() { + VLOG(1) << "Autofill data type controller StartImpl called."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); + // No additional services need to be started before we can proceed + // with model association. + { + base::AutoLock lock(abort_association_lock_); + if (abort_association_) { + abort_association_complete_.Signal(); + return; + } + ProfileSyncFactory::SyncComponents sync_components = + CreateSyncComponents( + sync_service_, + web_data_service_->GetDatabase(), + profile_->GetPersonalDataManager(), + this); + model_associator_.reset(sync_components.model_associator); + change_processor_.reset(sync_components.change_processor); + } + + if (!model_associator_->CryptoReadyIfNecessary()) { + StartFailed(NEEDS_CRYPTO); + return; + } + + bool sync_has_nodes = false; + if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { + StartFailed(UNRECOVERABLE_ERROR); + return; + } + + base::TimeTicks start_time = base::TimeTicks::Now(); + bool merge_success = model_associator_->AssociateModels(); + UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", + base::TimeTicks::Now() - start_time); + VLOG(1) << "Autofill association time: " << + (base::TimeTicks::Now() - start_time).InSeconds(); + if (!merge_success) { + StartFailed(ASSOCIATION_FAILED); + return; + } + + sync_service_->ActivateDataType(this, change_processor_.get()); + StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); } -void AutofillDataTypeController::RecordAssociationTime(base::TimeDelta time) { +void AutofillDataTypeController::StartDone( + DataTypeController::StartResult result, + DataTypeController::State new_state) { + VLOG(1) << "Autofill data type controller StartDone called."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", time); + + abort_association_complete_.Signal(); + base::AutoLock lock(abort_association_lock_); + if (!abort_association_) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartDoneImpl, + result, + new_state, + FROM_HERE)); + } } -void AutofillDataTypeController::RecordStartFailure(StartResult result) { +void AutofillDataTypeController::StartDoneImpl( + DataTypeController::StartResult result, + DataTypeController::State new_state, + const tracked_objects::Location& location) { + VLOG(1) << "Autofill data type controller StartDoneImpl called."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", - result, - MAX_START_RESULT); + + set_state(new_state); + start_callback_->Run(result, location); + start_callback_.reset(); + + if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) { + UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", + result, + MAX_START_RESULT); + } } -PersonalDataManager* AutofillDataTypeController::personal_data() const { - return personal_data_; +void AutofillDataTypeController::StopImpl() { + VLOG(1) << "Autofill data type controller StopImpl called."; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + + if (model_associator_ != NULL) + model_associator_->DisassociateModels(); + + change_processor_.reset(); + model_associator_.reset(); + + datatype_stopped_.Signal(); } -WebDataService* AutofillDataTypeController::web_data_service() const { - return web_data_service_; +void AutofillDataTypeController::StartFailed(StartResult result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + change_processor_.reset(); + model_associator_.reset(); + StartDone(result, NOT_RUNNING); +} + +void AutofillDataTypeController::OnUnrecoverableError( + const tracked_objects::Location& from_here, + const std::string& message) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &AutofillDataTypeController::OnUnrecoverableErrorImpl, + from_here, message)); + UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); +} + +void AutofillDataTypeController::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 diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.h b/chrome/browser/sync/glue/autofill_data_type_controller.h index ee58113..8df5fb8 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.h +++ b/chrome/browser/sync/glue/autofill_data_type_controller.h @@ -8,31 +8,53 @@ #include <string> -#include "base/memory/ref_counted.h" +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/waitable_event.h" #include "chrome/browser/autofill/personal_data_manager.h" -#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" -#include "content/common/notification_observer.h" -#include "content/common/notification_registrar.h" +#include "chrome/browser/sync/profile_sync_factory.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/glue/data_type_controller.h" -class NotificationDetails; -class NotificationType; -class NotificationSource; +class Profile; +class ProfileSyncFactory; +class ProfileSyncService; namespace browser_sync { +class AssociatorInterface; +class ChangeProcessor; + // A class that manages the startup and shutdown of autofill sync. -class AutofillDataTypeController : public NonFrontendDataTypeController, +class AutofillDataTypeController : public DataTypeController, public NotificationObserver, public PersonalDataManager::Observer { public: - AutofillDataTypeController(ProfileSyncFactory* profile_sync_factory, - Profile* profile); + AutofillDataTypeController( + ProfileSyncFactory* profile_sync_factory, + Profile* profile, + ProfileSyncService* sync_service); virtual ~AutofillDataTypeController(); - // NonFrontendDataTypeController implementation + // DataTypeController implementation + virtual void Start(StartCallback* start_callback); + + virtual void Stop(); + + virtual bool enabled(); + virtual syncable::ModelType type() const; + virtual browser_sync::ModelSafeGroup model_safe_group() const; + virtual std::string name() const; + + virtual State state() const; + + // UnrecoverableHandler implementation + virtual void OnUnrecoverableError(const tracked_objects::Location& from_here, + const std::string& message); + // NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, @@ -42,26 +64,52 @@ class AutofillDataTypeController : public NonFrontendDataTypeController, virtual void OnPersonalDataLoaded(); protected: - // NonFrontendDataTypeController interface. - virtual bool StartModels(); - virtual bool StartAssociationAsync(); - virtual void CreateSyncComponents(); - virtual void StopModels(); - virtual bool StopAssociationAsync(); - virtual void RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message); - virtual void RecordAssociationTime(base::TimeDelta time); - virtual void RecordStartFailure(StartResult result); - - // Getters and setters - PersonalDataManager* personal_data() const; - WebDataService* web_data_service() const; + virtual ProfileSyncFactory::SyncComponents CreateSyncComponents( + ProfileSyncService* profile_sync_service, + WebDatabase* web_database, + PersonalDataManager* personal_data, + browser_sync::UnrecoverableErrorHandler* error_handler); + ProfileSyncFactory* profile_sync_factory_; + private: + void StartImpl(); + void StartDone(StartResult result, State state); + void StartDoneImpl(StartResult result, State state, + const tracked_objects::Location& location); + void StopImpl(); + void StartFailed(StartResult result); + void OnUnrecoverableErrorImpl(const tracked_objects::Location& from_here, + const std::string& message); + + // Second-half of "Start" implementation, called once personal data has + // loaded. + void ContinueStartAfterPersonalDataLoaded(); + + void set_state(State state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + state_ = state; + } + + Profile* profile_; + ProfileSyncService* sync_service_; + State state_; + PersonalDataManager* personal_data_; scoped_refptr<WebDataService> web_data_service_; + scoped_ptr<AssociatorInterface> model_associator_; + scoped_ptr<ChangeProcessor> change_processor_; + scoped_ptr<StartCallback> start_callback_; + NotificationRegistrar notification_registrar_; + base::Lock abort_association_lock_; + bool abort_association_; + base::WaitableEvent abort_association_complete_; + + // Barrier to ensure that the datatype has been stopped on the DB thread + // from the UI thread. + base::WaitableEvent datatype_stopped_; + DISALLOW_COPY_AND_ASSIGN(AutofillDataTypeController); }; diff --git a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc index a4e854e..01deb74 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc @@ -87,14 +87,13 @@ class AutofillDataTypeControllerTest : public testing::Test { db_thread_(BrowserThread::DB) {} virtual void SetUp() { - EXPECT_CALL(profile_, GetProfileSyncService()).WillRepeatedly( - Return(&service_)); db_thread_.Start(); web_data_service_ = new WebDataServiceFake(true); personal_data_manager_ = new PersonalDataManagerMock(); autofill_dtc_ = new AutofillDataTypeController(&profile_sync_factory_, - &profile_); + &profile_, + &service_); } virtual void TearDown() { diff --git a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc index 7ea3959..2203197 100644 --- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc @@ -4,17 +4,18 @@ #include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h" -#include "base/metrics/histogram.h" +#include "chrome/browser/sync/glue/autofill_data_type_controller.h" #include "chrome/browser/sync/profile_sync_factory.h" namespace browser_sync { AutofillProfileDataTypeController::AutofillProfileDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile) - : AutofillDataTypeController( - profile_sync_factory, - profile) {} + Profile* profile, + ProfileSyncService* sync_service) : AutofillDataTypeController( + profile_sync_factory, + profile, + sync_service) {} AutofillProfileDataTypeController::~AutofillProfileDataTypeController() {} @@ -22,38 +23,22 @@ syncable::ModelType AutofillProfileDataTypeController::type() const { return syncable::AUTOFILL_PROFILE; } -void AutofillProfileDataTypeController::CreateSyncComponents() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - ProfileSyncFactory::SyncComponents sync_components = - profile_sync_factory()-> - CreateAutofillProfileSyncComponents( - profile_sync_service(), - web_data_service()->GetDatabase(), - personal_data(), - this); - set_model_associator(sync_components.model_associator); - set_change_processor(sync_components.change_processor); +std::string AutofillProfileDataTypeController::name() const { + // For logging only. + return "autofill_profile"; } -void AutofillProfileDataTypeController::RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - UMA_HISTOGRAM_COUNTS("Sync.AutofillProfileRunFailures", 1); +ProfileSyncFactory::SyncComponents + AutofillProfileDataTypeController::CreateSyncComponents( + ProfileSyncService* profile_sync_service, + WebDatabase* web_database, + PersonalDataManager* personal_data, + browser_sync::UnrecoverableErrorHandler* error_handler) { + return profile_sync_factory_->CreateAutofillProfileSyncComponents( + profile_sync_service, + web_database, + personal_data, + this); } - -void AutofillProfileDataTypeController::RecordAssociationTime( - base::TimeDelta time) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - UMA_HISTOGRAM_TIMES("Sync.AutofillProfileAssociationTime", time); -} - -void AutofillProfileDataTypeController::RecordStartFailure(StartResult result) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_ENUMERATION("Sync.AutofillProfileStartFailures", - result, - MAX_START_RESULT); -} - } // namepsace browser_sync diff --git a/chrome/browser/sync/glue/autofill_profile_data_type_controller.h b/chrome/browser/sync/glue/autofill_profile_data_type_controller.h index 64f0f49..d97a904 100644 --- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.h +++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.h @@ -15,18 +15,20 @@ class AutofillProfileDataTypeController : public AutofillDataTypeController { public: AutofillProfileDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile); + Profile* profile, + ProfileSyncService* sync_service); virtual ~AutofillProfileDataTypeController(); virtual syncable::ModelType type() const; + virtual std::string name() const; + protected: - virtual void CreateSyncComponents(); - virtual void RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message); - virtual void RecordAssociationTime(base::TimeDelta time); - virtual void RecordStartFailure(StartResult result); + virtual ProfileSyncFactory::SyncComponents CreateSyncComponents( + ProfileSyncService* profile_sync_service, + WebDatabase* web_database, + PersonalDataManager* personal_data, + browser_sync::UnrecoverableErrorHandler* error_handler); }; } // namespace browser_sync diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller.cc b/chrome/browser/sync/glue/bookmark_data_type_controller.cc index 4415240..8cd3d5b 100644 --- a/chrome/browser/sync/glue/bookmark_data_type_controller.cc +++ b/chrome/browser/sync/glue/bookmark_data_type_controller.cc @@ -43,7 +43,7 @@ bool BookmarkDataTypeController::StartModels() { } // Cleanup for our extra registrar usage. -void BookmarkDataTypeController::CleanUpState() { +void BookmarkDataTypeController::CleanupState() { registrar_.RemoveAll(); } diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller.h b/chrome/browser/sync/glue/bookmark_data_type_controller.h index 2655056..8741e84 100644 --- a/chrome/browser/sync/glue/bookmark_data_type_controller.h +++ b/chrome/browser/sync/glue/bookmark_data_type_controller.h @@ -39,7 +39,7 @@ class BookmarkDataTypeController : public FrontendDataTypeController, private: // FrontendDataTypeController interface. virtual bool StartModels(); - virtual void CleanUpState(); + virtual void CleanupState(); virtual void CreateSyncComponents(); virtual void RecordUnrecoverableError( const tracked_objects::Location& from_here, diff --git a/chrome/browser/sync/glue/frontend_data_type_controller.cc b/chrome/browser/sync/glue/frontend_data_type_controller.cc index edec12c..5e324f9 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller.cc +++ b/chrome/browser/sync/glue/frontend_data_type_controller.cc @@ -103,46 +103,15 @@ bool FrontendDataTypeController::Associate() { return true; } -void FrontendDataTypeController::StartFailed(StartResult result, - const tracked_objects::Location& location) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - CleanUpState(); - model_associator_.reset(); - change_processor_.reset(); - 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_. - scoped_ptr<StartCallback> callback(start_callback_.release()); - callback->Run(result, location); -} - -void FrontendDataTypeController::FinishStart(StartResult result, - const tracked_objects::Location& location) { - 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 - // confused by the non-NULL start_callback_. - scoped_ptr<StartCallback> callback(start_callback_.release()); - callback->Run(result, location); -} - void FrontendDataTypeController::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, FROM_HERE); - // We can just return here since we haven't performed association if we're - // still in MODEL_STARTING. - return; - } + if (state_ == MODEL_STARTING) + FinishStart(ABORTED, FROM_HERE); DCHECK(!start_callback_.get()); - CleanUpState(); + CleanupState(); if (change_processor_ != NULL) sync_service_->DeactivateDataType(this, change_processor_.get()); @@ -156,7 +125,7 @@ void FrontendDataTypeController::Stop() { state_ = NOT_RUNNING; } -void FrontendDataTypeController::CleanUpState() { +void FrontendDataTypeController::CleanupState() { // Do nothing by default. } @@ -181,4 +150,23 @@ void FrontendDataTypeController::OnUnrecoverableError( sync_service_->OnUnrecoverableError(from_here, message); } +void FrontendDataTypeController::FinishStart(StartResult result, + const tracked_objects::Location& location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + start_callback_->Run(result, location); + start_callback_.reset(); +} + +void FrontendDataTypeController::StartFailed(StartResult result, + const tracked_objects::Location& location) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + CleanupState(); + model_associator_.reset(); + change_processor_.reset(); + state_ = NOT_RUNNING; + start_callback_->Run(result, location); + start_callback_.reset(); + RecordStartFailure(result); +} + } // namespace browser_sync diff --git a/chrome/browser/sync/glue/frontend_data_type_controller.h b/chrome/browser/sync/glue/frontend_data_type_controller.h index 6a2d863..d228bb9 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller.h +++ b/chrome/browser/sync/glue/frontend_data_type_controller.h @@ -22,8 +22,6 @@ namespace browser_sync { class AssociatorInterface; class ChangeProcessor; -// TODO(zea): have naming and style match NonFrontendDataTypeController. -// TODO(zea): Rename frontend to UI (http://crbug.com/78833). // Implementation for datatypes that reside on the frontend thread // (UI thread). This is the same thread we perform initialization on, so we // don't have to worry about thread safety. The main start/stop funtionality is @@ -46,10 +44,15 @@ class FrontendDataTypeController : public DataTypeController { // DataTypeController interface. virtual void Start(StartCallback* start_callback); + virtual void Stop(); + virtual syncable::ModelType type() const = 0; + virtual browser_sync::ModelSafeGroup model_safe_group() const; + virtual std::string name() const; + virtual State state() const; // UnrecoverableErrorHandler interface. @@ -61,33 +64,25 @@ class FrontendDataTypeController : public DataTypeController { // Kick off any dependent services that need to be running before we can // associate models. The default implementation is a no-op. - // Return value: - // True - if models are ready and association can proceed. - // False - if models are not ready. Associate() should be called when the - // models are ready. Refer to Start(_) implementation. virtual bool StartModels(); // Build sync components and associate models. - // Return value: - // True - if association was successful. FinishStart should have been - // invoked. - // False - if association failed. StartFailed should have been invoked. virtual bool Associate(); - // Datatype specific creation of sync components. - virtual void CreateSyncComponents() = 0; - // Perform any DataType controller specific state cleanup before stopping // the datatype controller. The default implementation is a no-op. - virtual void CleanUpState(); + virtual void CleanupState(); + + // Helper method to run the stashed start callback with a given result. + virtual void FinishStart(StartResult result, + const tracked_objects::Location& from_here); // Cleans up state and calls callback when start fails. virtual void StartFailed(StartResult result, const tracked_objects::Location& from_here); - // Helper method to run the stashed start callback with a given result. - virtual void FinishStart(StartResult result, - const tracked_objects::Location& from_here); + // Datatype specific creation of sync components. + virtual void CreateSyncComponents() = 0; // DataType specific histogram methods. Because histograms use static's, the // specific datatype controllers must implement this themselves. diff --git a/chrome/browser/sync/glue/frontend_data_type_controller_mock.h b/chrome/browser/sync/glue/frontend_data_type_controller_mock.h index a32f3f5..0485b1f 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller_mock.h +++ b/chrome/browser/sync/glue/frontend_data_type_controller_mock.h @@ -30,12 +30,12 @@ class FrontendDataTypeControllerMock : public FrontendDataTypeController { // FrontendDataTypeController mocks. MOCK_METHOD0(StartModels, bool()); MOCK_METHOD0(Associate, bool()); - MOCK_METHOD0(CreateSyncComponents, void()); - MOCK_METHOD2(StartFailed, void(StartResult result, - const tracked_objects::Location& from_here)); + MOCK_METHOD0(CleanupState, void()); MOCK_METHOD2(FinishStart, void(StartResult result, const tracked_objects::Location& from_here)); - MOCK_METHOD0(CleanUpState, void()); + MOCK_METHOD2(StartFailed, void(StartResult result, + const tracked_objects::Location& from_here)); + MOCK_METHOD0(CreateSyncComponents, void()); MOCK_METHOD2(RecordUnrecoverableError, void(const tracked_objects::Location&, const std::string&)); MOCK_METHOD1(RecordAssociationTime, void(base::TimeDelta time)); diff --git a/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc b/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc index ee3f42f..c0c2290 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc +++ b/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc @@ -63,8 +63,8 @@ class FrontendDataTypeControllerFake : public FrontendDataTypeController { virtual bool StartModels() { return mock_->StartModels(); } - virtual void CleanUpState() { - mock_->CleanUpState(); + virtual void CleanupState() { + mock_->CleanupState(); } virtual void RecordUnrecoverableError( const tracked_objects::Location& from_here, @@ -122,13 +122,13 @@ class FrontendDataTypeControllerTest : public testing::Test { } void SetStopExpectations() { - EXPECT_CALL(*dtc_mock_, CleanUpState()); + EXPECT_CALL(*dtc_mock_, CleanupState()); EXPECT_CALL(service_, DeactivateDataType(_, _)); EXPECT_CALL(*model_associator_, DisassociateModels()); } void SetStartFailExpectations(DataTypeController::StartResult result) { - EXPECT_CALL(*dtc_mock_, CleanUpState()); + EXPECT_CALL(*dtc_mock_, CleanupState()); EXPECT_CALL(*dtc_mock_, RecordStartFailure(result)); EXPECT_CALL(start_callback_, Run(result,_)); } @@ -169,16 +169,6 @@ TEST_F(FrontendDataTypeControllerTest, StartFirstRun) { EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state()); } -TEST_F(FrontendDataTypeControllerTest, AbortDuringStartModels) { - EXPECT_CALL(*dtc_mock_, StartModels()).WillOnce(Return(false)); - SetStartFailExpectations(DataTypeController::ABORTED); - EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state()); - frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - EXPECT_EQ(DataTypeController::MODEL_STARTING, frontend_dtc_->state()); - frontend_dtc_->Stop(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state()); -} - TEST_F(FrontendDataTypeControllerTest, StartAssociationFailed) { SetStartExpectations(); EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). @@ -225,6 +215,7 @@ TEST_F(FrontendDataTypeControllerTest, Stop) { SetAssociateExpectations(); SetActivateExpectations(DataTypeController::OK); SetStopExpectations(); + EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state()); frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state()); @@ -241,6 +232,7 @@ TEST_F(FrontendDataTypeControllerTest, OnUnrecoverableError) { WillOnce(InvokeWithoutArgs(frontend_dtc_.get(), &FrontendDataTypeController::Stop)); SetStopExpectations(); + EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state()); frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state()); diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc deleted file mode 100644 index d26baef..0000000 --- a/chrome/browser/sync/glue/non_frontend_data_type_controller.cc +++ /dev/null @@ -1,279 +0,0 @@ -// 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), - profile_sync_service_(NULL), - abort_association_(false), - abort_association_complete_(false, false), - datatype_stopped_(false, false) {} - -NonFrontendDataTypeController::NonFrontendDataTypeController( - ProfileSyncFactory* profile_sync_factory, - Profile* profile) - : profile_sync_factory_(profile_sync_factory), - profile_(profile), - profile_sync_service_(profile->GetProfileSyncService()), - 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(profile_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 (!StartAssociationAsync()) { - 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::StartAssociation() { - 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; - } - - profile_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. - StopModels(); - 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_. - scoped_ptr<StartCallback> callback(start_callback_.release()); - callback->Run(result, location); -} - -// 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 StopModels, we never will. - StartDoneImpl(ABORTED, NOT_RUNNING, FROM_HERE); - return; - } else { - state_ = STOPPING; - - StopModels(); - } - 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) - profile_sync_service_->DeactivateDataType(this, change_processor_.get()); - - if (StopAssociationAsync()) { - 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::StopModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(state_ == STOPPING || state_ == NOT_RUNNING); - // Do nothing by default. -} - -void NonFrontendDataTypeController::StopAssociation() { - 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)); - profile_sync_service_->OnUnrecoverableError(from_here, message); -} - -ProfileSyncFactory* NonFrontendDataTypeController::profile_sync_factory() - const { - return profile_sync_factory_; -} - -Profile* NonFrontendDataTypeController::profile() const { - return profile_; -} - -ProfileSyncService* NonFrontendDataTypeController::profile_sync_service() - const { - return profile_sync_service_; -} - -void NonFrontendDataTypeController::set_state(State state) { - state_ = state; -} - -void NonFrontendDataTypeController::set_model_associator( - AssociatorInterface* associator) { - model_associator_.reset(associator); -} - -void NonFrontendDataTypeController::set_change_processor( - ChangeProcessor* change_processor) { - change_processor_.reset(change_processor); -} - -} // namespace browser_sync diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller.h b/chrome/browser/sync/glue/non_frontend_data_type_controller.h deleted file mode 100644 index c2f2ee0..0000000 --- a/chrome/browser/sync/glue/non_frontend_data_type_controller.h +++ /dev/null @@ -1,165 +0,0 @@ -// 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. - -#ifndef CHROME_BROWSER_SYNC_GLUE_NON_FRONTEND_DATA_TYPE_CONTROLLER_H__ -#define CHROME_BROWSER_SYNC_GLUE_NON_FRONTEND_DATA_TYPE_CONTROLLER_H__ -#pragma once - -#include <string> - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/waitable_event.h" -#include "chrome/browser/sync/glue/data_type_controller.h" - -class Profile; -class ProfileSyncService; -class ProfileSyncFactory; - -namespace base { class TimeDelta; } -namespace browser_sync { - -class AssociatorInterface; -class ChangeProcessor; - -// TODO(zea): Rename frontend to UI (http://crbug.com/78833). -// Implementation for datatypes that do not reside on the frontend thread -// (UI thread). This is the same thread we perform initialization -// on, so we don't have to worry about thread safety. The main start/stop -// funtionality is implemented by default. Derived classes must implement: -// type() -// model_safe_group() -// StartAssociationAsync() -// CreateSyncComponents() -// StopAssociationAsync() -// RecordUnrecoverableError(...) -// RecordAssociationTime(...); -// RecordStartFailure(...); -class NonFrontendDataTypeController : public DataTypeController { - public: - NonFrontendDataTypeController( - ProfileSyncFactory* profile_sync_factory, - Profile* profile); - virtual ~NonFrontendDataTypeController(); - - // DataTypeController interface. - virtual void Start(StartCallback* start_callback); - virtual void Stop(); - virtual syncable::ModelType type() const = 0; - virtual browser_sync::ModelSafeGroup model_safe_group() const = 0; - virtual std::string name() const; - virtual State state() const; - - // UnrecoverableErrorHandler interface. - // Note: this is performed on the datatype's thread. - virtual void OnUnrecoverableError(const tracked_objects::Location& from_here, - const std::string& message); - protected: - // For testing only. - NonFrontendDataTypeController(); - - // Start any dependent services that need to be running before we can - // associate models. The default implementation is a no-op. - // Return value: - // True - if models are ready and association can proceed. - // False - if models are not ready. KickOffAssociation should be called - // when the models are ready. Refer to Start(_) implementation. - // Note: this is performed on the frontend (UI) thread. - virtual bool StartModels(); - - // Post the association task to the thread the datatype lives on. - // Note: this is performed on the frontend (UI) thread. - // Return value: True if task posted successfully, False otherwise. - virtual bool StartAssociationAsync() = 0; - - // Build sync components and associate models. - // Note: this is performed on the datatype's thread. - virtual void StartAssociation(); - - // Datatype specific creation of sync components. - // Note: this is performed on the datatype's thread. - virtual void CreateSyncComponents() = 0; - - // Start failed, make sure we record it, clean up state, and invoke the - // callback on the frontend thread. - // Note: this is performed on the datatype's thread. - virtual void StartFailed(StartResult result, - const tracked_objects::Location& location); - - // Start up complete, update the state and invoke the callback. - // Note: this is performed on the datatype's thread. - virtual void StartDone(DataTypeController::StartResult result, - DataTypeController::State new_state, - const tracked_objects::Location& location); - - // UI thread implementation of StartDone. - virtual void StartDoneImpl(DataTypeController::StartResult result, - DataTypeController::State new_state, - const tracked_objects::Location& location); - - // Perform any DataType controller specific state cleanup before stopping - // the datatype controller. The default implementation is a no-op. - // Note: this is performed on the frontend (UI) thread. - virtual void StopModels(); - - // Post the StopAssociation task to the thread the datatype lives on. - // Note: this is performed on the frontend (UI) thread. - // Return value: True if task posted successfully, False otherwise. - virtual bool StopAssociationAsync() = 0; - - // Disassociate the models and destroy the sync components. - // Note: this is performed on the datatype's thread. - virtual void StopAssociation(); - - // Implementation of OnUnrecoverableError that lives on UI thread. - virtual void OnUnrecoverableErrorImpl( - const tracked_objects::Location& from_here, - const std::string& message); - - // DataType specific histogram methods. Because histograms use static's, the - // specific datatype controllers must implement this themselves. - // Important: calling them on other threads can lead to memory corruption! - // Record unrecoverable errors. Called on Datatype's thread. - virtual void RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) = 0; - // Record association time. Called on Datatype's thread. - virtual void RecordAssociationTime(base::TimeDelta time) = 0; - // Record causes of start failure. Called on UI thread. - virtual void RecordStartFailure(StartResult result) = 0; - - // Accessors and mutators used by derived classes. - ProfileSyncFactory* profile_sync_factory() const; - Profile* profile() const; - ProfileSyncService* profile_sync_service() const; - void set_state(State state); - void set_model_associator(AssociatorInterface* associator); - void set_change_processor(ChangeProcessor* change_processor); - - private: - ProfileSyncFactory* const profile_sync_factory_; - Profile* const profile_; - ProfileSyncService* const profile_sync_service_; - - State state_; - - scoped_ptr<StartCallback> start_callback_; - scoped_ptr<AssociatorInterface> model_associator_; - scoped_ptr<ChangeProcessor> change_processor_; - - // Locks/Barriers for aborting association early. - base::Lock abort_association_lock_; - bool abort_association_; - base::WaitableEvent abort_association_complete_; - - // Barrier to ensure that the datatype has been stopped on the DB thread - // from the UI thread. - base::WaitableEvent datatype_stopped_; - - DISALLOW_COPY_AND_ASSIGN(NonFrontendDataTypeController); -}; - -} // namespace browser_sync - -#endif // CHROME_BROWSER_SYNC_GLUE_NON_FRONTEND_DATA_TYPE_CONTROLLER_H__ diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.cc deleted file mode 100644 index dfde595..0000000 --- a/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.cc +++ /dev/null @@ -1,13 +0,0 @@ -// 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_mock.h" - -namespace browser_sync { - -NonFrontendDataTypeControllerMock::NonFrontendDataTypeControllerMock() {} - -NonFrontendDataTypeControllerMock::~NonFrontendDataTypeControllerMock() {} - -} // namespace browser_sync diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h b/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h deleted file mode 100644 index 8e4316f..0000000 --- a/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -#ifndef CHROME_BROWSER_SYNC_GLUE_NON_FRONTEND_DATA_TYPE_CONTROLLER_MOCK_H__ -#define CHROME_BROWSER_SYNC_GLUE_NON_FRONTEND_DATA_TYPE_CONTROLLER_MOCK_H__ -#pragma once - -#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace browser_sync { - -class NonFrontendDataTypeControllerMock : public NonFrontendDataTypeController { - public: - NonFrontendDataTypeControllerMock(); - virtual ~NonFrontendDataTypeControllerMock(); - - // DataTypeController mocks. - MOCK_METHOD1(Start, void(StartCallback* start_callback)); - MOCK_METHOD0(Stop, void()); - MOCK_METHOD0(enabled, bool()); - MOCK_CONST_METHOD0(type, syncable::ModelType()); - MOCK_CONST_METHOD0(name, std::string()); - MOCK_CONST_METHOD0(model_safe_group, browser_sync::ModelSafeGroup()); - MOCK_CONST_METHOD0(state, State()); - MOCK_METHOD2(OnUnrecoverableError, void(const tracked_objects::Location&, - const std::string&)); - - // NonFrontendDataTypeController mocks. - MOCK_METHOD0(StartModels, bool()); - MOCK_METHOD0(StartAssociationAsync, bool()); - MOCK_METHOD0(StartAssociation, void()); - MOCK_METHOD0(CreateSyncComponents, void()); - MOCK_METHOD2(StartFailed, void(StartResult result, - const tracked_objects::Location& from_here)); - MOCK_METHOD3(StartDone, void(DataTypeController::StartResult result, - DataTypeController::State new_state, - const tracked_objects::Location& location)); - MOCK_METHOD3(StartDoneImpl, void(DataTypeController::StartResult result, - DataTypeController::State new_state, - const tracked_objects::Location& location)); - MOCK_METHOD0(StopModels, void()); - MOCK_METHOD0(StopAssociationAsync, bool()); - MOCK_METHOD0(StopAssociation, void()); - MOCK_METHOD2(OnUnrecoverableErrorImpl, void(const tracked_objects::Location&, - const std::string&)); - MOCK_METHOD2(RecordUnrecoverableError, void(const tracked_objects::Location&, - const std::string&)); - MOCK_METHOD1(RecordAssociationTime, void(base::TimeDelta time)); - MOCK_METHOD1(RecordStartFailure, void(StartResult result)); -}; - -} // namespace browser_sync - -#endif // CHROME_BROWSER_SYNC_GLUE_NON_FRONTEND_DATA_TYPE_CONTROLLER_MOCK_H__ diff --git a/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc b/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc deleted file mode 100644 index 602e582..0000000 --- a/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc +++ /dev/null @@ -1,397 +0,0 @@ -// 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 "testing/gtest/include/gtest/gtest.h" - -#include "base/callback.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/task.h" -#include "base/test/test_timeouts.h" -#include "base/tracked_objects.h" -#include "base/synchronization/waitable_event.h" -#include "chrome/browser/sync/engine/model_safe_worker.h" -#include "chrome/browser/sync/glue/change_processor_mock.h" -#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" -#include "chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h" -#include "chrome/browser/sync/glue/model_associator_mock.h" -#include "chrome/browser/sync/profile_sync_factory_mock.h" -#include "chrome/browser/sync/profile_sync_service_mock.h" -#include "chrome/test/profile_mock.h" -#include "content/browser/browser_thread.h" - -using base::WaitableEvent; -using browser_sync::ChangeProcessorMock; -using browser_sync::DataTypeController; -using browser_sync::GROUP_DB; -using browser_sync::NonFrontendDataTypeController; -using browser_sync::NonFrontendDataTypeControllerMock; -using browser_sync::ModelAssociatorMock; -using testing::_; -using testing::DoAll; -using testing::InvokeWithoutArgs; -using testing::Return; -using testing::SetArgumentPointee; -using testing::StrictMock; - -class StartCallback { - public: - MOCK_METHOD2(Run, void(DataTypeController::StartResult result, - const tracked_objects::Location& from_here)); -}; - -ACTION_P(WaitOnEvent, event) { - event->Wait(); -} - -ACTION_P(SignalEvent, event) { - event->Signal(); -} - -class NonFrontendDataTypeControllerFake : public NonFrontendDataTypeController { - public: - NonFrontendDataTypeControllerFake( - ProfileSyncFactory* profile_sync_factory, - Profile* profile, - NonFrontendDataTypeControllerMock* mock) - : NonFrontendDataTypeController(profile_sync_factory, - profile), - mock_(mock) {} - - virtual syncable::ModelType type() const { return syncable::BOOKMARKS; } - virtual browser_sync::ModelSafeGroup model_safe_group() const { - return GROUP_DB; - } - - private: - virtual void CreateSyncComponents() { - ProfileSyncFactory::SyncComponents sync_components = - profile_sync_factory()-> - CreateBookmarkSyncComponents(profile_sync_service(), this); - set_model_associator(sync_components.model_associator); - set_change_processor(sync_components.change_processor); - } - virtual bool StartAssociationAsync() { - mock_->StartAssociationAsync(); - return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - this, - &NonFrontendDataTypeControllerFake::StartAssociation)); - } - virtual bool StopAssociationAsync() { - mock_->StopAssociationAsync(); - return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - this, - &NonFrontendDataTypeControllerFake::StopAssociation)); - } - - // We mock the following methods because their default implementations do - // nothing, but we still want to make sure they're called appropriately. - virtual bool StartModels() { - return mock_->StartModels(); - } - virtual void StopModels() { - mock_->StopModels(); - } - virtual void RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) { - mock_->RecordUnrecoverableError(from_here, message); - } - virtual void RecordAssociationTime(base::TimeDelta time) { - mock_->RecordAssociationTime(time); - } - virtual void RecordStartFailure(DataTypeController::StartResult result) { - mock_->RecordStartFailure(result); - } - private: - NonFrontendDataTypeControllerMock* mock_; -}; - -class NonFrontendDataTypeControllerTest : public testing::Test { - public: - NonFrontendDataTypeControllerTest() - : ui_thread_(BrowserThread::UI, &message_loop_), - db_thread_(BrowserThread::DB), - model_associator_(NULL), - change_processor_(NULL), - signaled_(false) {} - - virtual void SetUp() { - EXPECT_CALL(profile_, GetProfileSyncService()).WillRepeatedly( - Return(&service_)); - db_thread_.Start(); - profile_sync_factory_.reset(new ProfileSyncFactoryMock()); - - // Both of these are refcounted, so don't need to be released. - dtc_mock_ = new StrictMock<NonFrontendDataTypeControllerMock>(); - non_frontend_dtc_ = - new NonFrontendDataTypeControllerFake(profile_sync_factory_.get(), - &profile_, - dtc_mock_.get()); - } - - virtual void TearDown() { - db_thread_.Stop(); - } - - protected: - void SetStartExpectations() { - EXPECT_CALL(*dtc_mock_, StartModels()).WillOnce(Return(true)); - model_associator_ = new ModelAssociatorMock(); - change_processor_ = new ChangeProcessorMock(); - EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)). - WillOnce(Return(ProfileSyncFactory::SyncComponents(model_associator_, - change_processor_))); - } - - void SetAssociateExpectations() { - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillOnce(Return(true)); - EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). - WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); - EXPECT_CALL(*model_associator_, AssociateModels()). - WillOnce(Return(true)); - EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_)); - } - - void SetActivateExpectations(DataTypeController::StartResult result) { - EXPECT_CALL(service_, ActivateDataType(_, _)); - EXPECT_CALL(start_callback_, Run(result,_)); - } - - void SetStopExpectations() { - EXPECT_CALL(*dtc_mock_, StopAssociationAsync()); - EXPECT_CALL(*dtc_mock_, StopModels()); - EXPECT_CALL(service_, DeactivateDataType(_, _)); - EXPECT_CALL(*model_associator_, DisassociateModels()); - } - - void SetStartFailExpectations(DataTypeController::StartResult result) { - EXPECT_CALL(*dtc_mock_, StopModels()); - EXPECT_CALL(*dtc_mock_, RecordStartFailure(result)); - EXPECT_CALL(start_callback_, Run(result,_)); - } - - static void SignalDone(WaitableEvent* done, bool* signaled) { - done->Signal(); - *signaled = true; - } - - void WaitForDTC() { - WaitableEvent done(false, false); - signaled_ = false; - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableFunction(&NonFrontendDataTypeControllerTest::SignalDone, - &done, &signaled_)); - done.TimedWait(base::TimeDelta::FromMilliseconds( - TestTimeouts::action_max_timeout_ms())); - if (!signaled_) { - ADD_FAILURE() << "Timed out waiting for DB thread to finish."; - } - signaled_ = false; - MessageLoop::current()->RunAllPending(); - } - - MessageLoopForUI message_loop_; - BrowserThread ui_thread_; - BrowserThread db_thread_; - scoped_refptr<NonFrontendDataTypeControllerFake> non_frontend_dtc_; - scoped_ptr<ProfileSyncFactoryMock> profile_sync_factory_; - scoped_refptr<NonFrontendDataTypeControllerMock> dtc_mock_; - ProfileMock profile_; - ProfileSyncServiceMock service_; - ModelAssociatorMock* model_associator_; - ChangeProcessorMock* change_processor_; - StartCallback start_callback_; - bool signaled_; -}; - -TEST_F(NonFrontendDataTypeControllerTest, StartOk) { - SetStartExpectations(); - SetAssociateExpectations(); - SetActivateExpectations(DataTypeController::OK); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, StartFirstRun) { - SetStartExpectations(); - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillOnce(Return(true)); - EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). - WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true))); - EXPECT_CALL(*model_associator_, AssociateModels()). - WillOnce(Return(true)); - EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_)); - SetActivateExpectations(DataTypeController::OK_FIRST_RUN); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, AbortDuringStartModels) { - EXPECT_CALL(*dtc_mock_, StartModels()).WillOnce(Return(false)); - SetStartFailExpectations(DataTypeController::ABORTED); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::MODEL_STARTING, non_frontend_dtc_->state()); - non_frontend_dtc_->Stop(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, StartAssociationFailed) { - SetStartExpectations(); - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillOnce(Return(true)); - EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). - WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true))); - EXPECT_CALL(*model_associator_, AssociateModels()). - WillOnce(Return(false)); - EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_)); - SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED); - // Set up association to fail with an association failed error. - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, - StartAssociationTriggersUnrecoverableError) { - SetStartExpectations(); - SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR); - // Set up association to fail with an unrecoverable error. - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillRepeatedly(Return(true)); - EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). - WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false))); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, StartAssociationCryptoNotReady) { - SetStartExpectations(); - SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO); - // Set up association to fail with a NEEDS_CRYPTO error. - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillRepeatedly(Return(false)); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -// Trigger a Stop() call when we check if the model associator has user created -// nodes. -TEST_F(NonFrontendDataTypeControllerTest, AbortDuringAssociationInactive) { - WaitableEvent wait_for_db_thread_pause(false, false); - WaitableEvent pause_db_thread(false, false); - - SetStartExpectations(); - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillOnce(Return(true)); - EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). - WillOnce(DoAll( - SignalEvent(&wait_for_db_thread_pause), - WaitOnEvent(&pause_db_thread), - SetArgumentPointee<0>(true), - Return(true))); - EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce( - SignalEvent(&pause_db_thread)); - EXPECT_CALL(*model_associator_, AssociateModels()).WillOnce(Return(true)); - EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_)); - EXPECT_CALL(service_, ActivateDataType(_, _)); - EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_)); - EXPECT_CALL(*dtc_mock_, RecordStartFailure(DataTypeController::ABORTED)); - SetStopExpectations(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - wait_for_db_thread_pause.Wait(); - non_frontend_dtc_->Stop(); - WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -// Same as above but abort during the Activate call. -TEST_F(NonFrontendDataTypeControllerTest, AbortDuringAssociationActivated) { - WaitableEvent wait_for_db_thread_pause(false, false); - WaitableEvent pause_db_thread(false, false); - - SetStartExpectations(); - EXPECT_CALL(*dtc_mock_, StartAssociationAsync()); - EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). - WillOnce(Return(true)); - EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). - WillOnce(DoAll( - SetArgumentPointee<0>(true), - Return(true))); - EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce( - SignalEvent(&pause_db_thread)); - EXPECT_CALL(*model_associator_, AssociateModels()). - WillOnce(Return(true)); - EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_)); - EXPECT_CALL(service_, ActivateDataType(_, _)).WillOnce(DoAll( - SignalEvent(&wait_for_db_thread_pause), - WaitOnEvent(&pause_db_thread))); - EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_)); - EXPECT_CALL(*dtc_mock_, RecordStartFailure(DataTypeController::ABORTED)); - SetStopExpectations(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - wait_for_db_thread_pause.Wait(); - non_frontend_dtc_->Stop(); - WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, Stop) { - SetStartExpectations(); - SetAssociateExpectations(); - SetActivateExpectations(DataTypeController::OK); - SetStopExpectations(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Stop(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} - -TEST_F(NonFrontendDataTypeControllerTest, OnUnrecoverableError) { - SetStartExpectations(); - SetAssociateExpectations(); - SetActivateExpectations(DataTypeController::OK); - EXPECT_CALL(*dtc_mock_, RecordUnrecoverableError(_, "Test")); - EXPECT_CALL(service_, OnUnrecoverableError(_,_)).WillOnce( - InvokeWithoutArgs(non_frontend_dtc_.get(), - &NonFrontendDataTypeController::Stop)); - SetStopExpectations(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); - non_frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run)); - WaitForDTC(); - EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state()); - // This should cause non_frontend_dtc_->Stop() to be called. - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - non_frontend_dtc_.get(), - &NonFrontendDataTypeControllerFake::OnUnrecoverableError, - FROM_HERE, - std::string("Test"))); - WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state()); -} diff --git a/chrome/browser/sync/glue/password_data_type_controller.cc b/chrome/browser/sync/glue/password_data_type_controller.cc index 69b1d89..2589861 100644 --- a/chrome/browser/sync/glue/password_data_type_controller.cc +++ b/chrome/browser/sync/glue/password_data_type_controller.cc @@ -5,66 +5,105 @@ #include "chrome/browser/sync/glue/password_data_type_controller.h" #include "base/metrics/histogram.h" +#include "base/logging.h" #include "base/task.h" +#include "base/time.h" #include "chrome/browser/password_manager/password_store.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/sync/profile_sync_factory.h" +#include "chrome/browser/sync/glue/password_change_processor.h" +#include "chrome/browser/sync/glue/password_model_associator.h" #include "chrome/browser/sync/profile_sync_service.h" -#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/browser/sync/profile_sync_factory.h" #include "content/browser/browser_thread.h" namespace browser_sync { PasswordDataTypeController::PasswordDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile) - : NonFrontendDataTypeController(profile_sync_factory, - profile) { + 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(profile_sync_factory); + DCHECK(profile); + DCHECK(sync_service); } PasswordDataTypeController::~PasswordDataTypeController() { } -bool PasswordDataTypeController::StartModels() { +void PasswordDataTypeController::Start(StartCallback* start_callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), MODEL_STARTING); - password_store_ = profile()->GetPasswordStore(Profile::EXPLICIT_ACCESS); + DCHECK(start_callback); + if (state_ != NOT_RUNNING) { + start_callback->Run(BUSY, FROM_HERE); + delete start_callback; + return; + } + + password_store_ = profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); if (!password_store_.get()) { LOG(ERROR) << "PasswordStore not initialized, password datatype controller" << " aborting."; - StartDoneImpl(ABORTED, NOT_RUNNING, FROM_HERE); - return false; + state_ = NOT_RUNNING; + start_callback->Run(ABORTED, FROM_HERE); + delete start_callback; + return; } - return true; -} -bool PasswordDataTypeController::StartAssociationAsync() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), ASSOCIATING); - DCHECK(password_store_.get()); + start_callback_.reset(start_callback); + abort_association_ = false; + set_state(ASSOCIATING); password_store_->ScheduleTask( - NewRunnableMethod(this, &PasswordDataTypeController::StartAssociation)); - return true; + NewRunnableMethod(this, &PasswordDataTypeController::StartImpl)); } -void PasswordDataTypeController::CreateSyncComponents() { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), ASSOCIATING); - ProfileSyncFactory::SyncComponents sync_components = - profile_sync_factory()->CreatePasswordSyncComponents( - profile_sync_service(), - password_store_.get(), - this); - set_model_associator(sync_components.model_associator); - set_change_processor(sync_components.change_processor); -} - -bool PasswordDataTypeController::StopAssociationAsync() { +// 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). Further, all this functionality should be +// abstracted to a higher layer, where we could ensure all datatypes are doing +// the same thing (http://crbug.com/76232). +void PasswordDataTypeController::Stop() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), STOPPING); + + // If Stop() is called while Start() is waiting for association to + // complete, we need to abort the association and wait for the PASSWORD + // thread to finish the StartImpl() task. + if (state_ == ASSOCIATING) { + { + 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); + } + + // If Stop() is called while Start() is waiting for another service to load, + // abort the start. + if (state_ == MODEL_STARTING) + StartDoneImpl(ABORTED, STOPPING); + + DCHECK(!start_callback_.get()); + + if (change_processor_ != NULL) + sync_service_->DeactivateDataType(this, change_processor_.get()); + + set_state(NOT_RUNNING); DCHECK(password_store_.get()); password_store_->ScheduleTask( - NewRunnableMethod(this, &PasswordDataTypeController::StopAssociation)); + NewRunnableMethod(this, &PasswordDataTypeController::StopImpl)); + datatype_stopped_.Wait(); +} + +bool PasswordDataTypeController::enabled() { return true; } @@ -77,23 +116,111 @@ browser_sync::ModelSafeGroup PasswordDataTypeController::model_safe_group() return browser_sync::GROUP_PASSWORD; } -void PasswordDataTypeController::RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_COUNTS("Sync.PasswordRunFailures", 1); +std::string PasswordDataTypeController::name() const { + // For logging only. + return "password"; } -void PasswordDataTypeController::RecordAssociationTime(base::TimeDelta time) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_TIMES("Sync.PasswordAssociationTime", time); +DataTypeController::State PasswordDataTypeController::state() const { + return state_; } -void PasswordDataTypeController::RecordStartFailure(StartResult result) { +void PasswordDataTypeController::StartImpl() { + // No additional services need to be started before we can proceed + // with model association. + { + base::AutoLock lock(abort_association_lock_); + if (abort_association_) { + abort_association_complete_.Signal(); + return; + } + ProfileSyncFactory::SyncComponents sync_components = + profile_sync_factory_->CreatePasswordSyncComponents( + sync_service_, + password_store_.get(), + this); + model_associator_.reset(sync_components.model_associator); + change_processor_.reset(sync_components.change_processor); + } + + if (!model_associator_->CryptoReadyIfNecessary()) { + StartFailed(NEEDS_CRYPTO); + return; + } + + bool sync_has_nodes = false; + if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { + StartFailed(UNRECOVERABLE_ERROR); + return; + } + + base::TimeTicks start_time = base::TimeTicks::Now(); + bool merge_success = model_associator_->AssociateModels(); + UMA_HISTOGRAM_TIMES("Sync.PasswordAssociationTime", + base::TimeTicks::Now() - start_time); + if (!merge_success) { + StartFailed(ASSOCIATION_FAILED); + return; + } + + sync_service_->ActivateDataType(this, change_processor_.get()); + StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); +} + +void PasswordDataTypeController::StartDone( + DataTypeController::StartResult result, + DataTypeController::State new_state) { + abort_association_complete_.Signal(); + base::AutoLock lock(abort_association_lock_); + if (!abort_association_) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod( + this, + &PasswordDataTypeController::StartDoneImpl, + result, + new_state)); + } +} + +void PasswordDataTypeController::StartDoneImpl( + DataTypeController::StartResult result, + DataTypeController::State new_state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + set_state(new_state); + start_callback_->Run(result, FROM_HERE); + start_callback_.reset(); +} + +void PasswordDataTypeController::StopImpl() { + if (model_associator_ != NULL) + model_associator_->DisassociateModels(); + + change_processor_.reset(); + model_associator_.reset(); + + datatype_stopped_.Signal(); +} + +void PasswordDataTypeController::StartFailed(StartResult result) { + change_processor_.reset(); + model_associator_.reset(); + StartDone(result, NOT_RUNNING); +} + +void PasswordDataTypeController::OnUnrecoverableError( + const tracked_objects::Location& from_here, const std::string& message) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &PasswordDataTypeController::OnUnrecoverableErrorImpl, + from_here, message)); +} + +void PasswordDataTypeController::OnUnrecoverableErrorImpl( + const tracked_objects::Location& from_here, + const std::string& message) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_ENUMERATION("Sync.PasswordStartFailures", - result, - MAX_START_RESULT); + sync_service_->OnUnrecoverableError(from_here, message); } } // namespace browser_sync diff --git a/chrome/browser/sync/glue/password_data_type_controller.h b/chrome/browser/sync/glue/password_data_type_controller.h index f2e14f8..1a3a9e41 100644 --- a/chrome/browser/sync/glue/password_data_type_controller.h +++ b/chrome/browser/sync/glue/password_data_type_controller.h @@ -8,40 +8,83 @@ #include <string> +#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" +#include "base/synchronization/waitable_event.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/glue/data_type_controller.h" class PasswordStore; +class Profile; +class ProfileSyncFactory; +class ProfileSyncService; namespace browser_sync { +class AssociatorInterface; +class ChangeProcessor; +class ControlTask; + // A class that manages the startup and shutdown of password sync. -class PasswordDataTypeController : public NonFrontendDataTypeController { +class PasswordDataTypeController : public DataTypeController { public: PasswordDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile); + Profile* profile, + ProfileSyncService* sync_service); virtual ~PasswordDataTypeController(); - // NonFrontendDataTypeController implementation + // DataTypeController implementation + virtual void Start(StartCallback* start_callback); + + virtual void Stop(); + + virtual bool enabled(); + virtual syncable::ModelType type() const; + virtual browser_sync::ModelSafeGroup model_safe_group() const; - protected: - // NonFrontendDataTypeController interface. - virtual bool StartModels(); - virtual bool StartAssociationAsync(); - virtual void CreateSyncComponents(); - virtual bool StopAssociationAsync(); - virtual void RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message); - virtual void RecordAssociationTime(base::TimeDelta time); - virtual void RecordStartFailure(StartResult result); + virtual std::string name() const; + + virtual State state() const; + + // UnrecoverableHandler implementation + virtual void OnUnrecoverableError(const tracked_objects::Location& from_here, + const std::string& message); private: + void StartImpl(); + void StartDone(StartResult result, State state); + void StartDoneImpl(StartResult result, State state); + void StopImpl(); + void StartFailed(StartResult result); + void OnUnrecoverableErrorImpl(const tracked_objects::Location& from_here, + const std::string& message); + + void set_state(State state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + state_ = state; + } + + ProfileSyncFactory* profile_sync_factory_; + Profile* profile_; + ProfileSyncService* sync_service_; + State state_; + + scoped_ptr<AssociatorInterface> model_associator_; + scoped_ptr<ChangeProcessor> change_processor_; + scoped_ptr<StartCallback> start_callback_; scoped_refptr<PasswordStore> password_store_; + base::Lock abort_association_lock_; + bool abort_association_; + base::WaitableEvent abort_association_complete_; + + // Barrier to ensure that the datatype has been stopped on the DB thread + // from the UI thread. + base::WaitableEvent datatype_stopped_; + DISALLOW_COPY_AND_ASSIGN(PasswordDataTypeController); }; diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.cc b/chrome/browser/sync/glue/typed_url_data_type_controller.cc index 73776fe..93be84d 100644 --- a/chrome/browser/sync/glue/typed_url_data_type_controller.cc +++ b/chrome/browser/sync/glue/typed_url_data_type_controller.cc @@ -4,10 +4,14 @@ #include "chrome/browser/sync/glue/typed_url_data_type_controller.h" +#include "base/logging.h" #include "base/metrics/histogram.h" #include "base/task.h" +#include "base/time.h" #include "chrome/browser/history/history.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/glue/typed_url_change_processor.h" +#include "chrome/browser/sync/glue/typed_url_model_associator.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "content/browser/browser_thread.h" @@ -22,7 +26,11 @@ class ControlTask : public HistoryDBTask { virtual bool RunOnDBThread(history::HistoryBackend* backend, history::HistoryDatabase* db) { - controller_->RunOnHistoryThread(start_, backend); + if (start_) { + controller_->StartImpl(backend); + } else { + controller_->StopImpl(); + } // Release the reference to the controller. This ensures that // the controller isn't held past its lifetime in unit tests. @@ -39,90 +47,104 @@ class ControlTask : public HistoryDBTask { TypedUrlDataTypeController::TypedUrlDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile) - : NonFrontendDataTypeController(profile_sync_factory, - profile), - backend_(NULL) { + 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); } TypedUrlDataTypeController::~TypedUrlDataTypeController() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } -void TypedUrlDataTypeController::RunOnHistoryThread(bool start, - history::HistoryBackend* backend) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - // The only variable we can access here is backend_, since it is always - // read from the DB thread. Touching anything else could lead to memory - // corruption. - backend_ = backend; - if (start) { - StartAssociation(); - } else { - StopAssociation(); +void TypedUrlDataTypeController::Start(StartCallback* start_callback) { + VLOG(1) << "Starting typed_url data controller."; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(start_callback); + if (state_ != NOT_RUNNING || start_callback_.get()) { + start_callback->Run(BUSY, FROM_HERE); + delete start_callback; + return; } - backend_ = NULL; -} -bool TypedUrlDataTypeController::StartModels() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), MODEL_STARTING); - HistoryService* history = profile()->GetHistoryServiceWithoutCreating(); + start_callback_.reset(start_callback); + abort_association_ = false; + + HistoryService* history = profile_->GetHistoryServiceWithoutCreating(); if (history) { + set_state(ASSOCIATING); history_service_ = history; - return true; + history_service_->ScheduleDBTask(new ControlTask(this, true), this); } else { + set_state(MODEL_STARTING); notification_registrar_.Add(this, NotificationType::HISTORY_LOADED, NotificationService::AllSources()); - return false; } } -bool TypedUrlDataTypeController::StartAssociationAsync() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), ASSOCIATING); - DCHECK(history_service_.get()); - history_service_->ScheduleDBTask(new ControlTask(this, true), this); - return true; -} - -void TypedUrlDataTypeController::CreateSyncComponents() { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), ASSOCIATING); - DCHECK(backend_); - ProfileSyncFactory::SyncComponents sync_components = - profile_sync_factory()->CreateTypedUrlSyncComponents( - profile_sync_service(), - backend_, - this); - set_model_associator(sync_components.model_associator); - set_change_processor(sync_components.change_processor); -} - void TypedUrlDataTypeController::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), MODEL_STARTING); + VLOG(1) << "History loaded observed."; notification_registrar_.Remove(this, NotificationType::HISTORY_LOADED, NotificationService::AllSources()); - history_service_ = profile()->GetHistoryServiceWithoutCreating(); + + history_service_ = profile_->GetHistoryServiceWithoutCreating(); DCHECK(history_service_.get()); - set_state(ASSOCIATING); - StopAssociationAsync(); + history_service_->ScheduleDBTask(new ControlTask(this, true), this); } -void TypedUrlDataTypeController::StopModels() { +// 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). Further, all this functionality should be +// abstracted to a higher layer, where we could ensure all datatypes are doing +// the same thing (http://crbug.com/76232). +void TypedUrlDataTypeController::Stop() { + VLOG(1) << "Stopping typed_url data type controller."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(state() == STOPPING || state() == NOT_RUNNING); - notification_registrar_.RemoveAll(); -} -bool TypedUrlDataTypeController::StopAssociationAsync() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK_EQ(state(), STOPPING); + // 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) { + { + 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); + } + + // If Stop() is called while Start() is waiting for the history service to + // load, abort the start. + if (state_ == MODEL_STARTING) + StartDoneImpl(ABORTED, STOPPING); + + DCHECK(!start_callback_.get()); + + if (change_processor_ != NULL) + sync_service_->DeactivateDataType(this, change_processor_.get()); + + set_state(NOT_RUNNING); DCHECK(history_service_.get()); history_service_->ScheduleDBTask(new ControlTask(this, false), this); + datatype_stopped_.Wait(); +} + +bool TypedUrlDataTypeController::enabled() { return true; } @@ -135,22 +157,125 @@ browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() return browser_sync::GROUP_HISTORY; } -void TypedUrlDataTypeController::RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1); +std::string TypedUrlDataTypeController::name() const { + // For logging only. + return "typed_url"; } -void TypedUrlDataTypeController::RecordAssociationTime(base::TimeDelta time) { - DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime", time); +DataTypeController::State TypedUrlDataTypeController::state() const { + return state_; } -void TypedUrlDataTypeController::RecordStartFailure(StartResult result) { +void TypedUrlDataTypeController::StartImpl(history::HistoryBackend* backend) { + VLOG(1) << "TypedUrl data type controller StartImpl called."; + // No additional services need to be started before we can proceed + // with model association. + { + base::AutoLock lock(abort_association_lock_); + if (abort_association_) { + abort_association_complete_.Signal(); + return; + } + ProfileSyncFactory::SyncComponents sync_components = + profile_sync_factory_->CreateTypedUrlSyncComponents( + sync_service_, + backend, + this); + model_associator_.reset(sync_components.model_associator); + change_processor_.reset(sync_components.change_processor); + } + + if (!model_associator_->CryptoReadyIfNecessary()) { + StartFailed(NEEDS_CRYPTO); + return; + } + + bool sync_has_nodes = false; + if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { + StartFailed(UNRECOVERABLE_ERROR); + return; + } + + base::TimeTicks start_time = base::TimeTicks::Now(); + bool merge_success = model_associator_->AssociateModels(); + UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime", + base::TimeTicks::Now() - start_time); + if (!merge_success) { + StartFailed(ASSOCIATION_FAILED); + return; + } + + sync_service_->ActivateDataType(this, change_processor_.get()); + StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); +} + +void TypedUrlDataTypeController::StartDone( + DataTypeController::StartResult result, + DataTypeController::State new_state) { + VLOG(1) << "TypedUrl data type controller StartDone called."; + + abort_association_complete_.Signal(); + base::AutoLock lock(abort_association_lock_); + if (!abort_association_) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod( + this, + &TypedUrlDataTypeController::StartDoneImpl, + result, + new_state)); + } +} + +void TypedUrlDataTypeController::StartDoneImpl( + DataTypeController::StartResult result, + DataTypeController::State new_state) { + VLOG(1) << "TypedUrl data type controller StartDoneImpl called."; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures", - result, - MAX_START_RESULT); + set_state(new_state); + start_callback_->Run(result, FROM_HERE); + start_callback_.reset(); + + if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) { + UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures", + result, + MAX_START_RESULT); + } } + +void TypedUrlDataTypeController::StopImpl() { + VLOG(1) << "TypedUrl data type controller StopImpl called."; + + if (model_associator_ != NULL) + model_associator_->DisassociateModels(); + + change_processor_.reset(); + model_associator_.reset(); + + datatype_stopped_.Signal(); +} + +void TypedUrlDataTypeController::StartFailed(StartResult result) { + change_processor_.reset(); + model_associator_.reset(); + StartDone(result, NOT_RUNNING); +} + +void TypedUrlDataTypeController::OnUnrecoverableError( + const tracked_objects::Location& from_here, + const std::string& message) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &TypedUrlDataTypeController::OnUnrecoverableErrorImpl, + from_here, message)); +} + +void TypedUrlDataTypeController::OnUnrecoverableErrorImpl( + const tracked_objects::Location& from_here, + const std::string& message) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1); + sync_service_->OnUnrecoverableError(from_here, message); +} + } // namespace browser_sync diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.h b/chrome/browser/sync/glue/typed_url_data_type_controller.h index 2db5520..386a01d 100644 --- a/chrome/browser/sync/glue/typed_url_data_type_controller.h +++ b/chrome/browser/sync/glue/typed_url_data_type_controller.h @@ -8,8 +8,11 @@ #include <string> +#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" +#include "base/synchronization/waitable_event.h" +#include "chrome/browser/sync/glue/data_type_controller.h" +#include "chrome/browser/sync/profile_sync_service.h" #include "content/browser/cancelable_request.h" #include "content/common/notification_observer.h" #include "content/common/notification_registrar.h" @@ -18,6 +21,9 @@ class NotificationSource; class NotificationDetails; class HistoryService; +class Profile; +class ProfileSyncFactory; +class ProfileSyncService; namespace history { class HistoryBackend; @@ -25,22 +31,40 @@ class HistoryBackend; namespace browser_sync { +class AssociatorInterface; +class ChangeProcessor; class ControlTask; // A class that manages the startup and shutdown of typed_url sync. -class TypedUrlDataTypeController : public NonFrontendDataTypeController, +class TypedUrlDataTypeController : public DataTypeController, public NotificationObserver, public CancelableRequestConsumerBase { public: TypedUrlDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile); + Profile* profile, + ProfileSyncService* sync_service); virtual ~TypedUrlDataTypeController(); - // NonFrontendDataTypeController implementation + // DataTypeController implementation + virtual void Start(StartCallback* start_callback); + + virtual void Stop(); + + virtual bool enabled(); + virtual syncable::ModelType type() const; + virtual browser_sync::ModelSafeGroup model_safe_group() const; + virtual std::string name() const; + + virtual State state() const; + + // UnrecoverableHandler implementation + virtual void OnUnrecoverableError(const tracked_objects::Location& from_here, + const std::string& message); + // NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, @@ -49,36 +73,51 @@ class TypedUrlDataTypeController : public NonFrontendDataTypeController, // CancelableRequestConsumerBase implementation. virtual void OnRequestAdded(CancelableRequestProvider* provider, CancelableRequestProvider::Handle handle) {} + virtual void OnRequestRemoved(CancelableRequestProvider* provider, CancelableRequestProvider::Handle handle) {} + virtual void WillExecute(CancelableRequestProvider* provider, CancelableRequestProvider::Handle handle) {} + virtual void DidExecute(CancelableRequestProvider* provider, CancelableRequestProvider::Handle handle) {} - protected: - // NonFrontendDataTypeController interface. - virtual bool StartModels(); - virtual bool StartAssociationAsync(); - virtual void CreateSyncComponents(); - virtual void StopModels(); - virtual bool StopAssociationAsync(); - virtual void RecordUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message); - virtual void RecordAssociationTime(base::TimeDelta time); - virtual void RecordStartFailure(StartResult result); - private: friend class ControlTask; - - // Helper method to launch Associate() and Destroy() on the history thread. - void RunOnHistoryThread(bool start, history::HistoryBackend* backend); - - history::HistoryBackend* backend_; + void StartImpl(history::HistoryBackend* backend); + void StartDone(StartResult result, State state); + void StartDoneImpl(StartResult result, State state); + void StopImpl(); + void StartFailed(StartResult result); + void OnUnrecoverableErrorImpl(const tracked_objects::Location& from_here, + const std::string& message); + + void set_state(State state) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + state_ = state; + } + + ProfileSyncFactory* profile_sync_factory_; + Profile* profile_; + ProfileSyncService* sync_service_; + State state_; + + scoped_ptr<AssociatorInterface> model_associator_; + scoped_ptr<ChangeProcessor> change_processor_; + scoped_ptr<StartCallback> start_callback_; scoped_refptr<HistoryService> history_service_; + NotificationRegistrar notification_registrar_; + base::Lock abort_association_lock_; + bool abort_association_; + base::WaitableEvent abort_association_complete_; + + // Barrier to ensure that the datatype has been stopped on the DB thread + // from the UI thread. + base::WaitableEvent datatype_stopped_; + DISALLOW_COPY_AND_ASSIGN(TypedUrlDataTypeController); }; diff --git a/chrome/browser/sync/profile_sync_factory.h b/chrome/browser/sync/profile_sync_factory.h index 8befe35..776b61d 100644 --- a/chrome/browser/sync/profile_sync_factory.h +++ b/chrome/browser/sync/profile_sync_factory.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -47,15 +47,12 @@ class ProfileSyncFactory { virtual ~ProfileSyncFactory() {} - // Instantiates a new ProfileSyncService. The return pointer is owned by the - // caller. + // Instantiates and initializes a new ProfileSyncService. Enabled + // data types are registered with the service. The return pointer + // is owned by the caller. virtual ProfileSyncService* CreateProfileSyncService( const std::string& cros_user) = 0; - // Creates and registers enabled datatypes with the provided - // ProfileSyncService. - virtual void RegisterDataTypes(ProfileSyncService* pss) = 0; - // Instantiates a new DataTypeManager with a SyncBackendHost and a // list of data type controllers. The return pointer is owned by // the caller. diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc index 19f4938..9102e88 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_factory_impl.cc @@ -86,10 +86,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( ProfileSyncService* pss = new ProfileSyncService( this, profile_, cros_user); - return pss; -} -void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) { // App sync is enabled by default. Register unless explicitly // disabled. if (!command_line_->HasSwitch(switches::kDisableSyncApps)) { @@ -101,7 +98,7 @@ void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) { // disabled. if (!command_line_->HasSwitch(switches::kDisableSyncAutofill)) { pss->RegisterDataTypeController( - new AutofillDataTypeController(this, profile_)); + new AutofillDataTypeController(this, profile_, pss)); } // Bookmark sync is enabled by default. Register unless explicitly @@ -122,7 +119,7 @@ void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) { // disabled. if (!command_line_->HasSwitch(switches::kDisableSyncPasswords)) { pss->RegisterDataTypeController( - new PasswordDataTypeController(this, profile_)); + new PasswordDataTypeController(this, profile_, pss)); } // Preference sync is enabled by default. Register unless explicitly @@ -142,7 +139,7 @@ void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) { // explicitly enabled. if (command_line_->HasSwitch(switches::kEnableSyncTypedUrls)) { pss->RegisterDataTypeController( - new TypedUrlDataTypeController(this, profile_)); + new TypedUrlDataTypeController(this, profile_, pss)); } // Session sync is disabled by default. Register only if explicitly @@ -153,9 +150,10 @@ void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) { } if (!command_line_->HasSwitch(switches::kDisableSyncAutofillProfile)) { - pss->RegisterDataTypeController( - new AutofillProfileDataTypeController(this, profile_)); + pss->RegisterDataTypeController(new AutofillProfileDataTypeController( + this, profile_, pss)); } + return pss; } DataTypeManager* ProfileSyncFactoryImpl::CreateDataTypeManager( diff --git a/chrome/browser/sync/profile_sync_factory_impl.h b/chrome/browser/sync/profile_sync_factory_impl.h index 2015a40..eb93f1c 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.h +++ b/chrome/browser/sync/profile_sync_factory_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -23,8 +23,6 @@ class ProfileSyncFactoryImpl : public ProfileSyncFactory { virtual ProfileSyncService* CreateProfileSyncService( const std::string& cros_user); - virtual void RegisterDataTypes(ProfileSyncService* pss); - virtual browser_sync::DataTypeManager* CreateDataTypeManager( browser_sync::SyncBackendHost* backend, const browser_sync::DataTypeController::TypeMap& controllers); diff --git a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc index 56643ac..907b2f6 100644 --- a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc +++ b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc @@ -76,7 +76,6 @@ class ProfileSyncFactoryImplTest : public testing::Test { command_line_->AppendSwitch(cmd_switch); scoped_ptr<ProfileSyncService> pss( profile_sync_service_factory_->CreateProfileSyncService("")); - profile_sync_service_factory_->RegisterDataTypes(pss.get()); DataTypeController::StateMap controller_states; pss->GetDataTypeControllerStates(&controller_states); EXPECT_EQ(DefaultDatatypesCount() - 1, controller_states.size()); @@ -93,7 +92,6 @@ class ProfileSyncFactoryImplTest : public testing::Test { TEST_F(ProfileSyncFactoryImplTest, CreatePSSDefault) { scoped_ptr<ProfileSyncService> pss( profile_sync_service_factory_->CreateProfileSyncService("")); - profile_sync_service_factory_->RegisterDataTypes(pss.get()); DataTypeController::StateMap controller_states; pss->GetDataTypeControllerStates(&controller_states); EXPECT_EQ(DefaultDatatypesCount(), controller_states.size()); diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h index 3fa856c..2d8d8e2 100644 --- a/chrome/browser/sync/profile_sync_factory_mock.h +++ b/chrome/browser/sync/profile_sync_factory_mock.h @@ -26,7 +26,6 @@ class ProfileSyncFactoryMock : public ProfileSyncFactory { MOCK_METHOD1(CreateProfileSyncService, ProfileSyncService*(const std::string&)); - MOCK_METHOD1(RegisterDataTypes, void(ProfileSyncService*)); MOCK_METHOD2(CreateDataTypeManager, browser_sync::DataTypeManager*( browser_sync::SyncBackendHost*, diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc index 763d6bb..a7b0e36 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -207,7 +207,8 @@ class AutofillEntryFactory : public AbstractAutofillFactory { ProfileMock* profile, ProfileSyncService* service) { return new AutofillDataTypeController(factory, - profile); + profile, + service); } void SetExpectation(ProfileSyncFactoryMock* factory, @@ -227,7 +228,8 @@ class AutofillProfileFactory : public AbstractAutofillFactory { ProfileMock* profile, ProfileSyncService* service) { return new AutofillProfileDataTypeController(factory, - profile); + profile, + service); } void SetExpectation(ProfileSyncFactoryMock* factory, @@ -300,8 +302,6 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { service_.reset( new TestProfileSyncService(&factory_, &profile_, "test_user", false, task)); - EXPECT_CALL(profile_, GetProfileSyncService()).WillRepeatedly( - Return(service_.get())); AutofillDataTypeController* data_type_controller = factory->CreateDataTypeController(&factory_, &profile_, @@ -670,6 +670,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) { } TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) { + std::vector<AutofillProfile*> profiles; std::vector<AutofillProfile> expected_profiles; // Owned by GetAutofillProfiles caller. diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc index 94f2169..22005ce 100644 --- a/chrome/browser/sync/profile_sync_service_password_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc @@ -189,11 +189,10 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest { &factory_, &profile_, "test_user", false, root_task, node_task)); service_->RegisterPreferences(); profile_.GetPrefs()->SetBoolean(prefs::kSyncPasswords, true); - EXPECT_CALL(profile_, GetProfileSyncService()).WillRepeatedly( - Return(service_.get())); PasswordDataTypeController* data_type_controller = new PasswordDataTypeController(&factory_, - &profile_); + &profile_, + service_.get()); EXPECT_CALL(factory_, CreatePasswordSyncComponents(_, _, _)). Times(AtLeast(1)). // Can be more if we hit NEEDS_CRYPTO. diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc index 6184b6a..7125cce 100644 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc @@ -163,11 +163,10 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest { service_.reset( new TestProfileSyncService(&factory_, &profile_, "test", false, task)); - EXPECT_CALL(profile_, GetProfileSyncService()).WillRepeatedly( - Return(service_.get())); TypedUrlDataTypeController* data_type_controller = new TypedUrlDataTypeController(&factory_, - &profile_); + &profile_, + service_.get()); EXPECT_CALL(factory_, CreateTypedUrlSyncComponents(_, _, _)). WillOnce(MakeTypedUrlSyncComponents(service_.get(), diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 54618b2..04057ac 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1932,8 +1932,6 @@ 'browser/sync/glue/http_bridge.cc', 'browser/sync/glue/http_bridge.h', 'browser/sync/glue/model_associator.h', - 'browser/sync/glue/non_frontend_data_type_controller.cc', - 'browser/sync/glue/non_frontend_data_type_controller.h', 'browser/sync/glue/password_change_processor.cc', 'browser/sync/glue/password_change_processor.h', 'browser/sync/glue/password_data_type_controller.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 5851c3a..6646ccb 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1551,9 +1551,6 @@ 'browser/sync/glue/http_bridge_unittest.cc', 'browser/sync/glue/model_associator_mock.cc', 'browser/sync/glue/model_associator_mock.h', - 'browser/sync/glue/non_frontend_data_type_controller_mock.cc', - 'browser/sync/glue/non_frontend_data_type_controller_mock.h', - 'browser/sync/glue/non_frontend_data_type_controller_unittest.cc', 'browser/sync/glue/preference_data_type_controller_unittest.cc', 'browser/sync/glue/preference_model_associator_unittest.cc', 'browser/sync/glue/session_model_associator_unittest.cc', |