diff options
Diffstat (limited to 'chrome')
30 files changed, 1347 insertions, 76 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() { diff --git a/chrome/browser/webdata/autofill_entry.cc b/chrome/browser/webdata/autofill_entry.cc index 08f7557..d0f8913 100644 --- a/chrome/browser/webdata/autofill_entry.cc +++ b/chrome/browser/webdata/autofill_entry.cc @@ -9,6 +9,17 @@ bool AutofillKey::operator==(const AutofillKey& key) const { return name_ == key.name() && value_ == key.value(); } +bool AutofillKey::operator<(const AutofillKey& key) const { + int diff = name_.compare(key.name()); + if (diff < 0) { + return true; + } else if (diff == 0) { + return value_.compare(key.value()) < 0; + } else { + return false; + } +} + bool AutofillEntry::operator==(const AutofillEntry& entry) const { if (!(key_ == entry.key())) return false; @@ -25,3 +36,7 @@ bool AutofillEntry::operator==(const AutofillEntry& entry) const { return true; } + +bool AutofillEntry::operator<(const AutofillEntry& entry) const { + return key_ < entry.key(); +} diff --git a/chrome/browser/webdata/autofill_entry.h b/chrome/browser/webdata/autofill_entry.h index 850f93b..d67a090 100644 --- a/chrome/browser/webdata/autofill_entry.h +++ b/chrome/browser/webdata/autofill_entry.h @@ -11,6 +11,7 @@ class AutofillKey { public: + AutofillKey() {} AutofillKey(const string16& name, const string16& value) : name_(name), value_(value) {} @@ -23,6 +24,7 @@ class AutofillKey { const string16& value() const { return value_; } bool operator==(const AutofillKey& key) const; + bool operator<(const AutofillKey& key) const; private: string16 name_; @@ -40,6 +42,7 @@ class AutofillEntry { const std::vector<base::Time>& timestamps() const { return timestamps_; } bool operator==(const AutofillEntry& entry) const; + bool operator<(const AutofillEntry& entry) const; private: AutofillKey key_; diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc index 197b859..68c330c 100644 --- a/chrome/browser/webdata/web_data_service.cc +++ b/chrome/browser/webdata/web_data_service.cc @@ -225,6 +225,15 @@ WebDataService::Handle WebDataService::GetCreditCards( return request->GetHandle(); } +bool WebDataService::IsDatabaseLoaded() { + return db_ != NULL; +} + +WebDatabase* WebDataService::GetDatabase() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + return db_; +} + void WebDataService::RequestCompleted(Handle h) { pending_lock_.Acquire(); RequestMap::iterator i = pending_requests_.find(h); @@ -245,20 +254,6 @@ void WebDataService::RequestCompleted(Handle h) { consumer->OnWebDataServiceRequestDone(request->GetHandle(), request->GetResult()); } - - // If this is an autofill change request, post the notifications - // including the list of affected keys. - const WDTypedResult* result = request->GetResult(); - if (!request->IsCancelled() && result) { - if (result->GetType() == AUTOFILL_CHANGES) { - AutofillChangeList changes = - static_cast<const WDResult<AutofillChangeList>*>(result)->GetValue(); - NotificationService::current()->Notify( - NotificationType::AUTOFILL_ENTRIES_CHANGED, - NotificationService::AllSources(), - Details<AutofillChangeList>(&changes)); - } - } } ////////////////////////////////////////////////////////////////////////////// @@ -708,7 +703,16 @@ void WebDataService::AddFormFieldValuesImpl( request->SetResult( new WDResult<AutofillChangeList>(AUTOFILL_CHANGES, changes)); ScheduleCommit(); + + // Post the notifications including the list of affected keys. + // This is sent here so that work resulting from this notification will be + // done on the DB thread, and not the UI thread. + NotificationService::current()->Notify( + NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources(), + Details<AutofillChangeList>(&changes)); } + request->RequestComplete(); } @@ -719,8 +723,7 @@ void WebDataService::GetFormValuesForElementNameImpl(WebDataRequest* request, std::vector<string16> values; db_->GetFormValuesForElementName(name, prefix, &values, limit); request->SetResult( - new WDResult<std::vector<string16> >(AUTOFILL_VALUE_RESULT, - values)); + new WDResult<std::vector<string16> >(AUTOFILL_VALUE_RESULT, values)); } request->RequestComplete(); } @@ -736,6 +739,14 @@ void WebDataService::RemoveFormElementsAddedBetweenImpl( if (changes.size() > 0) { request->SetResult( new WDResult<AutofillChangeList>(AUTOFILL_CHANGES, changes)); + + // Post the notifications including the list of affected keys. + // This is sent here so that work resulting from this notification + // will be done on the DB thread, and not the UI thread. + NotificationService::current()->Notify( + NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources(), + Details<AutofillChangeList>(&changes)); } ScheduleCommit(); } @@ -757,6 +768,12 @@ void WebDataService::RemoveFormValueForElementNameImpl( request->SetResult( new WDResult<AutofillChangeList>(AUTOFILL_CHANGES, changes)); ScheduleCommit(); + + // Post the notifications including the list of affected keys. + NotificationService::current()->Notify( + NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources(), + Details<AutofillChangeList>(&changes)); } } request->RequestComplete(); diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h index c81061d..c6887f4 100644 --- a/chrome/browser/webdata/web_data_service.h +++ b/chrome/browser/webdata/web_data_service.h @@ -448,6 +448,9 @@ class WebDataService void set_failed_init(bool value) { failed_init_ = value; } #endif + bool IsDatabaseLoaded(); + WebDatabase* GetDatabase(); + protected: friend class TemplateURLModelTest; friend class TemplateURLModelTestingProfile; diff --git a/chrome/browser/webdata/web_data_service_unittest.cc b/chrome/browser/webdata/web_data_service_unittest.cc index 34092b8..96f2b6e 100644 --- a/chrome/browser/webdata/web_data_service_unittest.cc +++ b/chrome/browser/webdata/web_data_service_unittest.cc @@ -8,10 +8,12 @@ #include "base/file_util.h" #include "base/message_loop.h" #include "base/path_service.h" +#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/string16.h" #include "base/string_util.h" #include "base/time.h" +#include "base/waitable_event.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/webdata/autofill_change.h" #include "chrome/browser/webdata/autofill_entry.h" @@ -29,6 +31,7 @@ using base::Time; using base::TimeDelta; +using base::WaitableEvent; using testing::_; using testing::ElementsAreArray; using testing::Pointee; @@ -36,9 +39,8 @@ using testing::Property; typedef std::vector<AutofillChange> AutofillChangeList; -ACTION(QuitUIMessageLoop) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - MessageLoop::current()->Quit(); +ACTION_P(SignalEvent, event) { + event->Signal(); } class AutofillWebDataServiceConsumer: public WebDataServiceConsumer { @@ -69,6 +71,49 @@ class AutofillWebDataServiceConsumer: public WebDataServiceConsumer { DISALLOW_COPY_AND_ASSIGN(AutofillWebDataServiceConsumer); }; +// This class will add and remove a mock notification observer from +// the DB thread. +class DBThreadObserverHelper : + public base::RefCountedThreadSafe<DBThreadObserverHelper, + ChromeThread::DeleteOnDBThread> { + public: + DBThreadObserverHelper() : done_event_(true, false) {} + + void Init() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + ChromeThread::PostTask( + ChromeThread::DB, + FROM_HERE, + NewRunnableMethod(this, &DBThreadObserverHelper::AddObserverTask)); + done_event_.Wait(); + } + + virtual ~DBThreadObserverHelper() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + registrar_.Remove(&observer_, + NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources()); + } + + NotificationObserverMock* observer() { + return &observer_; + } + + private: + friend class base::RefCountedThreadSafe<DBThreadObserverHelper>; + + void AddObserverTask() { + registrar_.Add(&observer_, + NotificationType::AUTOFILL_ENTRIES_CHANGED, + NotificationService::AllSources()); + done_event_.Signal(); + } + + WaitableEvent done_event_; + NotificationRegistrar registrar_; + NotificationObserverMock observer_; +}; + class WebDataServiceTest : public testing::Test { public: WebDataServiceTest() @@ -78,10 +123,6 @@ class WebDataServiceTest : public testing::Test { protected: virtual void SetUp() { db_thread_.Start(); - name1_ = ASCIIToUTF16("name1"); - name2_ = ASCIIToUTF16("name2"); - value1_ = ASCIIToUTF16("value1"); - value2_ = ASCIIToUTF16("value2"); PathService::Get(chrome::DIR_TEST_DATA, &profile_dir_); const std::string test_profile = "WebDataServiceTest"; @@ -102,6 +143,35 @@ class WebDataServiceTest : public testing::Test { MessageLoop::current()->Run(); } + MessageLoopForUI message_loop_; + ChromeThread ui_thread_; + ChromeThread db_thread_; + FilePath profile_dir_; + scoped_refptr<WebDataService> wds_; +}; + +class WebDataServiceAutofillTest : public WebDataServiceTest { + public: + WebDataServiceAutofillTest() + : WebDataServiceTest(), done_event_(true, false) {} + + protected: + virtual void SetUp() { + WebDataServiceTest::SetUp(); + name1_ = ASCIIToUTF16("name1"); + name2_ = ASCIIToUTF16("name2"); + value1_ = ASCIIToUTF16("value1"); + value2_ = ASCIIToUTF16("value2"); + observer_helper_ = new DBThreadObserverHelper(); + observer_helper_->Init(); + } + + virtual void TearDown() { + // Release this first so it can get destructed on the db thread. + observer_helper_ = NULL; + WebDataServiceTest::TearDown(); + } + void AppendFormField(const string16& name, const string16& value, std::vector<webkit_glue::FormField>* form_fields) { @@ -113,20 +183,15 @@ class WebDataServiceTest : public testing::Test { WebKit::WebInputElement::Text)); } - MessageLoopForUI message_loop_; - ChromeThread ui_thread_; - ChromeThread db_thread_; string16 name1_; string16 name2_; string16 value1_; string16 value2_; - FilePath profile_dir_; - scoped_refptr<WebDataService> wds_; - NotificationRegistrar registrar_; - NotificationObserverMock observer_; + scoped_refptr<DBThreadObserverHelper> observer_helper_; + WaitableEvent done_event_; }; -TEST_F(WebDataServiceTest, AutofillAdd) { +TEST_F(WebDataServiceAutofillTest, Add) { const AutofillChange expected_changes[] = { AutofillChange(AutofillChange::ADD, AutofillKey(name1_, value1_)), AutofillChange(AutofillChange::ADD, AutofillKey(name2_, value2_)) @@ -135,24 +200,20 @@ TEST_F(WebDataServiceTest, AutofillAdd) { // This will verify that the correct notification is triggered, // passing the correct list of autofill keys in the details. EXPECT_CALL( - observer_, + *observer_helper_->observer(), Observe(NotificationType(NotificationType::AUTOFILL_ENTRIES_CHANGED), NotificationService::AllSources(), Property(&Details<const AutofillChangeList>::ptr, Pointee(ElementsAreArray(expected_changes))))). - WillOnce(QuitUIMessageLoop()); - - registrar_.Add(&observer_, - NotificationType::AUTOFILL_ENTRIES_CHANGED, - NotificationService::AllSources()); + WillOnce(SignalEvent(&done_event_)); std::vector<webkit_glue::FormField> form_fields; AppendFormField(name1_, value1_, &form_fields); AppendFormField(name2_, value2_, &form_fields); wds_->AddFormFieldValues(form_fields); - // The message loop will exit when the mock observer is notified. - MessageLoop::current()->Run(); + // The event will be signaled when the mock observer is notified. + done_event_.Wait(); AutofillWebDataServiceConsumer consumer; WebDataService::Handle handle; @@ -168,18 +229,16 @@ TEST_F(WebDataServiceTest, AutofillAdd) { EXPECT_EQ(value1_, consumer.values()[0]); } -TEST_F(WebDataServiceTest, AutofillRemoveOne) { +TEST_F(WebDataServiceAutofillTest, RemoveOne) { // First add some values to autofill. - EXPECT_CALL(observer_, Observe(_, _, _)).WillOnce(QuitUIMessageLoop()); - registrar_.Add(&observer_, - NotificationType::AUTOFILL_ENTRIES_CHANGED, - NotificationService::AllSources()); + EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). + WillOnce(SignalEvent(&done_event_)); std::vector<webkit_glue::FormField> form_fields; AppendFormField(name1_, value1_, &form_fields); wds_->AddFormFieldValues(form_fields); - // The message loop will exit when the mock observer is notified. - MessageLoop::current()->Run(); + // The event will be signaled when the mock observer is notified. + done_event_.Wait(); // This will verify that the correct notification is triggered, // passing the correct list of autofill keys in the details. @@ -187,33 +246,31 @@ TEST_F(WebDataServiceTest, AutofillRemoveOne) { AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)) }; EXPECT_CALL( - observer_, + *observer_helper_->observer(), Observe(NotificationType(NotificationType::AUTOFILL_ENTRIES_CHANGED), NotificationService::AllSources(), Property(&Details<const AutofillChangeList>::ptr, Pointee(ElementsAreArray(expected_changes))))). - WillOnce(QuitUIMessageLoop()); + WillOnce(SignalEvent(&done_event_)); wds_->RemoveFormValueForElementName(name1_, value1_); - // The message loop will exit when the mock observer is notified. - MessageLoop::current()->Run(); + // The event will be signaled when the mock observer is notified. + done_event_.Wait(); } -TEST_F(WebDataServiceTest, AutofillRemoveMany) { +TEST_F(WebDataServiceAutofillTest,RemoveMany) { TimeDelta one_day(TimeDelta::FromDays(1)); Time t = Time::Now(); - EXPECT_CALL(observer_, Observe(_, _, _)).WillOnce(QuitUIMessageLoop()); - registrar_.Add(&observer_, - NotificationType::AUTOFILL_ENTRIES_CHANGED, - NotificationService::AllSources()); + EXPECT_CALL(*observer_helper_->observer(), Observe(_, _, _)). + WillOnce(SignalEvent(&done_event_)); std::vector<webkit_glue::FormField> form_fields; AppendFormField(name1_, value1_, &form_fields); AppendFormField(name2_, value2_, &form_fields); wds_->AddFormFieldValues(form_fields); - // The message loop will exit when the mock observer is notified. - MessageLoop::current()->Run(); + // The event will be signaled when the mock observer is notified. + done_event_.Wait(); // This will verify that the correct notification is triggered, // passing the correct list of autofill keys in the details. @@ -222,14 +279,14 @@ TEST_F(WebDataServiceTest, AutofillRemoveMany) { AutofillChange(AutofillChange::REMOVE, AutofillKey(name2_, value2_)) }; EXPECT_CALL( - observer_, + *observer_helper_->observer(), Observe(NotificationType(NotificationType::AUTOFILL_ENTRIES_CHANGED), NotificationService::AllSources(), Property(&Details<const AutofillChangeList>::ptr, Pointee(ElementsAreArray(expected_changes))))). - WillOnce(QuitUIMessageLoop()); + WillOnce(SignalEvent(&done_event_)); wds_->RemoveFormElementsAddedBetween(t, t + one_day); - // The message loop will exit when the mock observer is notified. - MessageLoop::current()->Run(); + // The event will be signaled when the mock observer is notified. + done_event_.Wait(); } diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc index 7f8c3c7..d87ef0b 100644 --- a/chrome/browser/webdata/web_database.cc +++ b/chrome/browser/webdata/web_database.cc @@ -21,6 +21,7 @@ #include "chrome/browser/diagnostics/sqlite_diagnostics.h" #include "chrome/browser/history/history_database.h" #include "chrome/browser/webdata/autofill_change.h" +#include "chrome/common/notification_service.h" #include "webkit/glue/password_form.h" // Encryptor is the *wrong* way of doing things; we need to turn it into a @@ -180,6 +181,11 @@ void WebDatabase::CommitTransaction() { } sql::InitStatus WebDatabase::Init(const FilePath& db_name) { + // When running in unit tests, there is already a NotificationService object. + // Since only one can exist at a time per thread, check first. + if (!NotificationService::current()) + notification_service_.reset(new NotificationService); + // Set the exceptional sqlite error handler. db_.set_error_delegate(GetErrorHandlerForWebDb()); @@ -1041,6 +1047,29 @@ bool WebDatabase::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { return s.Succeeded(); } +bool WebDatabase::GetAutofillTimestamps(const string16& name, + const string16& value, + std::vector<base::Time>* timestamps) { + DCHECK(timestamps); + sql::Statement s(db_.GetUniqueStatement( + "SELECT date_created FROM autofill a JOIN " + "autofill_dates ad ON a.pair_id=ad.pair_id " + "WHERE a.name = ? AND a.value = ?")); + + if (!s) { + NOTREACHED() << "Statement prepare failed"; + return false; + } + + s.BindString(0, UTF16ToUTF8(name)); + s.BindString(1, UTF16ToUTF8(value)); + while (s.Step()) { + timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0))); + } + + return s.Succeeded(); +} + bool WebDatabase::UpdateAutofillEntries( const std::vector<AutofillEntry>& entries) { if (!entries.size()) diff --git a/chrome/browser/webdata/web_database.h b/chrome/browser/webdata/web_database.h index 6745503..6d1a99d 100644 --- a/chrome/browser/webdata/web_database.h +++ b/chrome/browser/webdata/web_database.h @@ -10,6 +10,7 @@ #include "app/sql/connection.h" #include "app/sql/init_status.h" #include "app/sql/meta_table.h" +#include "base/scoped_ptr.h" #include "chrome/browser/search_engines/template_url.h" #include "third_party/skia/include/core/SkBitmap.h" #include "testing/gtest/include/gtest/gtest_prod.h" @@ -20,6 +21,7 @@ class AutofillEntry; class AutoFillProfile; class CreditCard; class FilePath; +class NotificationService; class WebDatabaseTest; namespace base { @@ -204,6 +206,11 @@ class WebDatabase { // Retrieves all of the entries in the autofill table. bool GetAllAutofillEntries(std::vector<AutofillEntry>* entries); + // Retrieves a single entry from the autofill table. + bool GetAutofillTimestamps(const string16& name, + const string16& value, + std::vector<base::Time>* timestamps); + // Replaces existing autofill entries with the entries supplied in // the argument. If the entry does not already exist, it will be // added. @@ -298,6 +305,8 @@ class WebDatabase { sql::Connection db_; sql::MetaTable meta_table_; + scoped_ptr<NotificationService> notification_service_; + DISALLOW_COPY_AND_ASSIGN(WebDatabase); }; diff --git a/chrome/browser/webdata/web_database_unittest.cc b/chrome/browser/webdata/web_database_unittest.cc index 52d6f41..9a84816 100644 --- a/chrome/browser/webdata/web_database_unittest.cc +++ b/chrome/browser/webdata/web_database_unittest.cc @@ -791,6 +791,24 @@ TEST_F(WebDatabaseTest, Autofill_UpdateOneWithTwoTimestamps) { EXPECT_TRUE(entry == all_entries[0]); } +TEST_F(WebDatabaseTest, Autofill_GetAutofillTimestamps) { + WebDatabase db; + ASSERT_EQ(sql::INIT_OK, db.Init(file_)); + + AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2)); + std::vector<AutofillEntry> entries; + entries.push_back(entry); + ASSERT_TRUE(db.UpdateAutofillEntries(entries)); + + std::vector<base::Time> timestamps; + ASSERT_TRUE(db.GetAutofillTimestamps(ASCIIToUTF16("foo"), + ASCIIToUTF16("bar"), + ×tamps)); + ASSERT_EQ(2U, timestamps.size()); + EXPECT_TRUE(Time::FromTimeT(1) == timestamps[0]); + EXPECT_TRUE(Time::FromTimeT(2) == timestamps[1]); +} + TEST_F(WebDatabaseTest, Autofill_UpdateTwo) { WebDatabase db; ASSERT_EQ(sql::INIT_OK, db.Init(file_)); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 50e60f4e..677af2c 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1768,7 +1768,12 @@ 'browser/transport_security_persister.cc', 'browser/transport_security_persister.h', 'browser/sync/engine/syncapi.h', - 'browser/sync/glue/bookmark_change_processor.h', + 'browser/sync/glue/autofill_change_processor.h', + 'browser/sync/glue/autofill_change_processor.cc', + 'browser/sync/glue/autofill_data_type_controller.cc', + 'browser/sync/glue/autofill_data_type_controller.h', + 'browser/sync/glue/autofill_model_associator.h', + 'browser/sync/glue/autofill_model_associator.cc', 'browser/sync/glue/bookmark_change_processor.cc', 'browser/sync/glue/bookmark_change_processor.h', 'browser/sync/glue/bookmark_data_type_controller.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index c521395..fc060f1 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -161,6 +161,9 @@ const char kDisableSiteSpecificQuirks[] = "disable-site-specific-quirks"; // Disable syncing browser data to a Google Account. const char kDisableSync[] = "disable-sync"; +// Disable syncing of autofill. +const char kDisableSyncAutofill[] = "disable-sync-autofill"; + // Disable syncing of bookmarks. const char kDisableSyncBookmarks[] = "disable-sync-bookmarks"; @@ -281,6 +284,9 @@ const char kEnableStatsTable[] = "enable-stats-table"; // Enable syncing browser data to a Google Account. const char kEnableSync[] = "enable-sync"; +// Enable syncing browser autofill. +const char kEnableSyncAutofill[] = "enable-sync-autofill"; + // Enable syncing browser bookmarks. const char kEnableSyncBookmarks[] = "enable-sync-bookmarks"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 0ce84a6..d9c1b7e 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -61,6 +61,7 @@ extern const char kDisableSessionStorage[]; extern const char kDisableSharedWorkers[]; extern const char kDisableSiteSpecificQuirks[]; extern const char kDisableSync[]; +extern const char kDisableSyncAutofill[]; extern const char kDisableSyncBookmarks[]; extern const char kDisableSyncPreferences[]; extern const char kDisableWebResources[]; @@ -98,6 +99,7 @@ extern const char kEnableRendererAccessibility[]; extern const char kEnableSeccompSandbox[]; extern const char kEnableStatsTable[]; extern const char kEnableSync[]; +extern const char kEnableSyncAutofill[]; extern const char kEnableSyncBookmarks[]; extern const char kEnableSyncPreferences[]; extern const char kEnableUserDataDirProfiles[]; |