summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
authorzork@google.com <zork@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-01 22:06:21 +0000
committerzork@google.com <zork@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-01 22:06:21 +0000
commit8b3b23abcf91b7573c6fcf230240395118c04580 (patch)
tree3c4dc9c354662a6d9ecff8443f4620fa017c7718 /chrome/browser/sync
parent3950400350f7f8494f09a3568acb6d40592701f5 (diff)
downloadchromium_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')
-rwxr-xr-xchrome/browser/sync/engine/syncapi.cc19
-rwxr-xr-xchrome/browser/sync/engine/syncapi.h11
-rwxr-xr-xchrome/browser/sync/glue/autofill_change_processor.cc260
-rwxr-xr-xchrome/browser/sync/glue/autofill_change_processor.h74
-rwxr-xr-xchrome/browser/sync/glue/autofill_data_type_controller.cc172
-rwxr-xr-xchrome/browser/sync/glue/autofill_data_type_controller.h90
-rwxr-xr-xchrome/browser/sync/glue/autofill_model_associator.cc291
-rwxr-xr-xchrome/browser/sync/glue/autofill_model_associator.h117
-rw-r--r--chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc6
-rw-r--r--chrome/browser/sync/glue/data_type_controller.h22
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.cc5
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl_unittest.cc1
-rw-r--r--chrome/browser/sync/glue/preference_data_type_controller_unittest.cc6
-rw-r--r--chrome/browser/sync/profile_sync_factory.h8
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.cc28
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.h4
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl_unittest.cc10
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.h4
-rw-r--r--chrome/browser/sync/profile_sync_service.cc7
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(),
+ &timestamps)) {
+ 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(),
+ &timestamps)) {
+ 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(), &timestamps)) {
+ 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() {