diff options
author | zork@google.com <zork@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-01 22:06:21 +0000 |
---|---|---|
committer | zork@google.com <zork@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-01 22:06:21 +0000 |
commit | 8b3b23abcf91b7573c6fcf230240395118c04580 (patch) | |
tree | 3c4dc9c354662a6d9ecff8443f4620fa017c7718 /chrome/browser/sync | |
parent | 3950400350f7f8494f09a3568acb6d40592701f5 (diff) | |
download | chromium_src-8b3b23abcf91b7573c6fcf230240395118c04580.zip chromium_src-8b3b23abcf91b7573c6fcf230240395118c04580.tar.gz chromium_src-8b3b23abcf91b7573c6fcf230240395118c04580.tar.bz2 |
Add autofill Change Processor and Model Associator
BUG=29926
TEST=none
Review URL: http://codereview.chromium.org/628003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40304 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
19 files changed, 1121 insertions, 14 deletions
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 6be8c11..938003e 100755 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -48,6 +48,7 @@ #include "chrome/browser/sync/engine/syncer_thread.h" #include "chrome/browser/sync/notifier/listener/talk_mediator.h" #include "chrome/browser/sync/notifier/listener/talk_mediator_impl.h" +#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" #include "chrome/browser/sync/protocol/preference_specifics.pb.h" #include "chrome/browser/sync/protocol/service_constants.h" @@ -466,6 +467,11 @@ int64 BaseNode::GetExternalId() const { return GetEntry()->Get(syncable::LOCAL_EXTERNAL_ID); } +const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const { + DCHECK(GetModelType() == syncable::AUTOFILL); + return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::autofill); +} + const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const { DCHECK(GetModelType() == syncable::BOOKMARKS); return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::bookmark); @@ -509,6 +515,19 @@ void WriteNode::SetURL(const GURL& url) { SetBookmarkSpecifics(new_value); } +void WriteNode::SetAutofillSpecifics( + const sync_pb::AutofillSpecifics& new_value) { + DCHECK(GetModelType() == syncable::AUTOFILL); + PutAutofillSpecificsAndMarkForSyncing(new_value); +} + +void WriteNode::PutAutofillSpecificsAndMarkForSyncing( + const sync_pb::AutofillSpecifics& new_value) { + sync_pb::EntitySpecifics entity_specifics; + entity_specifics.MutableExtension(sync_pb::autofill)->CopyFrom(new_value); + PutSpecificsAndMarkForSyncing(entity_specifics); +} + void WriteNode::SetBookmarkSpecifics( const sync_pb::BookmarkSpecifics& new_value) { DCHECK(GetModelType() == syncable::BOOKMARKS); diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index 5ed3248..9fa0765 100755 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -68,6 +68,7 @@ class WriteTransaction; } namespace sync_pb { +class AutofillSpecifics; class BookmarkSpecifics; class EntitySpecifics; class PreferenceSpecifics; @@ -161,6 +162,10 @@ class BaseNode { // TODO(ncarter): Remove this datatype-specific accessor. void GetFaviconBytes(std::vector<unsigned char>* output) const; + // Getter specific to the AUTOFILL datatype. Returns protobuf + // data. Can only be called if GetModelType() == AUTOFILL. + const sync_pb::AutofillSpecifics& GetAutofillSpecifics() const; + // Getter specific to the PREFERENCE datatype. Returns protobuf // data. Can only be called if GetModelType() == PREFERENCE. const sync_pb::PreferenceSpecifics& GetPreferenceSpecifics() const; @@ -264,6 +269,10 @@ class WriteNode : public BaseNode { void SetURL(const GURL& url); void SetFaviconBytes(const std::vector<unsigned char>& bytes); + // Set the autofill specifics (name and value). + // Should only be called if GetModelType() == AUTOFILL. + void SetAutofillSpecifics(const sync_pb::AutofillSpecifics& specifics); + // Set the preference specifics (name and value). // Should only be called if GetModelType() == PREFERENCE. void SetPreferenceSpecifics(const sync_pb::PreferenceSpecifics& specifics); @@ -287,6 +296,8 @@ class WriteNode : public BaseNode { // for internal initialization (you can use them to set the modeltype). // Additionally, they will mark for syncing if the underlying value // changes. + void PutAutofillSpecificsAndMarkForSyncing( + const sync_pb::AutofillSpecifics& new_value); void PutBookmarkSpecificsAndMarkForSyncing( const sync_pb::BookmarkSpecifics& new_value); void PutPreferenceSpecificsAndMarkForSyncing( diff --git a/chrome/browser/sync/glue/autofill_change_processor.cc b/chrome/browser/sync/glue/autofill_change_processor.cc new file mode 100755 index 0000000..9e7e174 --- /dev/null +++ b/chrome/browser/sync/glue/autofill_change_processor.cc @@ -0,0 +1,260 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/glue/autofill_change_processor.h" + +#include "base/string_util.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/sync/glue/autofill_model_associator.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" +#include "chrome/browser/webdata/autofill_change.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/browser/webdata/web_database.h" +#include "chrome/common/notification_service.h" + +namespace browser_sync { + +AutofillChangeProcessor::AutofillChangeProcessor( + AutofillModelAssociator* model_associator, + UnrecoverableErrorHandler* error_handler) + : ChangeProcessor(error_handler), + model_associator_(model_associator), + observing_(false) { + DCHECK(model_associator); + DCHECK(error_handler); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + StartObserving(); +} + +void AutofillChangeProcessor::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + LOG(INFO) << "Observed autofill change."; + DCHECK(running()); + DCHECK(NotificationType::AUTOFILL_ENTRIES_CHANGED == type); + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + if (!observing_ || !web_data_service_.get()) { + return; + } + + DCHECK(web_data_service_->GetDatabase()); + + AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr(); + + sync_api::WriteTransaction trans(share_handle()); + + for (AutofillChangeList::iterator change = changes->begin(); + change != changes->end(); ++change) { + switch (change->type()) { + case AutofillChange::ADD: + { + sync_api::ReadNode autofill_root(&trans); + if (!autofill_root.InitByTagLookup(kAutofillTag)) { + error_handler()->OnUnrecoverableError(); + LOG(ERROR) << "Server did not create the top-level autofill node. " + << "We might be running against an out-of-date server."; + return; + } + + sync_api::WriteNode sync_node(&trans); + std::string tag = + AutofillModelAssociator::KeyToTag(change->key().name(), + change->key().value()); + if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL, + autofill_root, tag)) { + LOG(ERROR) << "Failed to create autofill sync node."; + error_handler()->OnUnrecoverableError(); + return; + } + + std::vector<base::Time> timestamps; + if (!web_data_service_->GetDatabase()->GetAutofillTimestamps( + change->key().name(), + change->key().value(), + ×tamps)) { + LOG(ERROR) << "Failed to get timestamps."; + error_handler()->OnUnrecoverableError(); + return; + } + + sync_node.SetTitle(UTF16ToWide(change->key().name() + + change->key().value())); + + if (!WriteAutofill(&sync_node, change->key().name(), + change->key().value(), timestamps)) { + LOG(ERROR) << "Failed to update autofill node."; + error_handler()->OnUnrecoverableError(); + return; + } + + model_associator_->Associate(&(change->key()), sync_node.GetId()); + } + break; + + case AutofillChange::UPDATE: + { + sync_api::WriteNode sync_node(&trans); + int64 sync_id = + model_associator_->GetSyncIdFromChromeId(change->key()); + if (sync_api::kInvalidId == sync_id) { + LOG(ERROR) << "Unexpected notification for: " << + change->key().name(); + error_handler()->OnUnrecoverableError(); + return; + } else { + if (!sync_node.InitByIdLookup(sync_id)) { + LOG(ERROR) << "Autofill node lookup failed."; + error_handler()->OnUnrecoverableError(); + return; + } + } + + std::vector<base::Time> timestamps; + if (!web_data_service_->GetDatabase()->GetAutofillTimestamps( + change->key().name(), + change->key().value(), + ×tamps)) { + LOG(ERROR) << "Failed to get timestamps."; + error_handler()->OnUnrecoverableError(); + return; + } + + if (!WriteAutofill(&sync_node, change->key().name(), + change->key().value(), timestamps)) { + LOG(ERROR) << "Failed to update autofill node."; + error_handler()->OnUnrecoverableError(); + return; + } + } + break; + + case AutofillChange::REMOVE: + { + sync_api::WriteNode sync_node(&trans); + int64 sync_id = + model_associator_->GetSyncIdFromChromeId(change->key()); + if (sync_api::kInvalidId == sync_id) { + LOG(ERROR) << "Unexpected notification for: " << + change->key().name(); + error_handler()->OnUnrecoverableError(); + return; + } else { + model_associator_->Disassociate(sync_node.GetId()); + sync_node.Remove(); + } + } + break; + } + } +} + +void AutofillChangeProcessor::ApplyChangesFromSyncModel( + const sync_api::BaseTransaction* trans, + const sync_api::SyncManager::ChangeRecord* changes, + int change_count) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + DCHECK(web_data_service_.get()); + DCHECK(web_data_service_->GetDatabase()); + if (!running()) + return; + StopObserving(); + + sync_api::ReadNode autofill_root(trans); + if (!autofill_root.InitByTagLookup(kAutofillTag)) { + LOG(ERROR) << "Autofill root node lookup failed."; + error_handler()->OnUnrecoverableError(); + return; + } + + std::vector<AutofillEntry> new_entries; + for (int i = 0; i < change_count; ++i) { + + sync_api::ReadNode sync_node(trans); + if (!sync_node.InitByIdLookup(changes[i].id)) { + LOG(ERROR) << "Autofill node lookup failed."; + error_handler()->OnUnrecoverableError(); + return; + } + + // Check that the changed node is a child of the autofills folder. + DCHECK(autofill_root.GetId() == sync_node.GetParentId()); + DCHECK(syncable::AUTOFILL == sync_node.GetModelType()); + + const sync_pb::AutofillSpecifics& autofill( + sync_node.GetAutofillSpecifics()); + if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == + changes[i].action) { + if (!web_data_service_->GetDatabase()->RemoveFormElement( + UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) { + LOG(ERROR) << "Could not remove autofill node."; + error_handler()->OnUnrecoverableError(); + return; + } + } else { + std::vector<base::Time> timestamps; + size_t timestamps_size = autofill.usage_timestamp_size(); + for (size_t c = 0; c < timestamps_size; ++c) { + timestamps.push_back( + base::Time::FromInternalValue(autofill.usage_timestamp(c))); + } + AutofillEntry new_entry(AutofillKey(UTF8ToUTF16(autofill.name()), + UTF8ToUTF16(autofill.value())), + timestamps); + + new_entries.push_back(new_entry); + } + } + if (!web_data_service_->GetDatabase()->UpdateAutofillEntries(new_entries)) { + LOG(ERROR) << "Could not update autofill entries."; + error_handler()->OnUnrecoverableError(); + return; + } + StartObserving(); +} + +bool AutofillChangeProcessor::WriteAutofill( + sync_api::WriteNode* node, + const string16& name, + const string16& value, + std::vector<base::Time>& timestamps) { + sync_pb::AutofillSpecifics autofill; + autofill.set_name(UTF16ToUTF8(name)); + autofill.set_value(UTF16ToUTF8(value)); + for (std::vector<base::Time>::iterator timestamp = timestamps.begin(); + timestamp != timestamps.end(); ++timestamp) { + autofill.add_usage_timestamp(timestamp->ToInternalValue()); + } + node->SetAutofillSpecifics(autofill); + return true; +} + + +void AutofillChangeProcessor::StartImpl(Profile* profile) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + web_data_service_ = profile->GetWebDataService(Profile::IMPLICIT_ACCESS); + observing_ = true; +} + +void AutofillChangeProcessor::StopImpl() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + observing_ = false; + web_data_service_.release(); +} + + +void AutofillChangeProcessor::StartObserving() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources()); +} + +void AutofillChangeProcessor::StopObserving() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + notification_registrar_.Remove(this, + NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources()); +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/glue/autofill_change_processor.h b/chrome/browser/sync/glue/autofill_change_processor.h new file mode 100755 index 0000000..7468544 --- /dev/null +++ b/chrome/browser/sync/glue/autofill_change_processor.h @@ -0,0 +1,74 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_GLUE_AUTOFILL_CHANGE_PROCESSOR_H_ +#define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_CHANGE_PROCESSOR_H_ + +#include "base/scoped_ptr.h" +#include "chrome/browser/sync/engine/syncapi.h" +#include "chrome/browser/sync/glue/change_processor.h" +#include "chrome/browser/sync/glue/sync_backend_host.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" + +class WebDataService; + +namespace browser_sync { + +class AutofillModelAssociator; +class UnrecoverableErrorHandler; + +// This class is responsible for taking changes from the web data service and +// applying them to the sync_api 'syncable' model, and vice versa. All +// operations and use of this class are from the UI thread. +class AutofillChangeProcessor : public ChangeProcessor, + public NotificationObserver { + public: + AutofillChangeProcessor(AutofillModelAssociator* model_associator, + UnrecoverableErrorHandler* error_handler); + virtual ~AutofillChangeProcessor() {} + + // NotificationObserver implementation. + // WebDataService -> sync_api model change application. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // sync_api model -> WebDataService change application. + virtual void ApplyChangesFromSyncModel( + const sync_api::BaseTransaction* trans, + const sync_api::SyncManager::ChangeRecord* changes, + int change_count); + + protected: + virtual void StartImpl(Profile* profile); + virtual void StopImpl(); + + private: + bool WriteAutofill(sync_api::WriteNode* node, + const string16& name, + const string16& value, + std::vector<base::Time>& timestamps); + + void StartObserving(); + void StopObserving(); + + // The model we are processing changes from. Non-NULL when |running_| is true. + // We keep a reference to web data service instead of the database because + // WebDatabase is not refcounted. + scoped_refptr<WebDataService> web_data_service_; + + // The two models should be associated according to this ModelAssociator. + AutofillModelAssociator* model_associator_; + + NotificationRegistrar notification_registrar_; + + bool observing_; + + DISALLOW_COPY_AND_ASSIGN(AutofillChangeProcessor); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_GLUE_AUTOFILL_CHANGE_PROCESSOR_H_ diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.cc b/chrome/browser/sync/glue/autofill_data_type_controller.cc new file mode 100755 index 0000000..eaf163a --- /dev/null +++ b/chrome/browser/sync/glue/autofill_data_type_controller.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/histogram.h" +#include "base/logging.h" +#include "base/task.h" +#include "base/time.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/sync/glue/autofill_change_processor.h" +#include "chrome/browser/sync/glue/autofill_data_type_controller.h" +#include "chrome/browser/sync/glue/autofill_model_associator.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/profile_sync_factory.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/notification_service.h" + +namespace browser_sync { + +AutofillDataTypeController::AutofillDataTypeController( + ProfileSyncFactory* profile_sync_factory, + ProfileSyncService* sync_service) + : profile_sync_factory_(profile_sync_factory), + sync_service_(sync_service), + state_(NOT_RUNNING), + merge_allowed_(false) { + DCHECK(profile_sync_factory); + DCHECK(sync_service); +} + +AutofillDataTypeController::~AutofillDataTypeController() { +} + +void AutofillDataTypeController::Start(bool merge_allowed, + StartCallback* start_callback) { + LOG(INFO) << "Starting autofill data controller."; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + if (state_ != NOT_RUNNING || start_callback_.get()) { + start_callback->Run(BUSY); + delete start_callback; + return; + } + + start_callback_.reset(start_callback); + merge_allowed_ = merge_allowed; + + WebDataService *service = + sync_service_->profile()->GetWebDataService(Profile::IMPLICIT_ACCESS); + if (service && service->IsDatabaseLoaded()) { + ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartImpl, + merge_allowed_)); + } else { + notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, + NotificationService::AllSources()); + } +} + +void AutofillDataTypeController::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + LOG(INFO) << "Web database loaded observed."; + notification_registrar_.Remove(this, + NotificationType::WEB_DATABASE_LOADED, + NotificationService::AllSources()); + + ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartImpl, + merge_allowed_)); +} + +void AutofillDataTypeController::Stop() { + LOG(INFO) << "Stopping autofill data type controller."; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + if (change_processor_ != NULL) + sync_service_->DeactivateDataType(this, change_processor_.get()); + + if (model_associator_ != NULL) + model_associator_->DisassociateModels(); + + ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StopImpl)); +} + +void AutofillDataTypeController::StartImpl(bool merge_allowed) { + LOG(INFO) << "Autofill data type controller StartImpl called."; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + // No additional services need to be started before we can proceed + // with model association. + ProfileSyncFactory::SyncComponents sync_components = + profile_sync_factory_->CreateAutofillSyncComponents(sync_service_, this); + model_associator_.reset(sync_components.model_associator); + change_processor_.reset(sync_components.change_processor); + bool needs_merge = model_associator_->ChromeModelHasUserCreatedNodes() && + model_associator_->SyncModelHasUserCreatedNodes(); + if (needs_merge && !merge_allowed) { + model_associator_.reset(); + change_processor_.reset(); + StartDone(NEEDS_MERGE); + return; + } + + base::TimeTicks start_time = base::TimeTicks::Now(); + bool first_run = !model_associator_->SyncModelHasUserCreatedNodes(); + bool merge_success = model_associator_->AssociateModels(); + UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", + base::TimeTicks::Now() - start_time); + if (!merge_success) { + model_associator_.reset(); + change_processor_.reset(); + StartDone(ASSOCIATION_FAILED); + return; + } + + StartDone(first_run ? OK_FIRST_RUN : OK); +} + +void AutofillDataTypeController::StartDone( + DataTypeController::StartResult result) { + LOG(INFO) << "Autofill data type controller StartDone called."; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, + &AutofillDataTypeController::StartDoneImpl, + result)); +} + +void AutofillDataTypeController::StartDoneImpl( + DataTypeController::StartResult result) { + LOG(INFO) << "Autofill data type controller StartDoneImpl called."; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + if (result == OK_FIRST_RUN || result == OK) { + sync_service_->ActivateDataType(this, change_processor_.get()); + state_ = RUNNING; + } + + start_callback_->Run(result); + start_callback_.reset(); +} + +void AutofillDataTypeController::StopImpl() { + LOG(INFO) << "Autofill data type controller StopImpl called."; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + + change_processor_.reset(); + model_associator_.reset(); + + state_ = NOT_RUNNING; +} + +void AutofillDataTypeController::OnUnrecoverableError() { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod(this, + &AutofillDataTypeController::OnUnrecoverableErrorImpl)); +} + +void AutofillDataTypeController::OnUnrecoverableErrorImpl() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + sync_service_->OnUnrecoverableError(); +} + +} // 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 new file mode 100755 index 0000000..6500c0b --- /dev/null +++ b/chrome/browser/sync/glue/autofill_data_type_controller.h @@ -0,0 +1,90 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_GLUE_AUTOFILL_DATA_TYPE_CONTROLLER_H__ +#define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_DATA_TYPE_CONTROLLER_H__ + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/glue/data_type_controller.h" + +class ProfileSyncFactory; +class ProfileSyncService; + +namespace browser_sync { + +class AssociatorInterface; +class ChangeProcessor; + +// A class that manages the startup and shutdown of autofill sync. +class AutofillDataTypeController : public DataTypeController, + public UnrecoverableErrorHandler, + public NotificationObserver { + public: + AutofillDataTypeController( + ProfileSyncFactory* profile_sync_factory, + ProfileSyncService* sync_service); + virtual ~AutofillDataTypeController(); + + // DataTypeController implementation + virtual void Start(bool merge_allowed, StartCallback* start_callback); + + virtual void Stop(); + + virtual bool enabled() { + return true; + } + + virtual syncable::ModelType type() { + return syncable::AUTOFILL; + } + + virtual browser_sync::ModelSafeGroup model_safe_group() { + return browser_sync::GROUP_DB; + } + + virtual const char* name() const { + // For logging only. + return "autofill"; + } + + virtual State state() { + return state_; + } + + // UnrecoverableHandler implementation + virtual void OnUnrecoverableError(); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + void StartImpl(bool merge_allowed); + void StartDone(DataTypeController::StartResult result); + void StartDoneImpl(DataTypeController::StartResult result); + void StopImpl(); + void OnUnrecoverableErrorImpl(); + + ProfileSyncFactory* profile_sync_factory_; + ProfileSyncService* sync_service_; + + State state_; + + scoped_ptr<AssociatorInterface> model_associator_; + scoped_ptr<ChangeProcessor> change_processor_; + scoped_ptr<StartCallback> start_callback_; + + NotificationRegistrar notification_registrar_; + + bool merge_allowed_; + + DISALLOW_COPY_AND_ASSIGN(AutofillDataTypeController); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_GLUE_AUTOFILL_DATA_TYPE_CONTROLLER_H__ diff --git a/chrome/browser/sync/glue/autofill_model_associator.cc b/chrome/browser/sync/glue/autofill_model_associator.cc new file mode 100755 index 0000000..da26816 --- /dev/null +++ b/chrome/browser/sync/glue/autofill_model_associator.cc @@ -0,0 +1,291 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/glue/autofill_model_associator.h" + +#include "base/utf_string_conversions.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/sync/engine/syncapi.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" +#include "chrome/browser/webdata/web_database.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "net/base/escape.h" + +namespace browser_sync { + +const char kAutofillTag[] = "google_chrome_autofill"; + +AutofillModelAssociator::AutofillModelAssociator( + ProfileSyncService* sync_service, + UnrecoverableErrorHandler* error_handler) + : sync_service_(sync_service), + error_handler_(error_handler), + autofill_node_id_(sync_api::kInvalidId), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { + DCHECK(sync_service_); + DCHECK(error_handler_); +} + +bool AutofillModelAssociator::AssociateModels() { + LOG(INFO) << "Associating Autofill Models"; + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + + sync_api::WriteTransaction trans( + sync_service_->backend()->GetUserShareHandle()); + WebDataService* web_data_service = + sync_service_->profile()->GetWebDataService(Profile::EXPLICIT_ACCESS); + if (!web_data_service) { + LOG(ERROR) << "Could not get the web data service."; + return false; + } + + std::vector<AutofillEntry> entries; + if (!web_data_service->GetDatabase()->GetAllAutofillEntries(&entries)) { + LOG(ERROR) << "Could not get the autofill entries."; + return false; + } + + sync_api::ReadNode autofill_root(&trans); + if (!autofill_root.InitByTagLookup(kAutofillTag)) { + error_handler_->OnUnrecoverableError(); + LOG(ERROR) << "Server did not create the top-level autofill node. We " + << "might be running against an out-of-date server."; + return false; + } + + std::set<AutofillKey> current_entries; + std::vector<AutofillEntry> new_entries; + + for (std::vector<AutofillEntry>::iterator ix = entries.begin(); + ix != entries.end(); ++ix) { + if (id_map_.find(ix->key()) != id_map_.end()) { + // It seems that name/value pairs are not unique in the web database. + // As a result, we have to filter out duplicates here. This is probably + // a bug in the database. + continue; + } + + std::string tag = KeyToTag(ix->key().name(), ix->key().value()); + + sync_api::ReadNode node(&trans); + if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) { + const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics()); + DCHECK_EQ(tag, KeyToTag(UTF8ToUTF16(autofill.name()), + UTF8ToUTF16(autofill.value()))); + + std::vector<base::Time> timestamps; + if (MergeTimestamps(autofill, ix->timestamps(), ×tamps)) { + new_entries.push_back(AutofillEntry( + AutofillKey(ix->key().name(), ix->key().value()), + timestamps)); + } + + Associate(&(ix->key()), node.GetId()); + } else { + sync_api::WriteNode node(&trans); + if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) { + LOG(ERROR) << "Failed to create autofill sync node."; + error_handler_->OnUnrecoverableError(); + return false; + } + + node.SetTitle(UTF16ToWide(ix->key().name() + ix->key().value())); + + sync_pb::AutofillSpecifics autofill; + autofill.set_name(UTF16ToUTF8(ix->key().name())); + autofill.set_value(UTF16ToUTF8(ix->key().value())); + + for (std::vector<base::Time>::const_iterator timestamp = + ix->timestamps().begin(); timestamp != ix->timestamps().end(); + ++timestamp) { + autofill.add_usage_timestamp(timestamp->ToInternalValue()); + } + + node.SetAutofillSpecifics(autofill); + Associate(&(ix->key()), node.GetId()); + } + + current_entries.insert(ix->key()); + } + + int64 sync_child_id = autofill_root.GetFirstChildId(); + while (sync_child_id != sync_api::kInvalidId) { + sync_api::ReadNode sync_child_node(&trans); + if (!sync_child_node.InitByIdLookup(sync_child_id)) { + LOG(ERROR) << "Failed to fetch child node."; + sync_service_->OnUnrecoverableError(); + return false; + } + const sync_pb::AutofillSpecifics& autofill( + sync_child_node.GetAutofillSpecifics()); + AutofillKey key(UTF8ToUTF16(autofill.name()), + UTF8ToUTF16(autofill.value())); + + if (current_entries.find(key) == current_entries.end()) { + std::vector<base::Time> timestamps; + int timestamps_count = autofill.usage_timestamp_size(); + for (int c = 0; c < timestamps_count; ++c) { + timestamps.push_back(base::Time::FromInternalValue( + autofill.usage_timestamp(c))); + } + Associate(&key, sync_child_node.GetId()); + new_entries.push_back(AutofillEntry(key, timestamps)); + } + + sync_child_id = sync_child_node.GetSuccessorId(); + } + + if (!web_data_service->GetDatabase()->UpdateAutofillEntries(new_entries)) { + LOG(ERROR) << "Failed to update autofill entries."; + sync_service_->OnUnrecoverableError(); + return false; + } + + return true; +} + +bool AutofillModelAssociator::DisassociateModels() { + id_map_.clear(); + id_map_inverse_.clear(); + dirty_associations_sync_ids_.clear(); + return true; +} + +bool AutofillModelAssociator::SyncModelHasUserCreatedNodes() { + int64 autofill_sync_id; + if (!GetSyncIdForTaggedNode(kAutofillTag, &autofill_sync_id)) { + error_handler_->OnUnrecoverableError(); + LOG(ERROR) << "Server did not create the top-level autofill node. We " + << "might be running against an out-of-date server."; + return false; + } + sync_api::ReadTransaction trans( + sync_service_->backend()->GetUserShareHandle()); + + sync_api::ReadNode autofill_node(&trans); + if (!autofill_node.InitByIdLookup(autofill_sync_id)) { + error_handler_->OnUnrecoverableError(); + LOG(ERROR) << "Server did not create the top-level autofill node. We " + << "might be running against an out-of-date server."; + return false; + } + + // The sync model has user created nodes if the autofill folder has any + // children. + return sync_api::kInvalidId != autofill_node.GetFirstChildId(); +} + +bool AutofillModelAssociator::ChromeModelHasUserCreatedNodes() { + // Assume the autofill model always have user-created nodes. + return true; +} + +int64 AutofillModelAssociator::GetSyncIdFromChromeId( + const AutofillKey autofill) { + AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill); + return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; +} + +void AutofillModelAssociator::Associate( + const AutofillKey* autofill, int64 sync_id) { + DCHECK_NE(sync_api::kInvalidId, sync_id); + DCHECK(id_map_.find(*autofill) == id_map_.end()); + DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); + id_map_[*autofill] = sync_id; + id_map_inverse_[sync_id] = *autofill; + dirty_associations_sync_ids_.insert(sync_id); + PostPersistAssociationsTask(); +} + +void AutofillModelAssociator::Disassociate(int64 sync_id) { + SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id); + if (iter == id_map_inverse_.end()) + return; + CHECK(id_map_.erase(iter->second)); + id_map_inverse_.erase(iter); + CHECK(dirty_associations_sync_ids_.erase(sync_id)); +} + +bool AutofillModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, + int64* sync_id) { + sync_api::ReadTransaction trans( + sync_service_->backend()->GetUserShareHandle()); + sync_api::ReadNode sync_node(&trans); + if (!sync_node.InitByTagLookup(tag.c_str())) + return false; + *sync_id = sync_node.GetId(); + return true; +} + +void AutofillModelAssociator::PostPersistAssociationsTask() { + // No need to post a task if a task is already pending. + if (!method_factory_.empty()) + return; + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &AutofillModelAssociator::PersistAssociations)); +} + +void AutofillModelAssociator::PersistAssociations() { + // If there are no dirty associations we have nothing to do. We handle this + // explicity instead of letting the for loop do it to avoid creating a write + // transaction in this case. + if (dirty_associations_sync_ids_.empty()) { + DCHECK(id_map_.empty()); + DCHECK(id_map_inverse_.empty()); + return; + } + + sync_api::WriteTransaction trans( + sync_service_->backend()->GetUserShareHandle()); + DirtyAssociationsSyncIds::iterator iter; + for (iter = dirty_associations_sync_ids_.begin(); + iter != dirty_associations_sync_ids_.end(); + ++iter) { + int64 sync_id = *iter; + sync_api::WriteNode sync_node(&trans); + if (!sync_node.InitByIdLookup(sync_id)) { + error_handler_->OnUnrecoverableError(); + return; + } + } + dirty_associations_sync_ids_.clear(); +} + +// static +std::string AutofillModelAssociator::KeyToTag(const string16& name, + const string16& value) { + return EscapePath(UTF16ToUTF8(name) + "|" + + UTF16ToUTF8(value)); +} +// static +bool AutofillModelAssociator::MergeTimestamps( + const sync_pb::AutofillSpecifics& autofill, + const std::vector<base::Time>& timestamps, + std::vector<base::Time>* new_timestamps) { + DCHECK(new_timestamps); + std::set<base::Time> timestamp_union(timestamps.begin(), + timestamps.end()); + + size_t timestamps_count = autofill.usage_timestamp_size(); + + bool different = timestamps.size() != timestamps_count; + for (size_t c = 0; c < timestamps_count; ++c) { + if (timestamp_union.insert(base::Time::FromInternalValue( + autofill.usage_timestamp(c))).second) { + different = true; + } + } + + if (different) { + new_timestamps->insert(new_timestamps->begin(), + timestamp_union.begin(), + timestamp_union.end()); + } + return different; +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/glue/autofill_model_associator.h b/chrome/browser/sync/glue/autofill_model_associator.h new file mode 100755 index 0000000..d22fcba --- /dev/null +++ b/chrome/browser/sync/glue/autofill_model_associator.h @@ -0,0 +1,117 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_GLUE_AUTOFILL_MODEL_ASSOCIATOR_H_ +#define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_MODEL_ASSOCIATOR_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/sync/glue/model_associator.h" +#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" +#include "chrome/browser/webdata/autofill_entry.h" + +class ProfileSyncService; + +namespace browser_sync { + +class AutofillChangeProcessor; +class UnrecoverableErrorHandler; + +extern const char kAutofillTag[]; + +// Contains all model association related logic: +// * Algorithm to associate autofill model and sync model. +// * Persisting model associations and loading them back. +// We do not check if we have local data before this run; we always +// merge and sync. +class AutofillModelAssociator + : public PerDataTypeAssociatorInterface<AutofillKey, AutofillKey> { + public: + static syncable::ModelType model_type() { return syncable::AUTOFILL; } + AutofillModelAssociator(ProfileSyncService* sync_service, + UnrecoverableErrorHandler* error_handler); + virtual ~AutofillModelAssociator() { } + + // PerDataTypeAssociatorInterface implementation. + // + // Iterates through the sync model looking for matched pairs of items. + virtual bool AssociateModels(); + + // Clears all associations. + virtual bool DisassociateModels(); + + // Returns whether the sync model has nodes other than the permanent tagged + // nodes. + virtual bool SyncModelHasUserCreatedNodes(); + + // Returns whether the autofill model has any user-defined autofills. + virtual bool ChromeModelHasUserCreatedNodes(); + + // Not implemented. + virtual const AutofillKey* GetChromeNodeFromSyncId(int64 sync_id) { + return NULL; + } + + // Not implemented. + virtual bool InitSyncNodeFromChromeId(AutofillKey node_id, + sync_api::BaseNode* sync_node) { + return false; + } + + // Returns the sync id for the given autofill name, or sync_api::kInvalidId + // if the autofill name is not associated to any sync id. + virtual int64 GetSyncIdFromChromeId(AutofillKey node_id); + + // Associates the given autofill name with the given sync id. + virtual void Associate(const AutofillKey* node, int64 sync_id); + + // Remove the association that corresponds to the given sync id. + virtual void Disassociate(int64 sync_id); + + // Returns whether a node with the given permanent tag was found and update + // |sync_id| with that node's id. + bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id); + + static std::string KeyToTag(const string16& name, const string16& value); + static bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill, + const std::vector<base::Time>& timestamps, + std::vector<base::Time>* new_timestamps); + + private: + typedef std::map<AutofillKey, int64> AutofillToSyncIdMap; + typedef std::map<int64, AutofillKey> SyncIdToAutofillMap; + typedef std::set<int64> DirtyAssociationsSyncIds; + + // Posts a task to persist dirty associations. + void PostPersistAssociationsTask(); + + // Persists all dirty associations. + void PersistAssociations(); + + ProfileSyncService* sync_service_; + UnrecoverableErrorHandler* error_handler_; + int64 autofill_node_id_; + + AutofillToSyncIdMap id_map_; + SyncIdToAutofillMap id_map_inverse_; + // Stores the IDs of dirty associations. + DirtyAssociationsSyncIds dirty_associations_sync_ids_; + + // Used to post PersistAssociation tasks to the current message loop and + // guarantees no invocations can occur if |this| has been deleted. (This + // allows this class to be non-refcounted). + ScopedRunnableMethodFactory<AutofillModelAssociator> method_factory_; + + DISALLOW_COPY_AND_ASSIGN(AutofillModelAssociator); +}; + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_GLUE_AUTOFILL_MODEL_ASSOCIATOR_H_ diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc b/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc index 88c69b8..0fc7ed7 100644 --- a/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc +++ b/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc @@ -56,10 +56,10 @@ class BookmarkDataTypeControllerTest : public testing::Test { profile_sync_factory_.reset( new ProfileSyncFactoryMock(model_associator_, change_processor_)); - bookmark_dtc_.reset( + bookmark_dtc_ = new BookmarkDataTypeController(profile_sync_factory_.get(), &profile_, - &service_)); + &service_); } protected: @@ -87,7 +87,7 @@ class BookmarkDataTypeControllerTest : public testing::Test { MessageLoopForUI message_loop_; ChromeThread ui_thread_; - scoped_ptr<BookmarkDataTypeController> bookmark_dtc_; + scoped_refptr<BookmarkDataTypeController> bookmark_dtc_; scoped_ptr<ProfileSyncFactoryMock> profile_sync_factory_; ProfileMock profile_; BookmarkModelMock bookmark_model_; diff --git a/chrome/browser/sync/glue/data_type_controller.h b/chrome/browser/sync/glue/data_type_controller.h index 9103813..4f72ae2 100644 --- a/chrome/browser/sync/glue/data_type_controller.h +++ b/chrome/browser/sync/glue/data_type_controller.h @@ -13,7 +13,11 @@ namespace browser_sync { -class DataTypeController { +// Data type controllers need to be refcounted threadsafe, as they may +// need to run model associator or change processor on other threads. +class DataTypeController + : public base::RefCountedThreadSafe<DataTypeController, + ChromeThread::DeleteOnUIThread> { public: enum State { NOT_RUNNING, // The controller has never been started or has @@ -45,9 +49,8 @@ class DataTypeController { typedef Callback1<StartResult>::Type StartCallback; - typedef std::map<syncable::ModelType, DataTypeController*> TypeMap; - - virtual ~DataTypeController() {} + typedef std::map<syncable::ModelType, + scoped_refptr<DataTypeController> > TypeMap; // Begins asynchronous start up of this data type. Start up will // wait for all other dependent services to be available, then @@ -81,6 +84,17 @@ class DataTypeController { // Current state of the data type controller. virtual State state() = 0; + + // TODO(sync): Make this protected. It currently causes a build error on + // Linux to do so. + virtual ~DataTypeController() {} + + protected: + friend class base::RefCountedThreadSafe<DataTypeController>; + friend class ChromeThread; + friend class DeleteTask<DataTypeController>; + friend class ShutdownTask; + }; } // namespace browser_sync diff --git a/chrome/browser/sync/glue/data_type_manager_impl.cc b/chrome/browser/sync/glue/data_type_manager_impl.cc index 322c37f..1b84597 100644 --- a/chrome/browser/sync/glue/data_type_manager_impl.cc +++ b/chrome/browser/sync/glue/data_type_manager_impl.cc @@ -13,7 +13,8 @@ namespace { static const syncable::ModelType kStartOrder[] = { syncable::BOOKMARKS, - syncable::PREFERENCES + syncable::PREFERENCES, + syncable::AUTOFILL }; } // namespace @@ -104,7 +105,7 @@ void DataTypeManagerImpl::TypeStartCallback( LOG(INFO) << "Failed " << controllers_[type]->name(); FinishStop(); StartResult start_result = DataTypeManager::ABORTED; - switch(result) { + switch (result) { case DataTypeController::ABORTED: start_result = DataTypeManager::ABORTED; break; diff --git a/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc b/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc index 3c12dd5..60115b4 100644 --- a/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc +++ b/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc @@ -37,7 +37,6 @@ class DataTypeManagerImplTest : public testing::Test { : ui_thread_(ChromeThread::UI, &message_loop_) {} virtual ~DataTypeManagerImplTest() { - STLDeleteValues(&controllers_); } protected: diff --git a/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc b/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc index f50487a..108a2e8 100644 --- a/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc +++ b/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc @@ -34,9 +34,9 @@ class PreferenceDataTypeControllerTest : public testing::Test { virtual void SetUp() { profile_sync_factory_.reset(new ProfileSyncFactoryMock()); - preference_dtc_.reset( + preference_dtc_ = new PreferenceDataTypeController(profile_sync_factory_.get(), - &service_)); + &service_); } protected: @@ -65,7 +65,7 @@ class PreferenceDataTypeControllerTest : public testing::Test { MessageLoopForUI message_loop_; ChromeThread ui_thread_; - scoped_ptr<PreferenceDataTypeController> preference_dtc_; + scoped_refptr<PreferenceDataTypeController> preference_dtc_; scoped_ptr<ProfileSyncFactoryMock> profile_sync_factory_; ProfileSyncServiceMock service_; ModelAssociatorMock* model_associator_; diff --git a/chrome/browser/sync/profile_sync_factory.h b/chrome/browser/sync/profile_sync_factory.h index fbb60b2..c027b0f 100644 --- a/chrome/browser/sync/profile_sync_factory.h +++ b/chrome/browser/sync/profile_sync_factory.h @@ -15,6 +15,7 @@ class ProfileSyncService; namespace browser_sync { class DataTypeManager; +class UnrecoverableErrorHandler; } // Factory class for all profile sync related classes. @@ -45,6 +46,13 @@ class ProfileSyncFactory { const browser_sync::DataTypeController::TypeMap& controllers) = 0; // Instantiates both a model associator and change processor for the + // autofill data type. The pointers in the return struct are owned + // by the caller. + virtual SyncComponents CreateAutofillSyncComponents( + ProfileSyncService* profile_sync_service, + browser_sync::UnrecoverableErrorHandler* error_handler) = 0; + + // Instantiates both a model associator and change processor for the // bookmark data type. The pointers in the return struct are owned // by the caller. virtual SyncComponents CreateBookmarkSyncComponents( diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc index 49293a3..093cfbe 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_factory_impl.cc @@ -5,6 +5,9 @@ #include "base/command_line.h" #include "chrome/browser/defaults.h" #include "chrome/browser/profile.h" +#include "chrome/browser/sync/glue/autofill_change_processor.h" +#include "chrome/browser/sync/glue/autofill_data_type_controller.h" +#include "chrome/browser/sync/glue/autofill_model_associator.h" #include "chrome/browser/sync/glue/bookmark_change_processor.h" #include "chrome/browser/sync/glue/bookmark_data_type_controller.h" #include "chrome/browser/sync/glue/bookmark_model_associator.h" @@ -14,10 +17,15 @@ #include "chrome/browser/sync/glue/preference_model_associator.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_factory_impl.h" +#include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/chrome_switches.h" +using browser_sync::AutofillChangeProcessor; +using browser_sync::AutofillDataTypeController; +using browser_sync::AutofillModelAssociator; using browser_sync::BookmarkChangeProcessor; using browser_sync::BookmarkDataTypeController; +using browser_sync::BookmarkChangeProcessor; using browser_sync::BookmarkModelAssociator; using browser_sync::DataTypeController; using browser_sync::DataTypeManager; @@ -38,6 +46,13 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService() { profile_, browser_defaults::kBootstrapSyncAuthentication); + // Autofill sync is disabled by default. Register only if + // explicitly enabled. + if (command_line_->HasSwitch(switches::kEnableSyncAutofill)) { + pss->RegisterDataTypeController( + new AutofillDataTypeController(this, pss)); + } + // Bookmark sync is enabled by default. Register unless explicitly // disabled. if (!command_line_->HasSwitch(switches::kDisableSyncBookmarks)) { @@ -61,6 +76,19 @@ DataTypeManager* ProfileSyncFactoryImpl::CreateDataTypeManager( } ProfileSyncFactory::SyncComponents +ProfileSyncFactoryImpl::CreateAutofillSyncComponents( + ProfileSyncService* profile_sync_service, + browser_sync::UnrecoverableErrorHandler* error_handler) { + AutofillModelAssociator* model_associator = + new AutofillModelAssociator(profile_sync_service, + error_handler); + AutofillChangeProcessor* change_processor = + new AutofillChangeProcessor(model_associator, + error_handler); + return SyncComponents(model_associator, change_processor); +} + +ProfileSyncFactory::SyncComponents ProfileSyncFactoryImpl::CreateBookmarkSyncComponents( ProfileSyncService* profile_sync_service) { BookmarkModelAssociator* model_associator = diff --git a/chrome/browser/sync/profile_sync_factory_impl.h b/chrome/browser/sync/profile_sync_factory_impl.h index 7ff8fb03..d193a59 100644 --- a/chrome/browser/sync/profile_sync_factory_impl.h +++ b/chrome/browser/sync/profile_sync_factory_impl.h @@ -22,6 +22,10 @@ class ProfileSyncFactoryImpl : public ProfileSyncFactory { virtual browser_sync::DataTypeManager* CreateDataTypeManager( const browser_sync::DataTypeController::TypeMap& controllers); + virtual SyncComponents CreateAutofillSyncComponents( + ProfileSyncService* profile_sync_service, + browser_sync::UnrecoverableErrorHandler* error_handler); + virtual SyncComponents CreateBookmarkSyncComponents( ProfileSyncService* profile_sync_service); diff --git a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc index 2913c81..ce901f2 100644 --- a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc +++ b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc @@ -45,6 +45,16 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDefault) { EXPECT_EQ(1U, controllers.count(syncable::BOOKMARKS)); } +TEST_F(ProfileSyncFactoryImplTest, CreatePSSEnableAutofill) { + command_line_->AppendSwitch(switches::kEnableSyncAutofill); + scoped_ptr<ProfileSyncService> pss; + pss.reset(profile_sync_service_factory_->CreateProfileSyncService()); + DataTypeController::TypeMap controllers(pss->data_type_controllers()); + EXPECT_EQ(2U, controllers.size()); + EXPECT_EQ(1U, controllers.count(syncable::BOOKMARKS)); + EXPECT_EQ(1U, controllers.count(syncable::AUTOFILL)); +} + TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableBookmarks) { command_line_->AppendSwitch(switches::kDisableSyncBookmarks); scoped_ptr<ProfileSyncService> pss; diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h index 4257a0f..1e09271 100644 --- a/chrome/browser/sync/profile_sync_factory_mock.h +++ b/chrome/browser/sync/profile_sync_factory_mock.h @@ -28,6 +28,10 @@ class ProfileSyncFactoryMock : public ProfileSyncFactory { browser_sync::DataTypeManager*( const browser_sync::DataTypeController::TypeMap& controllers)); + MOCK_METHOD2(CreateAutofillSyncComponents, + SyncComponents( + ProfileSyncService* profile_sync_service, + browser_sync::UnrecoverableErrorHandler* error_handler)); MOCK_METHOD1(CreateBookmarkSyncComponents, SyncComponents(ProfileSyncService* profile_sync_service)); MOCK_METHOD1(CreatePreferenceSyncComponents, diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index 4f2913e..a9a9972 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -56,7 +56,11 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory, ProfileSyncService::~ProfileSyncService() { Shutdown(false); - STLDeleteValues(&data_type_controllers_); + for (DataTypeController::TypeMap::iterator ix = + data_type_controllers_.begin(); + ix != data_type_controllers_.end(); ++ix) { + ix->second->Release(); + } } void ProfileSyncService::Initialize() { @@ -80,6 +84,7 @@ void ProfileSyncService::RegisterDataTypeController( DCHECK(data_type_controllers_.count(data_type_controller->type()) == 0); data_type_controllers_[data_type_controller->type()] = data_type_controller; + data_type_controller->AddRef(); } void ProfileSyncService::InitSettings() { |