diff options
author | zea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-18 23:28:43 +0000 |
---|---|---|
committer | zea@chromium.org <zea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-18 23:28:43 +0000 |
commit | 4e70191dc2dd52cf2f85617e916592d638203c92 (patch) | |
tree | 2a87dfe339cf153c43f072902baac9432de55585 /chrome/browser | |
parent | 1be4c6fa13ecc5e68a4f576728e518ba2255bb9a (diff) | |
download | chromium_src-4e70191dc2dd52cf2f85617e916592d638203c92.zip chromium_src-4e70191dc2dd52cf2f85617e916592d638203c92.tar.gz chromium_src-4e70191dc2dd52cf2f85617e916592d638203c92.tar.bz2 |
Reland r81454 (sync non-frontend datatype controllers) with longer timeouts.
Original codereview: http://codereview.chromium.org/6811003
BUG=77964
TEST=unit
Review URL: http://codereview.chromium.org/6869016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82028 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
29 files changed, 1311 insertions, 898 deletions
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 8ed41f2..b34fff7 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -1327,6 +1327,7 @@ 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 fa5dde4..a4f0e66 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller.cc @@ -4,165 +4,118 @@ #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, - 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); + Profile* profile) + : NonFrontendDataTypeController(profile_sync_factory, + profile), + personal_data_(NULL) { } 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."; } -void AutofillDataTypeController::Start(StartCallback* start_callback) { - VLOG(1) << "Starting autofill data controller."; +bool AutofillDataTypeController::StartModels() { 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; - + DCHECK_EQ(state(), MODEL_STARTING); // 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; + return false; } - ContinueStartAfterPersonalDataLoaded(); -} - -void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() { - web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); + web_data_service_ = profile()->GetWebDataService(Profile::IMPLICIT_ACCESS); if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { - set_state(ASSOCIATING); - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - this, - &AutofillDataTypeController::StartImpl)); + return true; } else { - set_state(MODEL_STARTING); notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, NotificationService::AllSources()); + return false; } } void AutofillDataTypeController::OnPersonalDataLoaded() { - DCHECK_EQ(state_, MODEL_STARTING); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), MODEL_STARTING); personal_data_->RemoveObserver(this); - ContinueStartAfterPersonalDataLoaded(); + 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()); + } } void AutofillDataTypeController::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - VLOG(1) << "Web database loaded observed."; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), MODEL_STARTING); notification_registrar_.RemoveAll(); set_state(ASSOCIATING); - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, - NewRunnableMethod( - this, - &AutofillDataTypeController::StartImpl)); + if (!StartAssociationAsync()) { + StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); + } } -// 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."; +bool AutofillDataTypeController::StartAssociationAsync() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), ASSOCIATING); + return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartAssociation)); +} - // 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. +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); 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::enabled() { - return true; +bool AutofillDataTypeController::StopAssociationAsync() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), STOPPING); + return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StopAssociation)); } syncable::ModelType AutofillDataTypeController::type() const { @@ -174,150 +127,31 @@ browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() return browser_sync::GROUP_DB; } -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."; +void AutofillDataTypeController::RecordUnrecoverableError( + const tracked_objects::Location& from_here, + const std::string& message) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - // 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); + UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); } -void AutofillDataTypeController::StartDone( - DataTypeController::StartResult result, - DataTypeController::State new_state) { - VLOG(1) << "Autofill data type controller StartDone called."; +void AutofillDataTypeController::RecordAssociationTime(base::TimeDelta time) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); - - 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)); - } + UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", time); } -void AutofillDataTypeController::StartDoneImpl( - DataTypeController::StartResult result, - DataTypeController::State new_state, - const tracked_objects::Location& location) { - VLOG(1) << "Autofill data type controller StartDoneImpl called."; +void AutofillDataTypeController::RecordStartFailure(StartResult result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - 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); - } + UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", + result, + MAX_START_RESULT); } -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(); +PersonalDataManager* AutofillDataTypeController::personal_data() const { + return personal_data_; } -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); +WebDataService* AutofillDataTypeController::web_data_service() const { + return web_data_service_; } } // 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 8df5fb8..ee58113 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller.h +++ b/chrome/browser/sync/glue/autofill_data_type_controller.h @@ -8,53 +8,31 @@ #include <string> -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/synchronization/waitable_event.h" +#include "base/memory/ref_counted.h" #include "chrome/browser/autofill/personal_data_manager.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" +#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" -class Profile; -class ProfileSyncFactory; -class ProfileSyncService; +class NotificationDetails; +class NotificationType; +class NotificationSource; namespace browser_sync { -class AssociatorInterface; -class ChangeProcessor; - // A class that manages the startup and shutdown of autofill sync. -class AutofillDataTypeController : public DataTypeController, +class AutofillDataTypeController : public NonFrontendDataTypeController, public NotificationObserver, public PersonalDataManager::Observer { public: - AutofillDataTypeController( - ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service); + AutofillDataTypeController(ProfileSyncFactory* profile_sync_factory, + Profile* profile); virtual ~AutofillDataTypeController(); - // DataTypeController implementation - virtual void Start(StartCallback* start_callback); - - virtual void Stop(); - - virtual bool enabled(); - + // NonFrontendDataTypeController implementation 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, @@ -64,52 +42,26 @@ class AutofillDataTypeController : public DataTypeController, virtual void OnPersonalDataLoaded(); protected: - virtual ProfileSyncFactory::SyncComponents CreateSyncComponents( - ProfileSyncService* profile_sync_service, - WebDatabase* web_database, - PersonalDataManager* personal_data, - browser_sync::UnrecoverableErrorHandler* error_handler); - ProfileSyncFactory* profile_sync_factory_; - + // 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; 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 01deb74..a4e854e 100644 --- a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc +++ b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc @@ -87,13 +87,14 @@ 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_, - &service_); + &profile_); } 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 2203197..7ea3959 100644 --- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc +++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc @@ -4,18 +4,17 @@ #include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h" -#include "chrome/browser/sync/glue/autofill_data_type_controller.h" +#include "base/metrics/histogram.h" #include "chrome/browser/sync/profile_sync_factory.h" namespace browser_sync { AutofillProfileDataTypeController::AutofillProfileDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service) : AutofillDataTypeController( - profile_sync_factory, - profile, - sync_service) {} + Profile* profile) + : AutofillDataTypeController( + profile_sync_factory, + profile) {} AutofillProfileDataTypeController::~AutofillProfileDataTypeController() {} @@ -23,22 +22,38 @@ syncable::ModelType AutofillProfileDataTypeController::type() const { return syncable::AUTOFILL_PROFILE; } -std::string AutofillProfileDataTypeController::name() const { - // For logging only. - return "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); } -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::RecordUnrecoverableError( + const tracked_objects::Location& from_here, + const std::string& message) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); + UMA_HISTOGRAM_COUNTS("Sync.AutofillProfileRunFailures", 1); } + +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 d97a904..64f0f49 100644 --- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.h +++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.h @@ -15,20 +15,18 @@ class AutofillProfileDataTypeController : public AutofillDataTypeController { public: AutofillProfileDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service); + Profile* profile); virtual ~AutofillProfileDataTypeController(); virtual syncable::ModelType type() const; - virtual std::string name() const; - protected: - virtual ProfileSyncFactory::SyncComponents CreateSyncComponents( - ProfileSyncService* profile_sync_service, - WebDatabase* web_database, - PersonalDataManager* personal_data, - browser_sync::UnrecoverableErrorHandler* error_handler); + 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); }; } // 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 8cd3d5b..4415240 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 8741e84..2655056 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 5e324f9..edec12c 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller.cc +++ b/chrome/browser/sync/glue/frontend_data_type_controller.cc @@ -103,15 +103,46 @@ 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) - FinishStart(ABORTED, FROM_HERE); + 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; + } DCHECK(!start_callback_.get()); - CleanupState(); + CleanUpState(); if (change_processor_ != NULL) sync_service_->DeactivateDataType(this, change_processor_.get()); @@ -125,7 +156,7 @@ void FrontendDataTypeController::Stop() { state_ = NOT_RUNNING; } -void FrontendDataTypeController::CleanupState() { +void FrontendDataTypeController::CleanUpState() { // Do nothing by default. } @@ -150,23 +181,4 @@ 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 d228bb9..6a2d863 100644 --- a/chrome/browser/sync/glue/frontend_data_type_controller.h +++ b/chrome/browser/sync/glue/frontend_data_type_controller.h @@ -22,6 +22,8 @@ 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 @@ -44,15 +46,10 @@ 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. @@ -64,25 +61,33 @@ 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(); - - // Helper method to run the stashed start callback with a given result. - virtual void FinishStart(StartResult result, - const tracked_objects::Location& from_here); + virtual void CleanUpState(); // Cleans up state and calls callback when start fails. virtual void StartFailed(StartResult result, const tracked_objects::Location& from_here); - // Datatype specific creation of sync components. - virtual void CreateSyncComponents() = 0; + // 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 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 0485b1f..a32f3f5 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(CleanupState, void()); - MOCK_METHOD2(FinishStart, void(StartResult result, - const tracked_objects::Location& from_here)); + MOCK_METHOD0(CreateSyncComponents, void()); MOCK_METHOD2(StartFailed, void(StartResult result, const tracked_objects::Location& from_here)); - MOCK_METHOD0(CreateSyncComponents, void()); + MOCK_METHOD2(FinishStart, void(StartResult result, + const tracked_objects::Location& from_here)); + MOCK_METHOD0(CleanUpState, 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 c0c2290..ee3f42f 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,6 +169,16 @@ 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()). @@ -215,7 +225,6 @@ 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()); @@ -232,7 +241,6 @@ 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 new file mode 100644 index 0000000..d26baef --- /dev/null +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller.cc @@ -0,0 +1,279 @@ +// 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 new file mode 100644 index 0000000..c2f2ee0 --- /dev/null +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller.h @@ -0,0 +1,165 @@ +// 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 new file mode 100644 index 0000000..dfde595 --- /dev/null +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.cc @@ -0,0 +1,13 @@ +// 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 new file mode 100644 index 0000000..8e4316f --- /dev/null +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h @@ -0,0 +1,56 @@ +// 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 new file mode 100644 index 0000000..602e582 --- /dev/null +++ b/chrome/browser/sync/glue/non_frontend_data_type_controller_unittest.cc @@ -0,0 +1,397 @@ +// 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 2589861..69b1d89 100644 --- a/chrome/browser/sync/glue/password_data_type_controller.cc +++ b/chrome/browser/sync/glue/password_data_type_controller.cc @@ -5,105 +5,66 @@ #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/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/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" namespace browser_sync { PasswordDataTypeController::PasswordDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service) - : profile_sync_factory_(profile_sync_factory), - profile_(profile), - sync_service_(sync_service), - state_(NOT_RUNNING), - abort_association_(false), - abort_association_complete_(false, false), - datatype_stopped_(false, false) { - DCHECK(profile_sync_factory); - DCHECK(profile); - DCHECK(sync_service); + Profile* profile) + : NonFrontendDataTypeController(profile_sync_factory, + profile) { } PasswordDataTypeController::~PasswordDataTypeController() { } -void PasswordDataTypeController::Start(StartCallback* start_callback) { +bool PasswordDataTypeController::StartModels() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(start_callback); - if (state_ != NOT_RUNNING) { - start_callback->Run(BUSY, FROM_HERE); - delete start_callback; - return; - } - - password_store_ = profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); + DCHECK_EQ(state(), MODEL_STARTING); + password_store_ = profile()->GetPasswordStore(Profile::EXPLICIT_ACCESS); if (!password_store_.get()) { LOG(ERROR) << "PasswordStore not initialized, password datatype controller" << " aborting."; - state_ = NOT_RUNNING; - start_callback->Run(ABORTED, FROM_HERE); - delete start_callback; - return; + StartDoneImpl(ABORTED, NOT_RUNNING, FROM_HERE); + return false; } - - start_callback_.reset(start_callback); - abort_association_ = false; - set_state(ASSOCIATING); - password_store_->ScheduleTask( - NewRunnableMethod(this, &PasswordDataTypeController::StartImpl)); + return true; } -// 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() { +bool PasswordDataTypeController::StartAssociationAsync() { 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 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_EQ(state(), ASSOCIATING); DCHECK(password_store_.get()); password_store_->ScheduleTask( - NewRunnableMethod(this, &PasswordDataTypeController::StopImpl)); - datatype_stopped_.Wait(); + NewRunnableMethod(this, &PasswordDataTypeController::StartAssociation)); + return true; +} + +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::enabled() { +bool PasswordDataTypeController::StopAssociationAsync() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), STOPPING); + DCHECK(password_store_.get()); + password_store_->ScheduleTask( + NewRunnableMethod(this, &PasswordDataTypeController::StopAssociation)); return true; } @@ -116,111 +77,23 @@ browser_sync::ModelSafeGroup PasswordDataTypeController::model_safe_group() return browser_sync::GROUP_PASSWORD; } -std::string PasswordDataTypeController::name() const { - // For logging only. - return "password"; -} - -DataTypeController::State PasswordDataTypeController::state() const { - return state_; -} - -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::RecordUnrecoverableError( + const tracked_objects::Location& from_here, + const std::string& message) { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); + UMA_HISTOGRAM_COUNTS("Sync.PasswordRunFailures", 1); } -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::RecordAssociationTime(base::TimeDelta time) { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); + UMA_HISTOGRAM_TIMES("Sync.PasswordAssociationTime", time); } -void PasswordDataTypeController::OnUnrecoverableErrorImpl( - const tracked_objects::Location& from_here, - const std::string& message) { +void PasswordDataTypeController::RecordStartFailure(StartResult result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - sync_service_->OnUnrecoverableError(from_here, message); + UMA_HISTOGRAM_ENUMERATION("Sync.PasswordStartFailures", + result, + MAX_START_RESULT); } } // 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 1a3a9e41..f2e14f8 100644 --- a/chrome/browser/sync/glue/password_data_type_controller.h +++ b/chrome/browser/sync/glue/password_data_type_controller.h @@ -8,83 +8,40 @@ #include <string> -#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "base/synchronization/waitable_event.h" -#include "chrome/browser/sync/profile_sync_service.h" -#include "chrome/browser/sync/glue/data_type_controller.h" +#include "chrome/browser/sync/glue/non_frontend_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 DataTypeController { +class PasswordDataTypeController : public NonFrontendDataTypeController { public: PasswordDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service); + Profile* profile); virtual ~PasswordDataTypeController(); - // DataTypeController implementation - virtual void Start(StartCallback* start_callback); - - virtual void Stop(); - - virtual bool enabled(); - + // NonFrontendDataTypeController implementation 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); + 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); 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 93be84d..73776fe 100644 --- a/chrome/browser/sync/glue/typed_url_data_type_controller.cc +++ b/chrome/browser/sync/glue/typed_url_data_type_controller.cc @@ -4,14 +4,10 @@ #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" @@ -26,11 +22,7 @@ class ControlTask : public HistoryDBTask { virtual bool RunOnDBThread(history::HistoryBackend* backend, history::HistoryDatabase* db) { - if (start_) { - controller_->StartImpl(backend); - } else { - controller_->StopImpl(); - } + controller_->RunOnHistoryThread(start_, backend); // Release the reference to the controller. This ensures that // the controller isn't held past its lifetime in unit tests. @@ -47,104 +39,90 @@ class ControlTask : public HistoryDBTask { TypedUrlDataTypeController::TypedUrlDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service) - : profile_sync_factory_(profile_sync_factory), - profile_(profile), - sync_service_(sync_service), - state_(NOT_RUNNING), - abort_association_(false), - abort_association_complete_(false, false), - datatype_stopped_(false, false) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - DCHECK(profile_sync_factory); - DCHECK(profile); - DCHECK(sync_service); + Profile* profile) + : NonFrontendDataTypeController(profile_sync_factory, + profile), + backend_(NULL) { } TypedUrlDataTypeController::~TypedUrlDataTypeController() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } -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; +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(); } + backend_ = NULL; +} - start_callback_.reset(start_callback); - abort_association_ = false; - - HistoryService* history = profile_->GetHistoryServiceWithoutCreating(); +bool TypedUrlDataTypeController::StartModels() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), MODEL_STARTING); + HistoryService* history = profile()->GetHistoryServiceWithoutCreating(); if (history) { - set_state(ASSOCIATING); history_service_ = history; - history_service_->ScheduleDBTask(new ControlTask(this, true), this); + return true; } 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) { - VLOG(1) << "History loaded observed."; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), MODEL_STARTING); notification_registrar_.Remove(this, NotificationType::HISTORY_LOADED, NotificationService::AllSources()); - - history_service_ = profile_->GetHistoryServiceWithoutCreating(); + history_service_ = profile()->GetHistoryServiceWithoutCreating(); DCHECK(history_service_.get()); - history_service_->ScheduleDBTask(new ControlTask(this, true), this); + set_state(ASSOCIATING); + 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 TypedUrlDataTypeController::Stop() { - VLOG(1) << "Stopping typed_url data type controller."; +void TypedUrlDataTypeController::StopModels() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(state() == STOPPING || state() == NOT_RUNNING); + notification_registrar_.RemoveAll(); +} - // 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); +bool TypedUrlDataTypeController::StopAssociationAsync() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK_EQ(state(), STOPPING); DCHECK(history_service_.get()); history_service_->ScheduleDBTask(new ControlTask(this, false), this); - datatype_stopped_.Wait(); -} - -bool TypedUrlDataTypeController::enabled() { return true; } @@ -157,125 +135,22 @@ browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() return browser_sync::GROUP_HISTORY; } -std::string TypedUrlDataTypeController::name() const { - // For logging only. - return "typed_url"; -} - -DataTypeController::State TypedUrlDataTypeController::state() const { - return state_; -} - -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)); - 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( +void TypedUrlDataTypeController::RecordUnrecoverableError( const tracked_objects::Location& from_here, const std::string& message) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &TypedUrlDataTypeController::OnUnrecoverableErrorImpl, - from_here, message)); + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); + UMA_HISTOGRAM_COUNTS("Sync.TypedUrlRunFailures", 1); } -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); +void TypedUrlDataTypeController::RecordAssociationTime(base::TimeDelta time) { + DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); + UMA_HISTOGRAM_TIMES("Sync.TypedUrlAssociationTime", time); } +void TypedUrlDataTypeController::RecordStartFailure(StartResult result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + UMA_HISTOGRAM_ENUMERATION("Sync.TypedUrlStartFailures", + result, + MAX_START_RESULT); +} } // 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 386a01d..2db5520 100644 --- a/chrome/browser/sync/glue/typed_url_data_type_controller.h +++ b/chrome/browser/sync/glue/typed_url_data_type_controller.h @@ -8,11 +8,8 @@ #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" -#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h" #include "content/browser/cancelable_request.h" #include "content/common/notification_observer.h" #include "content/common/notification_registrar.h" @@ -21,9 +18,6 @@ class NotificationSource; class NotificationDetails; class HistoryService; -class Profile; -class ProfileSyncFactory; -class ProfileSyncService; namespace history { class HistoryBackend; @@ -31,40 +25,22 @@ 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 DataTypeController, +class TypedUrlDataTypeController : public NonFrontendDataTypeController, public NotificationObserver, public CancelableRequestConsumerBase { public: TypedUrlDataTypeController( ProfileSyncFactory* profile_sync_factory, - Profile* profile, - ProfileSyncService* sync_service); + Profile* profile); virtual ~TypedUrlDataTypeController(); - // DataTypeController implementation - virtual void Start(StartCallback* start_callback); - - virtual void Stop(); - - virtual bool enabled(); - + // NonFrontendDataTypeController implementation 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, @@ -73,50 +49,35 @@ class TypedUrlDataTypeController : public DataTypeController, // 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; - 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_; + // Helper method to launch Associate() and Destroy() on the history thread. + void RunOnHistoryThread(bool start, history::HistoryBackend* backend); - // Barrier to ensure that the datatype has been stopped on the DB thread - // from the UI thread. - base::WaitableEvent datatype_stopped_; + history::HistoryBackend* backend_; + scoped_refptr<HistoryService> history_service_; + NotificationRegistrar notification_registrar_; DISALLOW_COPY_AND_ASSIGN(TypedUrlDataTypeController); }; diff --git a/chrome/browser/sync/profile_sync_factory.h b/chrome/browser/sync/profile_sync_factory.h index 776b61d..8befe35 100644 --- a/chrome/browser/sync/profile_sync_factory.h +++ b/chrome/browser/sync/profile_sync_factory.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -47,12 +47,15 @@ class ProfileSyncFactory { virtual ~ProfileSyncFactory() {} - // Instantiates and initializes a new ProfileSyncService. Enabled - // data types are registered with the service. The return pointer - // is owned by the caller. + // Instantiates a new ProfileSyncService. 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 9102e88..19f4938 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_factory_impl.cc @@ -86,7 +86,10 @@ 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)) { @@ -98,7 +101,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( // disabled. if (!command_line_->HasSwitch(switches::kDisableSyncAutofill)) { pss->RegisterDataTypeController( - new AutofillDataTypeController(this, profile_, pss)); + new AutofillDataTypeController(this, profile_)); } // Bookmark sync is enabled by default. Register unless explicitly @@ -119,7 +122,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( // disabled. if (!command_line_->HasSwitch(switches::kDisableSyncPasswords)) { pss->RegisterDataTypeController( - new PasswordDataTypeController(this, profile_, pss)); + new PasswordDataTypeController(this, profile_)); } // Preference sync is enabled by default. Register unless explicitly @@ -139,7 +142,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( // explicitly enabled. if (command_line_->HasSwitch(switches::kEnableSyncTypedUrls)) { pss->RegisterDataTypeController( - new TypedUrlDataTypeController(this, profile_, pss)); + new TypedUrlDataTypeController(this, profile_)); } // Session sync is disabled by default. Register only if explicitly @@ -150,10 +153,9 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService( } if (!command_line_->HasSwitch(switches::kDisableSyncAutofillProfile)) { - pss->RegisterDataTypeController(new AutofillProfileDataTypeController( - this, profile_, pss)); + pss->RegisterDataTypeController( + new AutofillProfileDataTypeController(this, profile_)); } - 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 eb93f1c..2015a40 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) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -23,6 +23,8 @@ 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 907b2f6..56643ac 100644 --- a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc +++ b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc @@ -76,6 +76,7 @@ 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()); @@ -92,6 +93,7 @@ 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 2d8d8e2..3fa856c 100644 --- a/chrome/browser/sync/profile_sync_factory_mock.h +++ b/chrome/browser/sync/profile_sync_factory_mock.h @@ -26,6 +26,7 @@ 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 a7b0e36..763d6bb 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -207,8 +207,7 @@ class AutofillEntryFactory : public AbstractAutofillFactory { ProfileMock* profile, ProfileSyncService* service) { return new AutofillDataTypeController(factory, - profile, - service); + profile); } void SetExpectation(ProfileSyncFactoryMock* factory, @@ -228,8 +227,7 @@ class AutofillProfileFactory : public AbstractAutofillFactory { ProfileMock* profile, ProfileSyncService* service) { return new AutofillProfileDataTypeController(factory, - profile, - service); + profile); } void SetExpectation(ProfileSyncFactoryMock* factory, @@ -302,6 +300,8 @@ 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,7 +670,6 @@ 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 22005ce..94f2169 100644 --- a/chrome/browser/sync/profile_sync_service_password_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc @@ -189,10 +189,11 @@ 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_, - service_.get()); + &profile_); 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 7125cce..6184b6a 100644 --- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc @@ -163,10 +163,11 @@ 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_, - service_.get()); + &profile_); EXPECT_CALL(factory_, CreateTypedUrlSyncComponents(_, _, _)). WillOnce(MakeTypedUrlSyncComponents(service_.get(), |