summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-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
-rw-r--r--chrome/browser/webdata/autofill_entry.cc15
-rw-r--r--chrome/browser/webdata/autofill_entry.h3
-rw-r--r--chrome/browser/webdata/web_data_service.cc49
-rw-r--r--chrome/browser/webdata/web_data_service.h3
-rw-r--r--chrome/browser/webdata/web_data_service_unittest.cc147
-rw-r--r--chrome/browser/webdata/web_database.cc29
-rw-r--r--chrome/browser/webdata/web_database.h9
-rw-r--r--chrome/browser/webdata/web_database_unittest.cc18
-rwxr-xr-xchrome/chrome_browser.gypi7
-rw-r--r--chrome/common/chrome_switches.cc6
-rw-r--r--chrome/common/chrome_switches.h2
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(),
+ &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() {
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"),
+ &timestamps));
+ 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[];