summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r--chrome/browser/sync/engine/read_node_mock.h29
-rw-r--r--chrome/browser/sync/engine/syncapi.cc11
-rw-r--r--chrome/browser/sync/engine/syncapi.h24
-rw-r--r--chrome/browser/sync/engine/syncapi_mock.h26
-rw-r--r--chrome/browser/sync/glue/autofill_change_processor.cc201
-rw-r--r--chrome/browser/sync/glue/autofill_change_processor.h35
-rw-r--r--chrome/browser/sync/glue/autofill_change_processor2.cc584
-rw-r--r--chrome/browser/sync/glue/autofill_change_processor2.h172
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator.cc192
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator.h31
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator2.cc547
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator2.h212
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator_unittest.cc12
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator.cc371
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator.h199
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc261
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.cc38
-rw-r--r--chrome/browser/sync/profile_sync_service_autofill_unittest.cc26
-rw-r--r--chrome/browser/sync/syncable/model_type.cc12
-rw-r--r--chrome/browser/sync/syncable/model_type.h2
-rw-r--r--chrome/browser/sync/syncable/syncable.cc20
-rw-r--r--chrome/browser/sync/syncable/syncable.h18
-rw-r--r--chrome/browser/sync/syncable/syncable_mock.h40
23 files changed, 2705 insertions, 358 deletions
diff --git a/chrome/browser/sync/engine/read_node_mock.h b/chrome/browser/sync/engine/read_node_mock.h
new file mode 100644
index 0000000..f13eddd
--- /dev/null
+++ b/chrome/browser/sync/engine/read_node_mock.h
@@ -0,0 +1,29 @@
+// 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_ENGINE_READ_NODE_MOCK_H_
+#define CHROME_BROWSER_SYNC_ENGINE_READ_NODE_MOCK_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+class ReadNodeMock : public sync_api::ReadNode {
+ public:
+ ReadNodeMock() { }
+ virtual ~ReadNodeMock() {}
+ MOCK_METHOD2(InitByClientTagLookup,
+ bool(syncable::ModelType model_type, const std::string& tag));
+ MOCK_CONST_METHOD0(GetAutofillProfileSpecifics,
+ const sync_pb::AutofillProfileSpecifics&());
+ MOCK_CONST_METHOD0(GetId, int64());
+ MOCK_CONST_METHOD0(GetFirstChildId, int64());
+ MOCK_CONST_METHOD0(GetFirstChild, int64());
+ MOCK_CONST_METHOD0(GetSuccessorId, int64());
+ MOCK_METHOD1(InitByIdLookup, bool(int64 id));
+};
+#endif // CHROME_BROWSER_SYNC_ENGINE_READ_NODE_MOCK_H_
+
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index 5a71eb8..bc34982 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -83,6 +83,7 @@ using syncable::Directory;
using syncable::DirectoryManager;
using syncable::Entry;
using syncable::SPECIFICS;
+using sync_pb::AutofillProfileSpecifics;
typedef GoogleServiceAuthError AuthError;
@@ -281,6 +282,11 @@ const sync_pb::AutofillSpecifics& BaseNode::GetAutofillSpecifics() const {
return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::autofill);
}
+const AutofillProfileSpecifics& BaseNode::GetAutofillProfileSpecifics() const {
+ DCHECK_EQ(GetModelType(), syncable::AUTOFILL_PROFILE);
+ return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::autofill_profile);
+}
+
const sync_pb::BookmarkSpecifics& BaseNode::GetBookmarkSpecifics() const {
DCHECK(GetModelType() == syncable::BOOKMARKS);
return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::bookmark);
@@ -757,6 +763,11 @@ ReadNode::ReadNode(const BaseTransaction* transaction)
DCHECK(transaction);
}
+ReadNode::ReadNode() {
+ entry_ = NULL;
+ transaction_ = NULL;
+}
+
ReadNode::~ReadNode() {
delete entry_;
}
diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h
index 95facaf..2019f41 100644
--- a/chrome/browser/sync/engine/syncapi.h
+++ b/chrome/browser/sync/engine/syncapi.h
@@ -81,6 +81,7 @@ class WriteTransaction;
namespace sync_pb {
class AppSpecifics;
class AutofillSpecifics;
+class AutofillProfileSpecifics;
class BookmarkSpecifics;
class EntitySpecifics;
class ExtensionSpecifics;
@@ -149,7 +150,7 @@ class BaseNode {
// metahandle). These ids are strictly local handles. They will persist
// on this client, but the same object on a different client may have a
// different ID value.
- int64 GetId() const;
+ virtual int64 GetId() const;
// Nodes are hierarchically arranged into a single-rooted tree.
// InitByRootLookup on ReadNode allows access to the root. GetParentId is
@@ -195,6 +196,9 @@ class BaseNode {
// data. Can only be called if GetModelType() == AUTOFILL.
const sync_pb::AutofillSpecifics& GetAutofillSpecifics() const;
+ virtual const sync_pb::AutofillProfileSpecifics&
+ GetAutofillProfileSpecifics() const;
+
// Getter specific to the NIGORI datatype. Returns protobuf
// data. Can only be called if GetModelType() == NIGORI.
const sync_pb::NigoriSpecifics& GetNigoriSpecifics() const;
@@ -232,11 +236,11 @@ class BaseNode {
// Return the ID of the node immediately after this in the sibling order.
// For the last node in the ordering, return 0.
- int64 GetSuccessorId() const;
+ virtual int64 GetSuccessorId() const;
// Return the ID of the first child of this node. If this node has no
// children, return 0.
- int64 GetFirstChildId() const;
+ virtual int64 GetFirstChildId() const;
// These virtual accessors provide access to data members of derived classes.
virtual const syncable::Entry* GetEntry() const = 0;
@@ -256,8 +260,7 @@ class BaseNode {
bool DecryptIfNecessary(syncable::Entry* entry);
private:
- // Node is meant for stack use only.
- void* operator new(size_t size);
+ void* operator new(size_t size); // Node is meant for stack use only.
// If this node represents a password, this field will hold the actual
// decrypted password data.
@@ -457,6 +460,9 @@ class ReadNode : public BaseNode {
virtual const syncable::Entry* GetEntry() const;
virtual const BaseTransaction* GetTransaction() const;
+ protected:
+ ReadNode();
+
private:
void* operator new(size_t size); // Node is meant for stack use only.
@@ -492,6 +498,8 @@ class BaseTransaction {
explicit BaseTransaction(UserShare* share);
virtual ~BaseTransaction();
+ BaseTransaction() { lookup_= NULL; }
+
private:
// A syncable ScopedDirLookup, which is the parent of syncable transactions.
syncable::ScopedDirLookup* lookup_;
@@ -537,6 +545,12 @@ class WriteTransaction : public BaseTransaction {
virtual syncable::BaseTransaction* GetWrappedTrans() const;
syncable::WriteTransaction* GetWrappedWriteTrans() { return transaction_; }
+ protected:
+ WriteTransaction() {}
+
+ void SetTransaction(syncable::WriteTransaction* trans) {
+ transaction_ = trans;}
+
private:
void* operator new(size_t size); // Transaction is meant for stack use only.
diff --git a/chrome/browser/sync/engine/syncapi_mock.h b/chrome/browser/sync/engine/syncapi_mock.h
new file mode 100644
index 0000000..ada0a56
--- /dev/null
+++ b/chrome/browser/sync/engine/syncapi_mock.h
@@ -0,0 +1,26 @@
+// 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_ENGINE_SYNCAPI_MOCK_H_
+#define CHROME_BROWSER_SYNC_ENGINE_SYNCAPI_MOCK_H_
+#pragma once
+
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/browser/sync/syncable/syncable_mock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_api::WriteTransaction;
+
+class MockWriteTransaction : public sync_api::WriteTransaction {
+ public:
+ explicit MockWriteTransaction(Directory* directory)
+ : sync_api::WriteTransaction() {
+ this->SetTransaction(new MockSyncableWriteTransaction(directory));
+ }
+};
+
+#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCAPI_MOCK_H_
+
diff --git a/chrome/browser/sync/glue/autofill_change_processor.cc b/chrome/browser/sync/glue/autofill_change_processor.cc
index d93f25d..d415950 100644
--- a/chrome/browser/sync/glue/autofill_change_processor.cc
+++ b/chrome/browser/sync/glue/autofill_change_processor.cc
@@ -9,8 +9,10 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/guid.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/sync/glue/autofill_change_processor2.h"
#include "chrome/browser/sync/glue/autofill_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/webdata/autofill_change.h"
@@ -73,62 +75,10 @@ void AutofillChangeProcessor::Observe(NotificationType type,
return;
}
- switch (type.value) {
- case NotificationType::AUTOFILL_ENTRIES_CHANGED: {
- AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr();
- ObserveAutofillEntriesChanged(changes, &trans, autofill_root);
- break;
- }
- case NotificationType::AUTOFILL_PROFILE_CHANGED: {
- AutofillProfileChange* change =
- Details<AutofillProfileChange>(details).ptr();
- ObserveAutofillProfileChanged(change, &trans, autofill_root);
- break;
- }
- default:
- NOTREACHED() << "Invalid NotificationType.";
- }
-}
+ DCHECK(type.value == NotificationType::AUTOFILL_ENTRIES_CHANGED);
-void AutofillChangeProcessor::ChangeProfileLabelIfAlreadyTaken(
- sync_api::BaseTransaction* trans,
- const string16& pre_update_label,
- AutoFillProfile* profile,
- std::string* tag) {
- DCHECK_EQ(AutofillModelAssociator::ProfileLabelToTag(profile->Label()),
- *tag);
- sync_api::ReadNode read_node(trans);
- if (!pre_update_label.empty() && pre_update_label == profile->Label())
- return;
- if (read_node.InitByClientTagLookup(syncable::AUTOFILL, *tag)) {
- // Handle the edge case of duplicate labels.
- string16 new_label(AutofillModelAssociator::MakeUniqueLabel(
- profile->Label(), pre_update_label, trans));
- if (new_label.empty()) {
- error_handler()->OnUnrecoverableError(FROM_HERE,
- "No unique label; can't move aside");
- return;
- }
- OverrideProfileLabel(new_label, profile, tag);
- }
-}
-
-void AutofillChangeProcessor::OverrideProfileLabel(
- const string16& new_label,
- AutoFillProfile* profile_to_update,
- std::string* tag_to_update) {
- tag_to_update->assign(AutofillModelAssociator::ProfileLabelToTag(new_label));
-
- profile_to_update->set_label(new_label);
- if (!web_database_->UpdateAutoFillProfile(*profile_to_update)) {
- std::string err = "Failed to overwrite label for node ";
- err += UTF16ToUTF8(new_label);
- error_handler()->OnUnrecoverableError(FROM_HERE, err);
- return;
- }
-
- // Notify the PersonalDataManager that it's out of date.
- PostOptimisticRefreshTask();
+ AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr();
+ ObserveAutofillEntriesChanged(changes, &trans, autofill_root);
}
void AutofillChangeProcessor::PostOptimisticRefreshTask() {
@@ -137,71 +87,6 @@ void AutofillChangeProcessor::PostOptimisticRefreshTask() {
personal_data_));
}
-void AutofillChangeProcessor::AddAutofillProfileSyncNode(
- sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill,
- const std::string& tag, const AutoFillProfile* profile) {
- sync_api::WriteNode sync_node(trans);
- if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL, autofill, tag)) {
- error_handler()->OnUnrecoverableError(FROM_HERE,
- "Failed to create autofill sync node.");
- return;
- }
- sync_node.SetTitle(UTF8ToWide(tag));
-
- WriteAutofillProfile(*profile, &sync_node);
- model_associator_->Associate(&tag, sync_node.GetId());
-}
-
-void AutofillChangeProcessor::ObserveAutofillProfileChanged(
- AutofillProfileChange* change, sync_api::WriteTransaction* trans,
- const sync_api::ReadNode& autofill_root) {
- std::string tag(AutofillModelAssociator::ProfileLabelToTag(change->key()));
- switch (change->type()) {
- case AutofillProfileChange::ADD: {
- scoped_ptr<AutoFillProfile> clone(
- static_cast<AutoFillProfile*>(change->profile()->Clone()));
- DCHECK_EQ(clone->Label(), change->key());
- ChangeProfileLabelIfAlreadyTaken(trans, string16(), clone.get(), &tag);
- AddAutofillProfileSyncNode(trans, autofill_root, tag, clone.get());
- break;
- }
- case AutofillProfileChange::UPDATE: {
- scoped_ptr<AutoFillProfile> clone(
- static_cast<AutoFillProfile*>(change->profile()->Clone()));
- std::string pre_update_tag = AutofillModelAssociator::ProfileLabelToTag(
- change->pre_update_label());
- DCHECK_EQ(clone->Label(), change->key());
- sync_api::WriteNode sync_node(trans);
- ChangeProfileLabelIfAlreadyTaken(trans, change->pre_update_label(),
- clone.get(), &tag);
- if (pre_update_tag != tag) {
- // If the label changes, replace the node instead of updating it.
- RemoveSyncNode(pre_update_tag, trans);
- AddAutofillProfileSyncNode(trans, autofill_root, tag, clone.get());
- return;
- }
- int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
- if (sync_api::kInvalidId == sync_id) {
- std::string err = "Unexpected notification for: " + tag;
- error_handler()->OnUnrecoverableError(FROM_HERE, err);
- return;
- } else {
- if (!sync_node.InitByIdLookup(sync_id)) {
- error_handler()->OnUnrecoverableError(FROM_HERE,
- "Autofill node lookup failed.");
- return;
- }
- WriteAutofillProfile(*clone.get(), &sync_node);
- }
- break;
- }
- case AutofillProfileChange::REMOVE: {
- RemoveSyncNode(tag, trans);
- break;
- }
- }
-}
-
void AutofillChangeProcessor::ObserveAutofillEntriesChanged(
AutofillChangeList* changes, sync_api::WriteTransaction* trans,
const sync_api::ReadNode& autofill_root) {
@@ -324,7 +209,8 @@ void AutofillChangeProcessor::ApplyChangesFromSyncModel(
<< "Autofill specifics data not present on delete!";
const sync_pb::AutofillSpecifics& autofill =
changes[i].specifics.GetExtension(sync_pb::autofill);
- if (autofill.has_value() || autofill.has_profile()) {
+ if (autofill.has_value() ||
+ (HasNotMigratedYet() && autofill.has_profile())) {
autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
changes[i].id,
autofill));
@@ -349,7 +235,8 @@ void AutofillChangeProcessor::ApplyChangesFromSyncModel(
const sync_pb::AutofillSpecifics& autofill(
sync_node.GetAutofillSpecifics());
int64 sync_id = sync_node.GetId();
- if (autofill.has_value() || autofill.has_profile()) {
+ if (autofill.has_value() ||
+ (HasNotMigratedYet() && autofill.has_profile())) {
autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
sync_id, autofill));
} else {
@@ -374,8 +261,8 @@ void AutofillChangeProcessor::CommitChangesFromSyncModel() {
if (autofill_changes_[i].autofill_.has_value()) {
ApplySyncAutofillEntryDelete(autofill_changes_[i].autofill_);
} else if (autofill_changes_[i].autofill_.has_profile()) {
- ApplySyncAutofillProfileDelete(autofill_changes_[i].autofill_.profile(),
- autofill_changes_[i].id_);
+ DCHECK(HasNotMigratedYet());
+ ApplySyncAutofillProfileDelete(autofill_changes_[i].id_);
} else {
NOTREACHED() << "Autofill's CommitChanges received change with no"
" data!";
@@ -389,6 +276,7 @@ void AutofillChangeProcessor::CommitChangesFromSyncModel() {
autofill_changes_[i].autofill_, &new_entries,
autofill_changes_[i].id_);
} else if (autofill_changes_[i].autofill_.has_profile()) {
+ DCHECK(HasNotMigratedYet());
ApplySyncAutofillProfileChange(autofill_changes_[i].action_,
autofill_changes_[i].autofill_.profile(),
autofill_changes_[i].id_);
@@ -448,38 +336,42 @@ void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
int64 sync_id) {
DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
- std::string tag(AutofillModelAssociator::ProfileLabelToTag(
- UTF8ToUTF16(profile.label())));
switch (action) {
case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
- PersonalDataManager* pdm = model_associator_->sync_service()->
- profile()->GetPersonalDataManager();
- scoped_ptr<AutoFillProfile> p(
- pdm->CreateNewEmptyAutoFillProfileForDBThread(
- UTF8ToUTF16(profile.label())));
- AutofillModelAssociator::OverwriteProfileWithServerData(p.get(),
+ std::string guid(guid::GenerateGUID());
+ scoped_ptr<AutoFillProfile> p(new AutoFillProfile);
+ p->set_guid(guid);
+ AutofillModelAssociator::FillProfileWithServerData(p.get(),
profile);
-
- model_associator_->Associate(&tag, sync_id);
if (!web_database_->AddAutoFillProfile(*p.get())) {
- NOTREACHED() << "Couldn't add autofill profile: " << profile.label();
+ NOTREACHED() << "Couldn't add autofill profile: " << guid;
return;
}
+ model_associator_->Associate(&guid, sync_id);
break;
}
case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
- AutoFillProfile* p = NULL;
- string16 label = UTF8ToUTF16(profile.label());
- if (!web_database_->GetAutoFillProfileForLabel(label, &p)) {
- NOTREACHED() << "Couldn't retrieve autofill profile: " << label;
+ const std::string* guid = model_associator_->GetChromeNodeFromSyncId(
+ sync_id);
+ if (guid == NULL) {
+ LOG(ERROR) << " Model association has not happened for " << sync_id;
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "model association has not happened");
return;
}
- AutofillModelAssociator::OverwriteProfileWithServerData(p, profile);
- if (!web_database_->UpdateAutoFillProfile(*p)) {
- NOTREACHED() << "Couldn't update autofill profile: " << label;
+ AutoFillProfile *temp_ptr;
+ if (!web_database_->GetAutoFillProfileForGUID(*guid, &temp_ptr)) {
+ LOG(ERROR) << "Autofill profile not found for " << *guid;
+ return;
+ }
+
+ scoped_ptr<AutoFillProfile> p(temp_ptr);
+
+ AutofillModelAssociator::FillProfileWithServerData(p.get(), profile);
+ if (!web_database_->UpdateAutoFillProfile(*(p.get()))) {
+ LOG(ERROR) << "Couldn't update autofill profile: " << guid;
return;
}
- delete p;
break;
}
default:
@@ -488,20 +380,19 @@ void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
}
void AutofillChangeProcessor::ApplySyncAutofillProfileDelete(
- const sync_pb::AutofillProfileSpecifics& profile,
int64 sync_id) {
- string16 label(UTF8ToUTF16(profile.label()));
- AutoFillProfile* ptr = NULL;
- bool get_success = web_database_->GetAutoFillProfileForLabel(label, &ptr);
- scoped_ptr<AutoFillProfile> p(ptr);
- if (!get_success) {
- NOTREACHED() << "Couldn't retrieve autofill profile: " << label;
+
+ const std::string *guid = model_associator_->GetChromeNodeFromSyncId(sync_id);
+ if (guid == NULL) {
+ LOG(ERROR)<< "The profile is not associated";
return;
}
- if (!web_database_->RemoveAutoFillProfile(p->guid())) {
- NOTREACHED() << "Couldn't remove autofill profile: " << label;
+
+ if (!web_database_->RemoveAutoFillProfile(*guid)) {
+ LOG(ERROR) << "Could not remove the profile";
return;
}
+
model_associator_->Disassociate(sync_id);
}
@@ -520,8 +411,6 @@ void AutofillChangeProcessor::StartObserving() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED,
NotificationService::AllSources());
- notification_registrar_.Add(this, NotificationType::AUTOFILL_PROFILE_CHANGED,
- NotificationService::AllSources());
}
void AutofillChangeProcessor::StopObserving() {
@@ -549,7 +438,6 @@ void AutofillChangeProcessor::WriteAutofillProfile(
const AutoFillProfile& profile, sync_api::WriteNode* node) {
sync_pb::AutofillSpecifics autofill;
sync_pb::AutofillProfileSpecifics* s(autofill.mutable_profile());
- s->set_label(UTF16ToUTF8(profile.Label()));
s->set_name_first(UTF16ToUTF8(
profile.GetFieldText(AutoFillType(NAME_FIRST))));
s->set_name_middle(UTF16ToUTF8(
@@ -577,5 +465,8 @@ void AutofillChangeProcessor::WriteAutofillProfile(
AutoFillType(PHONE_HOME_WHOLE_NUMBER))));
node->SetAutofillSpecifics(autofill);
}
+bool AutofillChangeProcessor::HasNotMigratedYet() {
+ return true;
+}
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_change_processor.h b/chrome/browser/sync/glue/autofill_change_processor.h
index 7a84520..e901c8e 100644
--- a/chrome/browser/sync/glue/autofill_change_processor.h
+++ b/chrome/browser/sync/glue/autofill_change_processor.h
@@ -12,6 +12,7 @@
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/autofill_change_processor2.h"
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
@@ -104,43 +105,15 @@ class AutofillChangeProcessor : public ChangeProcessor,
void ApplySyncAutofillEntryDelete(
const sync_pb::AutofillSpecifics& autofill);
void ApplySyncAutofillProfileDelete(
- const sync_pb::AutofillProfileSpecifics& profile,
int64 sync_id);
- // If the chrome model tries to add an AutoFillProfile with a label that
- // is already in use, we perform a move-aside by calling-back into the chrome
- // model and overwriting the label with a unique value we can apply for sync.
- // This method should be called on an ADD notification from the chrome model.
- // |tag| contains the unique sync client tag identifier for |profile|, which
- // is derived from the profile label using ProfileLabelToTag.
- // |existing_unique_label| is the current label of the object, if any; this
- // is an allowed value, because it's taken by the item in question.
- // For new items, set |existing_unique_label| to the empty string.
- void ChangeProfileLabelIfAlreadyTaken(
- sync_api::BaseTransaction* trans,
- const string16& existing_unique_label,
- AutoFillProfile* profile,
- std::string* tag);
-
- // Reassign the label of the profile, write this back to the web database,
- // and update |tag| with the tag corresponding to the new label.
- void OverrideProfileLabel(
- const string16& new_label,
- AutoFillProfile* profile_to_update,
- std::string* tag_to_update);
-
- // Helper to create a sync node with tag |tag|, storing |profile| as
- // the node's AutofillSpecifics.
- void AddAutofillProfileSyncNode(
- sync_api::WriteTransaction* trans,
- const sync_api::BaseNode& autofill,
- const std::string& tag,
- const AutoFillProfile* profile);
-
// Helper to post a task to the UI loop to inform the PersonalDataManager
// it needs to refresh itself.
void PostOptimisticRefreshTask();
+ // Called to see if we need to upgrade to the new autofill2 profile.
+ bool HasNotMigratedYet();
+
// The two models should be associated according to this ModelAssociator.
AutofillModelAssociator* model_associator_;
diff --git a/chrome/browser/sync/glue/autofill_change_processor2.cc b/chrome/browser/sync/glue/autofill_change_processor2.cc
new file mode 100644
index 0000000..b2b564e
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_change_processor2.cc
@@ -0,0 +1,584 @@
+// 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_processor2.h"
+
+#include <string>
+#include <vector>
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/sync/glue/autofill_model_associator.h"
+#include "chrome/browser/sync/glue/autofill_model_associator2.h"
+#include "chrome/browser/sync/profile_sync_service.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 {
+
+class AutofillModelAssociator2;
+struct AutofillChangeProcessor2::AutofillChangeRecord {
+ sync_api::SyncManager::ChangeRecord::Action action_;
+ int64 id_;
+ sync_pb::AutofillSpecifics autofill_;
+ AutofillChangeRecord(sync_api::SyncManager::ChangeRecord::Action action,
+ int64 id, const sync_pb::AutofillSpecifics& autofill)
+ : action_(action),
+ id_(id),
+ autofill_(autofill) { }
+};
+
+AutofillChangeProcessor2::AutofillChangeProcessor2(
+ AutofillModelAssociator2* model_associator,
+ WebDatabase* web_database,
+ PersonalDataManager* personal_data,
+ UnrecoverableErrorHandler* error_handler)
+ : ChangeProcessor(error_handler),
+ model_associator_(model_associator),
+ web_database_(web_database),
+ personal_data_(personal_data),
+ observing_(false) {
+ DCHECK(model_associator);
+ DCHECK(web_database);
+ DCHECK(error_handler);
+ DCHECK(personal_data);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ StartObserving();
+}
+
+AutofillChangeProcessor2::~AutofillChangeProcessor2() {}
+
+void AutofillChangeProcessor2::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // Ensure this notification came from our web database.
+ WebDataService* wds = Source<WebDataService>(source).ptr();
+ if (!wds || wds->GetDatabase() != web_database_)
+ return;
+
+ DCHECK(running());
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!observing_)
+ return;
+
+ sync_api::WriteTransaction trans(share_handle());
+ sync_api::ReadNode autofill_root(&trans);
+ if (!autofill_root.InitByTagLookup(kAutofillTag)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Server did not create the top-level autofill node. "
+ "We might be running against an out-of-date server.");
+ return;
+ }
+
+ switch (type.value) {
+ case NotificationType::AUTOFILL_ENTRIES_CHANGED: {
+ AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr();
+ ObserveAutofillEntriesChanged(changes, &trans, autofill_root);
+ break;
+ }
+ case NotificationType::AUTOFILL_PROFILE_CHANGED: {
+ AutofillProfileChange* change =
+ Details<AutofillProfileChange>(details).ptr();
+ ObserveAutofillProfileChanged(change, &trans, autofill_root);
+ break;
+ }
+ default:
+ NOTREACHED() << "Invalid NotificationType.";
+ }
+}
+
+void AutofillChangeProcessor2::ChangeProfileLabelIfAlreadyTaken(
+ sync_api::BaseTransaction* trans,
+ const string16& pre_update_label,
+ AutoFillProfile* profile,
+ std::string* tag) {
+ DCHECK_EQ(AutofillModelAssociator2::ProfileLabelToTag(profile->Label()),
+ *tag);
+ sync_api::ReadNode read_node(trans);
+ if (!pre_update_label.empty() && pre_update_label == profile->Label())
+ return;
+ if (read_node.InitByClientTagLookup(syncable::AUTOFILL, *tag)) {
+ // Handle the edge case of duplicate labels.
+ string16 new_label(AutofillModelAssociator2::MakeUniqueLabel(
+ profile->Label(), pre_update_label, trans));
+ if (new_label.empty()) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "No unique label; can't move aside");
+ return;
+ }
+ OverrideProfileLabel(new_label, profile, tag);
+ }
+}
+
+void AutofillChangeProcessor2::OverrideProfileLabel(
+ const string16& new_label,
+ AutoFillProfile* profile_to_update,
+ std::string* tag_to_update) {
+ tag_to_update->assign(AutofillModelAssociator2::ProfileLabelToTag(new_label));
+
+ profile_to_update->set_label(new_label);
+ if (!web_database_->UpdateAutoFillProfile(*profile_to_update)) {
+ std::string err = "Failed to overwrite label for node ";
+ err += UTF16ToUTF8(new_label);
+ error_handler()->OnUnrecoverableError(FROM_HERE, err);
+ return;
+ }
+
+ // Notify the PersonalDataManager that it's out of date.
+ PostOptimisticRefreshTask();
+}
+
+void AutofillChangeProcessor2::PostOptimisticRefreshTask() {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ new AutofillModelAssociator2::DoOptimisticRefreshTask(
+ personal_data_));
+}
+
+void AutofillChangeProcessor2::AddAutofillProfileSyncNode(
+ sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill,
+ const std::string& tag, const AutoFillProfile* profile) {
+ sync_api::WriteNode sync_node(trans);
+ if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL, autofill, tag)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Failed to create autofill sync node.");
+ return;
+ }
+ sync_node.SetTitle(UTF8ToWide(tag));
+
+ WriteAutofillProfile(*profile, &sync_node);
+ model_associator_->Associate(&tag, sync_node.GetId());
+}
+
+void AutofillChangeProcessor2::ObserveAutofillProfileChanged(
+ AutofillProfileChange* change, sync_api::WriteTransaction* trans,
+ const sync_api::ReadNode& autofill_root) {
+ std::string tag(AutofillModelAssociator2::ProfileLabelToTag(change->key()));
+ switch (change->type()) {
+ case AutofillProfileChange::ADD: {
+ scoped_ptr<AutoFillProfile> clone(
+ static_cast<AutoFillProfile*>(change->profile()->Clone()));
+ DCHECK_EQ(clone->Label(), change->key());
+ ChangeProfileLabelIfAlreadyTaken(trans, string16(), clone.get(), &tag);
+ AddAutofillProfileSyncNode(trans, autofill_root, tag, clone.get());
+ break;
+ }
+ case AutofillProfileChange::UPDATE: {
+ scoped_ptr<AutoFillProfile> clone(
+ static_cast<AutoFillProfile*>(change->profile()->Clone()));
+ std::string pre_update_tag = AutofillModelAssociator2::ProfileLabelToTag(
+ change->pre_update_label());
+ DCHECK_EQ(clone->Label(), change->key());
+ sync_api::WriteNode sync_node(trans);
+ ChangeProfileLabelIfAlreadyTaken(trans, change->pre_update_label(),
+ clone.get(), &tag);
+ if (pre_update_tag != tag) {
+ // If the label changes, replace the node instead of updating it.
+ RemoveSyncNode(pre_update_tag, trans);
+ AddAutofillProfileSyncNode(trans, autofill_root, tag, clone.get());
+ return;
+ }
+ int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
+ if (sync_api::kInvalidId == sync_id) {
+ std::string err = "Unexpected notification for: " + tag;
+ error_handler()->OnUnrecoverableError(FROM_HERE, err);
+ return;
+ } else {
+ if (!sync_node.InitByIdLookup(sync_id)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Autofill node lookup failed.");
+ return;
+ }
+ WriteAutofillProfile(*clone.get(), &sync_node);
+ }
+ break;
+ }
+ case AutofillProfileChange::REMOVE: {
+ RemoveSyncNode(tag, trans);
+ break;
+ }
+ }
+}
+
+void AutofillChangeProcessor2::ObserveAutofillEntriesChanged(
+ AutofillChangeList* changes, sync_api::WriteTransaction* trans,
+ const sync_api::ReadNode& autofill_root) {
+ for (AutofillChangeList::iterator change = changes->begin();
+ change != changes->end(); ++change) {
+ switch (change->type()) {
+ case AutofillChange::ADD:
+ {
+ sync_api::WriteNode sync_node(trans);
+ std::string tag =
+ AutofillModelAssociator2::KeyToTag(change->key().name(),
+ change->key().value());
+ if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL,
+ autofill_root, tag)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Failed to create autofill sync node.");
+ return;
+ }
+
+ std::vector<base::Time> timestamps;
+ if (!web_database_->GetAutofillTimestamps(
+ change->key().name(),
+ change->key().value(),
+ &timestamps)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Failed to get timestamps.");
+ return;
+ }
+
+ sync_node.SetTitle(UTF8ToWide(tag));
+
+ WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
+ &sync_node);
+ model_associator_->Associate(&tag, sync_node.GetId());
+ }
+ break;
+
+ case AutofillChange::UPDATE:
+ {
+ sync_api::WriteNode sync_node(trans);
+ std::string tag = AutofillModelAssociator2::KeyToTag(
+ change->key().name(), change->key().value());
+ int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
+ if (sync_api::kInvalidId == sync_id) {
+ std::string err = "Unexpected notification for: " +
+ UTF16ToUTF8(change->key().name());
+ error_handler()->OnUnrecoverableError(FROM_HERE, err);
+ return;
+ } else {
+ if (!sync_node.InitByIdLookup(sync_id)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Autofill node lookup failed.");
+ return;
+ }
+ }
+
+ std::vector<base::Time> timestamps;
+ if (!web_database_->GetAutofillTimestamps(
+ change->key().name(),
+ change->key().value(),
+ &timestamps)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Failed to get timestamps.");
+ return;
+ }
+
+ WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
+ &sync_node);
+ }
+ break;
+ case AutofillChange::REMOVE: {
+ std::string tag = AutofillModelAssociator2::KeyToTag(
+ change->key().name(), change->key().value());
+ RemoveSyncNode(tag, trans);
+ }
+ break;
+ }
+ }
+}
+
+void AutofillChangeProcessor2::RemoveSyncNode(const std::string& tag,
+ sync_api::WriteTransaction* trans) {
+ sync_api::WriteNode sync_node(trans);
+ int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
+ if (sync_api::kInvalidId == sync_id) {
+ std::string err = "Unexpected notification for: " + tag;
+ error_handler()->OnUnrecoverableError(FROM_HERE, err);
+ return;
+ } else {
+ if (!sync_node.InitByIdLookup(sync_id)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Autofill node lookup failed.");
+ return;
+ }
+ model_associator_->Disassociate(sync_node.GetId());
+ sync_node.Remove();
+ }
+}
+
+void AutofillChangeProcessor2::ApplyChangesFromSyncModel(
+ const sync_api::BaseTransaction* trans,
+ const sync_api::SyncManager::ChangeRecord* changes,
+ int change_count) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!running())
+ return;
+ StopObserving();
+
+ sync_api::ReadNode autofill_root(trans);
+ if (!autofill_root.InitByTagLookup(kAutofillTag)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Autofill root node lookup failed.");
+ return;
+ }
+
+ for (int i = 0; i < change_count; ++i) {
+ sync_api::SyncManager::ChangeRecord::Action action(changes[i].action);
+ if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == action) {
+ DCHECK(changes[i].specifics.HasExtension(sync_pb::autofill))
+ << "Autofill specifics data not present on delete!";
+ const sync_pb::AutofillSpecifics& autofill =
+ changes[i].specifics.GetExtension(sync_pb::autofill);
+ if (autofill.has_value() || autofill.has_profile()) {
+ autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
+ changes[i].id,
+ autofill));
+ } else {
+ NOTREACHED() << "Autofill specifics has no data!";
+ }
+ continue;
+ }
+
+ // Handle an update or add.
+ sync_api::ReadNode sync_node(trans);
+ if (!sync_node.InitByIdLookup(changes[i].id)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Autofill node lookup failed.");
+ 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());
+ int64 sync_id = sync_node.GetId();
+ if (autofill.has_value() || autofill.has_profile()) {
+ autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
+ sync_id, autofill));
+ } else {
+ NOTREACHED() << "Autofill specifics has no data!";
+ }
+ }
+
+ StartObserving();
+}
+
+void AutofillChangeProcessor2::CommitChangesFromSyncModel() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!running())
+ return;
+ StopObserving();
+
+ std::vector<AutofillEntry> new_entries;
+ for (unsigned int i = 0; i < autofill_changes_.size(); i++) {
+ // Handle deletions.
+ if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
+ autofill_changes_[i].action_) {
+ if (autofill_changes_[i].autofill_.has_value()) {
+ ApplySyncAutofillEntryDelete(autofill_changes_[i].autofill_);
+ } else if (autofill_changes_[i].autofill_.has_profile()) {
+ ApplySyncAutofillProfileDelete(autofill_changes_[i].autofill_.profile(),
+ autofill_changes_[i].id_);
+ } else {
+ NOTREACHED() << "Autofill's CommitChanges received change with no"
+ " data!";
+ }
+ continue;
+ }
+
+ // Handle update/adds.
+ if (autofill_changes_[i].autofill_.has_value()) {
+ ApplySyncAutofillEntryChange(autofill_changes_[i].action_,
+ autofill_changes_[i].autofill_, &new_entries,
+ autofill_changes_[i].id_);
+ } else if (autofill_changes_[i].autofill_.has_profile()) {
+ ApplySyncAutofillProfileChange(autofill_changes_[i].action_,
+ autofill_changes_[i].autofill_.profile(),
+ autofill_changes_[i].id_);
+ } else {
+ NOTREACHED() << "Autofill's CommitChanges received change with no data!";
+ }
+ }
+ autofill_changes_.clear();
+
+ // Make changes
+ if (!web_database_->UpdateAutofillEntries(new_entries)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Could not update autofill entries.");
+ return;
+ }
+
+ PostOptimisticRefreshTask();
+
+ StartObserving();
+}
+
+void AutofillChangeProcessor2::ApplySyncAutofillEntryDelete(
+ const sync_pb::AutofillSpecifics& autofill) {
+ if (!web_database_->RemoveFormElement(
+ UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Could not remove autofill node.");
+ return;
+ }
+}
+
+void AutofillChangeProcessor2::ApplySyncAutofillEntryChange(
+ sync_api::SyncManager::ChangeRecord::Action action,
+ const sync_pb::AutofillSpecifics& autofill,
+ std::vector<AutofillEntry>* new_entries,
+ int64 sync_id) {
+ DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
+
+ 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)));
+ }
+ AutofillKey k(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
+ AutofillEntry new_entry(k, timestamps);
+
+ new_entries->push_back(new_entry);
+ std::string tag(AutofillModelAssociator2::KeyToTag(k.name(), k.value()));
+ if (action == sync_api::SyncManager::ChangeRecord::ACTION_ADD)
+ model_associator_->Associate(&tag, sync_id);
+}
+
+void AutofillChangeProcessor2::ApplySyncAutofillProfileChange(
+ sync_api::SyncManager::ChangeRecord::Action action,
+ const sync_pb::AutofillProfileSpecifics& profile,
+ int64 sync_id) {
+ DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
+
+ std::string tag(AutofillModelAssociator2::ProfileLabelToTag(
+ UTF8ToUTF16(profile.label())));
+ switch (action) {
+ case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
+ PersonalDataManager* pdm = model_associator_->sync_service()->
+ profile()->GetPersonalDataManager();
+ scoped_ptr<AutoFillProfile> p(
+ pdm->CreateNewEmptyAutoFillProfileForDBThread(
+ UTF8ToUTF16(profile.label())));
+ AutofillModelAssociator2::OverwriteProfileWithServerData(p.get(),
+ profile);
+
+ model_associator_->Associate(&tag, sync_id);
+ if (!web_database_->AddAutoFillProfile(*p.get())) {
+ NOTREACHED() << "Couldn't add autofill profile: " << profile.label();
+ return;
+ }
+ break;
+ }
+ case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
+ AutoFillProfile* p = NULL;
+ string16 label = UTF8ToUTF16(profile.label());
+ if (!web_database_->GetAutoFillProfileForLabel(label, &p)) {
+ NOTREACHED() << "Couldn't retrieve autofill profile: " << label;
+ return;
+ }
+ AutofillModelAssociator2::OverwriteProfileWithServerData(p, profile);
+ if (!web_database_->UpdateAutoFillProfile(*p)) {
+ NOTREACHED() << "Couldn't update autofill profile: " << label;
+ return;
+ }
+ delete p;
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+}
+
+void AutofillChangeProcessor2::ApplySyncAutofillProfileDelete(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ int64 sync_id) {
+ string16 label(UTF8ToUTF16(profile.label()));
+ AutoFillProfile* ptr = NULL;
+ bool get_success = web_database_->GetAutoFillProfileForLabel(label, &ptr);
+ scoped_ptr<AutoFillProfile> p(ptr);
+ if (!get_success) {
+ NOTREACHED() << "Couldn't retrieve autofill profile: " << label;
+ return;
+ }
+ if (!web_database_->RemoveAutoFillProfile(p->guid())) {
+ NOTREACHED() << "Couldn't remove autofill profile: " << label;
+ return;
+ }
+ model_associator_->Disassociate(sync_id);
+}
+
+void AutofillChangeProcessor2::StartImpl(Profile* profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ observing_ = true;
+}
+
+void AutofillChangeProcessor2::StopImpl() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ observing_ = false;
+}
+
+
+void AutofillChangeProcessor2::StartObserving() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this, NotificationType::AUTOFILL_PROFILE_CHANGED,
+ NotificationService::AllSources());
+}
+
+void AutofillChangeProcessor2::StopObserving() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ notification_registrar_.RemoveAll();
+}
+
+// static
+void AutofillChangeProcessor2::WriteAutofillEntry(
+ const AutofillEntry& entry,
+ sync_api::WriteNode* node) {
+ sync_pb::AutofillSpecifics autofill;
+ autofill.set_name(UTF16ToUTF8(entry.key().name()));
+ autofill.set_value(UTF16ToUTF8(entry.key().value()));
+ const std::vector<base::Time>& ts(entry.timestamps());
+ for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
+ timestamp != ts.end(); ++timestamp) {
+ autofill.add_usage_timestamp(timestamp->ToInternalValue());
+ }
+ node->SetAutofillSpecifics(autofill);
+}
+
+// static
+void AutofillChangeProcessor2::WriteAutofillProfile(
+ const AutoFillProfile& profile, sync_api::WriteNode* node) {
+ sync_pb::AutofillSpecifics autofill;
+ sync_pb::AutofillProfileSpecifics* s(autofill.mutable_profile());
+ s->set_label(UTF16ToUTF8(profile.Label()));
+ s->set_name_first(UTF16ToUTF8(
+ profile.GetFieldText(AutoFillType(NAME_FIRST))));
+ s->set_name_middle(UTF16ToUTF8(
+ profile.GetFieldText(AutoFillType(NAME_MIDDLE))));
+ s->set_name_last(UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST))));
+ s->set_address_home_line1(
+ UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1))));
+ s->set_address_home_line2(
+ UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2))));
+ s->set_address_home_city(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(ADDRESS_HOME_CITY))));
+ s->set_address_home_state(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(ADDRESS_HOME_STATE))));
+ s->set_address_home_country(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(ADDRESS_HOME_COUNTRY))));
+ s->set_address_home_zip(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(ADDRESS_HOME_ZIP))));
+ s->set_email_address(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(EMAIL_ADDRESS))));
+ s->set_company_name(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(COMPANY_NAME))));
+ s->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(PHONE_FAX_WHOLE_NUMBER))));
+ s->set_phone_home_whole_number(UTF16ToUTF8(profile.GetFieldText(
+ AutoFillType(PHONE_HOME_WHOLE_NUMBER))));
+ node->SetAutofillSpecifics(autofill);
+}
+
+} // namespace browser_sync
+
diff --git a/chrome/browser/sync/glue/autofill_change_processor2.h b/chrome/browser/sync/glue/autofill_change_processor2.h
new file mode 100644
index 0000000..25d7f51
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_change_processor2.h
@@ -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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_AUTOFILL_CHANGE_PROCESSOR2_H_
+#define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_CHANGE_PROCESSOR2_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/autofill/credit_card.h"
+#include "chrome/browser/autofill/personal_data_manager.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/browser/sync/protocol/autofill_specifics.pb.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class AutofillCreditCardChange;
+class AutofillEntry;
+class AutofillProfileChange;
+class PersonalDataManager;
+class WebDatabase;
+
+namespace browser_sync {
+
+class AutofillModelAssociator2;
+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 DB thread.
+class AutofillChangeProcessor2 : public ChangeProcessor,
+ public NotificationObserver {
+ public:
+ AutofillChangeProcessor2(AutofillModelAssociator2* model_associator,
+ WebDatabase* web_database,
+ PersonalDataManager* personal_data,
+ UnrecoverableErrorHandler* error_handler);
+ virtual ~AutofillChangeProcessor2();
+
+ // 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);
+
+ // Commit any changes from ApplyChangesFromSyncModel buffered in
+ // autofill_changes_.
+ virtual void CommitChangesFromSyncModel();
+
+ // Copy the properties of the given Autofill entry into the sync
+ // node.
+ static void WriteAutofillEntry(const AutofillEntry& entry,
+ sync_api::WriteNode* node);
+ // As above, for autofill profiles.
+ static void WriteAutofillProfile(const AutoFillProfile& profile,
+ sync_api::WriteNode* node);
+ // TODO(georgey) : add the same processing for CC info (already in protocol
+ // buffers).
+
+ protected:
+ virtual void StartImpl(Profile* profile);
+ virtual void StopImpl();
+
+ private:
+ void StartObserving();
+ void StopObserving();
+
+ // A function to remove the sync node for an autofill entry or profile.
+ void RemoveSyncNode(const std::string& tag,
+ sync_api::WriteTransaction* trans);
+
+ // These two methods are dispatched to by Observe depending on the type.
+ void ObserveAutofillEntriesChanged(AutofillChangeList* changes,
+ sync_api::WriteTransaction* trans,
+ const sync_api::ReadNode& autofill_root);
+ void ObserveAutofillProfileChanged(AutofillProfileChange* change,
+ sync_api::WriteTransaction* trans,
+ const sync_api::ReadNode& autofill_root);
+
+ // The following methods are the implementation of ApplyChangeFromSyncModel
+ // for the respective autofill subtypes.
+ void ApplySyncAutofillEntryChange(
+ sync_api::SyncManager::ChangeRecord::Action action,
+ const sync_pb::AutofillSpecifics& autofill,
+ std::vector<AutofillEntry>* new_entries,
+ int64 sync_id);
+ void ApplySyncAutofillProfileChange(
+ sync_api::SyncManager::ChangeRecord::Action action,
+ const sync_pb::AutofillProfileSpecifics& profile,
+ int64 sync_id);
+
+ // Delete is a special case of change application.
+ void ApplySyncAutofillEntryDelete(
+ const sync_pb::AutofillSpecifics& autofill);
+ void ApplySyncAutofillProfileDelete(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ int64 sync_id);
+
+ // If the chrome model tries to add an AutoFillProfile with a label that
+ // is already in use, we perform a move-aside by calling-back into the chrome
+ // model and overwriting the label with a unique value we can apply for sync.
+ // This method should be called on an ADD notification from the chrome model.
+ // |tag| contains the unique sync client tag identifier for |profile|, which
+ // is derived from the profile label using ProfileLabelToTag.
+ // |existing_unique_label| is the current label of the object, if any; this
+ // is an allowed value, because it's taken by the item in question.
+ // For new items, set |existing_unique_label| to the empty string.
+ void ChangeProfileLabelIfAlreadyTaken(
+ sync_api::BaseTransaction* trans,
+ const string16& existing_unique_label,
+ AutoFillProfile* profile,
+ std::string* tag);
+
+ // Reassign the label of the profile, write this back to the web database,
+ // and update |tag| with the tag corresponding to the new label.
+ void OverrideProfileLabel(
+ const string16& new_label,
+ AutoFillProfile* profile_to_update,
+ std::string* tag_to_update);
+
+ // Helper to create a sync node with tag |tag|, storing |profile| as
+ // the node's AutofillSpecifics.
+ void AddAutofillProfileSyncNode(
+ sync_api::WriteTransaction* trans,
+ const sync_api::BaseNode& autofill,
+ const std::string& tag,
+ const AutoFillProfile* profile);
+
+ // Helper to post a task to the UI loop to inform the PersonalDataManager
+ // it needs to refresh itself.
+ void PostOptimisticRefreshTask();
+
+ // The two models should be associated according to this ModelAssociator.
+ AutofillModelAssociator2* model_associator_;
+
+ // The model we are processing changes from. This is owned by the
+ // WebDataService which is kept alive by our data type controller
+ // holding a reference.
+ WebDatabase* web_database_;
+
+ // We periodically tell the PersonalDataManager to refresh as we make
+ // changes to the autofill data in the WebDatabase.
+ PersonalDataManager* personal_data_;
+
+ NotificationRegistrar notification_registrar_;
+
+ bool observing_;
+
+ // Record of changes from ApplyChangesFromSyncModel. These are then processed
+ // in CommitChangesFromSyncModel.
+ struct AutofillChangeRecord;
+ std::vector<AutofillChangeRecord> autofill_changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillChangeProcessor2);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_AUTOFILL_CHANGE_PROCESSOR2_H_
+
diff --git a/chrome/browser/sync/glue/autofill_model_associator.cc b/chrome/browser/sync/glue/autofill_model_associator.cc
index b550ab5..6409e9b 100644
--- a/chrome/browser/sync/glue/autofill_model_associator.cc
+++ b/chrome/browser/sync/glue/autofill_model_associator.cc
@@ -12,6 +12,7 @@
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/guid.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/autofill_change_processor.h"
@@ -26,9 +27,6 @@ namespace browser_sync {
const char kAutofillTag[] = "google_chrome_autofill";
const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
-const char kAutofillProfileNamespaceTag[] = "autofill_profile|";
-
-static const int kMaxNumAttemptsToFindUniqueLabel = 100;
struct AutofillModelAssociator::DataBundle {
std::set<AutofillKey> current_entries;
@@ -123,87 +121,6 @@ bool AutofillModelAssociator::TraverseAndAssociateChromeAutofillEntries(
return true;
}
-bool AutofillModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
- sync_api::WriteTransaction* write_trans,
- const sync_api::ReadNode& autofill_root,
- const std::vector<AutoFillProfile*>& all_profiles_from_db,
- std::set<string16>* current_profiles,
- std::vector<AutoFillProfile*>* updated_profiles) {
- const std::vector<AutoFillProfile*>& profiles = all_profiles_from_db;
- for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin();
- ix != profiles.end(); ++ix) {
- string16 label((*ix)->Label());
- std::string tag(ProfileLabelToTag(label));
-
- sync_api::ReadNode node(write_trans);
- if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
- const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics());
- DCHECK(autofill.has_profile());
- DCHECK_EQ(ProfileLabelToTag(UTF8ToUTF16(autofill.profile().label())),
- tag);
- int64 sync_id = node.GetId();
- if (id_map_.find(tag) != id_map_.end()) {
- // We just looked up something we already associated. Move aside.
- label = MakeUniqueLabel(label, string16(), write_trans);
- if (label.empty()) {
- return false;
- }
- tag = ProfileLabelToTag(label);
- // TODO(dhollowa): Replace with |AutoFillProfile::set_guid|.
- // http://crbug.com/58813
- (*ix)->set_label(label);
- if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root,
- tag, **ix, &sync_id)) {
- return false;
- }
- updated_profiles->push_back(*ix);
- } else {
- // Overwrite local with cloud state.
- if (OverwriteProfileWithServerData(*ix, autofill.profile()))
- updated_profiles->push_back(*ix);
- sync_id = node.GetId();
- }
-
- Associate(&tag, sync_id);
- } else {
- int64 id;
- if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root,
- tag, **ix, &id)) {
- return false;
- }
- Associate(&tag, id);
- }
- current_profiles->insert(label);
- }
- return true;
-}
-
-// static
-string16 AutofillModelAssociator::MakeUniqueLabel(
- const string16& non_unique_label,
- const string16& existing_unique_label,
- sync_api::BaseTransaction* trans) {
- if (!non_unique_label.empty() && non_unique_label == existing_unique_label) {
- return existing_unique_label;
- }
- int unique_id = 1; // Priming so we start by appending "2".
- while (unique_id++ < kMaxNumAttemptsToFindUniqueLabel) {
- string16 suffix(base::IntToString16(unique_id));
- string16 unique_label = non_unique_label + suffix;
- if (unique_label == existing_unique_label)
- return unique_label; // We'll use the one we already have.
- sync_api::ReadNode node(trans);
- if (node.InitByClientTagLookup(syncable::AUTOFILL,
- ProfileLabelToTag(unique_label))) {
- continue;
- }
- return unique_label;
- }
-
- LOG(ERROR) << "Couldn't create unique tag for autofill node. Srsly?!";
- return string16();
-}
-
bool AutofillModelAssociator::MakeNewAutofillProfileSyncNode(
sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill_root,
const std::string& tag, const AutoFillProfile& profile, int64* sync_id) {
@@ -265,11 +182,15 @@ bool AutofillModelAssociator::AssociateModels() {
}
if (!TraverseAndAssociateChromeAutofillEntries(&trans, autofill_root,
- entries, &bundle.current_entries, &bundle.new_entries) ||
- !TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root,
- profiles.get(), &bundle.current_profiles,
- &bundle.updated_profiles) ||
- !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) {
+ entries, &bundle.current_entries, &bundle.new_entries)) {
+ return false;
+ }
+
+ if (!TraverseAndAssociateAllSyncNodes(
+ &trans,
+ autofill_root,
+ &bundle,
+ profiles.get())) {
return false;
}
}
@@ -319,7 +240,8 @@ bool AutofillModelAssociator::SaveChangesToWebData(const DataBundle& bundle) {
bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
- DataBundle* bundle) {
+ DataBundle* bundle,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
int64 sync_child_id = autofill_root.GetFirstChildId();
@@ -332,18 +254,57 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
const sync_pb::AutofillSpecifics& autofill(
sync_child.GetAutofillSpecifics());
- if (autofill.has_value())
+ if (autofill.has_value()) {
AddNativeEntryIfNeeded(autofill, bundle, sync_child);
- else if (autofill.has_profile())
- AddNativeProfileIfNeeded(autofill.profile(), bundle, sync_child);
- else
+ } else if (autofill.has_profile() && HasNotMigratedYet()) {
+ // Ignore autofill profiles if we are not upgrading.
+ AddNativeProfileIfNeeded(
+ autofill.profile(),
+ bundle,
+ sync_child,
+ all_profiles_from_db);
+ } else {
NOTREACHED() << "AutofillSpecifics has no autofill data!";
+ }
sync_child_id = sync_child.GetSuccessorId();
}
return true;
}
+// Define the functor to be used as the predicate in find_if call.
+struct CompareProfiles
+ : public std::binary_function<AutoFillProfile*, AutoFillProfile*, bool> {
+ bool operator() (AutoFillProfile* p1, AutoFillProfile* p2) const {
+ if (p1->Compare(*p2) == 0)
+ return true;
+ else
+ return false;
+ }
+};
+
+AutoFillProfile* AutofillModelAssociator::FindCorrespondingNodeFromWebDB(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db) {
+ static std::string guid(guid::GenerateGUID());
+ AutoFillProfile p;
+ p.set_guid(guid);
+ if (!FillProfileWithServerData(&p, profile)) {
+ // Not a big deal. We encountered an error. Just say this profile does not
+ // exist.
+ LOG(ERROR) << " Profile could not be associated";
+ return NULL;
+ }
+
+ // Now instantiate the functor and call find_if.
+ std::vector<AutoFillProfile*>::const_iterator ix =
+ std::find_if(all_profiles_from_db.begin(),
+ all_profiles_from_db.end(),
+ std::bind2nd(CompareProfiles(), &p));
+
+ return (ix == all_profiles_from_db.end()) ? NULL : *ix;
+}
+
void AutofillModelAssociator::AddNativeEntryIfNeeded(
const sync_pb::AutofillSpecifics& autofill, DataBundle* bundle,
const sync_api::ReadNode& node) {
@@ -364,16 +325,26 @@ void AutofillModelAssociator::AddNativeEntryIfNeeded(
}
void AutofillModelAssociator::AddNativeProfileIfNeeded(
- const sync_pb::AutofillProfileSpecifics& profile, DataBundle* bundle,
- const sync_api::ReadNode& node) {
+ const sync_pb::AutofillProfileSpecifics& profile,
+ DataBundle* bundle,
+ const sync_api::ReadNode& node,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db) {
+
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- if (bundle->current_profiles.find(UTF8ToUTF16(profile.label())) ==
- bundle->current_profiles.end()) {
- std::string tag(ProfileLabelToTag(UTF8ToUTF16(profile.label())));
- Associate(&tag, node.GetId());
- AutoFillProfile* p = personal_data_->
- CreateNewEmptyAutoFillProfileForDBThread(UTF8ToUTF16(profile.label()));
- OverwriteProfileWithServerData(p, profile);
+
+ scoped_ptr<AutoFillProfile> profile_in_web_db(FindCorrespondingNodeFromWebDB(
+ profile, all_profiles_from_db));
+
+ if (profile_in_web_db.get() != NULL) {
+ int64 sync_id = node.GetId();
+ std::string guid = profile_in_web_db->guid();
+ Associate(&guid, sync_id);
+ return;
+ } else { // Create a new node.
+ std::string guid = guid::GenerateGUID();
+ Associate(&guid, node.GetId());
+ AutoFillProfile* p = new AutoFillProfile(guid);
+ FillProfileWithServerData(p, profile);
bundle->new_profiles.push_back(p);
}
}
@@ -417,7 +388,8 @@ void AutofillModelAssociator::AbortAssociation() {
const std::string*
AutofillModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) {
- return NULL;
+ SyncIdToAutofillMap::const_iterator iter = id_map_inverse_.find(sync_id);
+ return iter == id_map_inverse_.end() ? NULL : &(iter->second);
}
bool AutofillModelAssociator::InitSyncNodeFromChromeId(
@@ -476,12 +448,6 @@ std::string AutofillModelAssociator::KeyToTag(const string16& name,
}
// static
-std::string AutofillModelAssociator::ProfileLabelToTag(const string16& label) {
- std::string ns(kAutofillProfileNamespaceTag);
- return ns + EscapePath(UTF16ToUTF8(label));
-}
-
-// static
bool AutofillModelAssociator::MergeTimestamps(
const sync_pb::AutofillSpecifics& autofill,
const std::vector<base::Time>& timestamps,
@@ -519,7 +485,7 @@ bool MergeField(FormGroup* f, AutoFillFieldType t,
}
// static
-bool AutofillModelAssociator::OverwriteProfileWithServerData(
+bool AutofillModelAssociator::FillProfileWithServerData(
AutoFillProfile* merge_into,
const sync_pb::AutofillProfileSpecifics& specifics) {
bool diff = false;
@@ -543,4 +509,8 @@ bool AutofillModelAssociator::OverwriteProfileWithServerData(
return diff;
}
+bool AutofillModelAssociator::HasNotMigratedYet() {
+ return true;
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_model_associator.h b/chrome/browser/sync/glue/autofill_model_associator.h
index d4e5363..ee81ed0 100644
--- a/chrome/browser/sync/glue/autofill_model_associator.h
+++ b/chrome/browser/sync/glue/autofill_model_associator.h
@@ -16,6 +16,7 @@
#include "base/ref_counted.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/autofill_model_associator2.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"
@@ -99,12 +100,11 @@ class AutofillModelAssociator
virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id);
static std::string KeyToTag(const string16& name, const string16& value);
- static std::string ProfileLabelToTag(const string16& label);
static bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill,
const std::vector<base::Time>& timestamps,
std::vector<base::Time>* new_timestamps);
- static bool OverwriteProfileWithServerData(
+ static bool FillProfileWithServerData(
AutoFillProfile* merge_into,
const sync_pb::AutofillProfileSpecifics& specifics);
@@ -114,15 +114,18 @@ class AutofillModelAssociator
// Returns sync service instance.
ProfileSyncService* sync_service() { return sync_service_; }
- // Compute and apply suffix to a label so that the resulting label is
- // unique in the sync database.
- // |new_non_unique_label| is the colliding label which is to be uniquified.
- // |existing_unique_label| is the current label of the object, if any; this
- // is treated as a unique label even if colliding. If no such label is
- // available, |existing_unique_label| may be empty.
- static string16 MakeUniqueLabel(const string16& new_non_unique_label,
- const string16& existing_unique_label,
- sync_api::BaseTransaction* trans);
+ protected:
+ // Is called to determine if we need to upgrade to the new
+ // autofillprofile2 data type. If so we need to sync up autofillprofile
+ // first to the latest available changes on the server and then upgrade
+ // to autofillprofile2.
+ virtual bool HasNotMigratedYet();
+
+ // Given a profile from sync db it tries to match the profile against
+ // one in web db. it ignores the guid and compares the actual data.
+ AutoFillProfile* FindCorrespondingNodeFromWebDB(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db);
private:
typedef std::map<std::string, int64> AutofillToSyncIdMap;
@@ -156,7 +159,8 @@ class AutofillModelAssociator
bool TraverseAndAssociateAllSyncNodes(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
- DataBundle* bundle);
+ DataBundle* bundle,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db);
// Helper to persist any changes that occured during model association to
// the WebDatabase.
@@ -173,7 +177,8 @@ class AutofillModelAssociator
void AddNativeProfileIfNeeded(
const sync_pb::AutofillProfileSpecifics& profile,
DataBundle* bundle,
- const sync_api::ReadNode& node);
+ const sync_api::ReadNode& node,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db);
// Helper to insert a sync node for the given AutoFillProfile (e.g. in
// response to encountering a native profile that doesn't exist yet in the
diff --git a/chrome/browser/sync/glue/autofill_model_associator2.cc b/chrome/browser/sync/glue/autofill_model_associator2.cc
new file mode 100644
index 0000000..272c8ae
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_model_associator2.cc
@@ -0,0 +1,547 @@
+// 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_associator2.h"
+
+#include <vector>
+
+#include "base/task.h"
+#include "base/time.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/autofill_change_processor.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 "net/base/escape.h"
+
+using base::TimeTicks;
+
+namespace browser_sync {
+
+extern const char kAutofillTag[];
+extern const char kAutofillEntryNamespaceTag[];
+const char kAutofillProfileNamespaceTag[] = "autofill_profile|";
+
+static const int kMaxNumAttemptsToFindUniqueLabel = 100;
+
+struct AutofillModelAssociator2::DataBundle {
+ std::set<AutofillKey> current_entries;
+ std::vector<AutofillEntry> new_entries;
+ std::set<string16> current_profiles;
+ std::vector<AutoFillProfile*> updated_profiles;
+ std::vector<AutoFillProfile*> new_profiles; // We own these pointers.
+ ~DataBundle() { STLDeleteElements(&new_profiles); }
+};
+
+AutofillModelAssociator2::DoOptimisticRefreshTask::DoOptimisticRefreshTask(
+ PersonalDataManager* pdm) : pdm_(pdm) {}
+
+AutofillModelAssociator2::DoOptimisticRefreshTask::~DoOptimisticRefreshTask() {}
+
+void AutofillModelAssociator2::DoOptimisticRefreshTask::Run() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ pdm_->Refresh();
+}
+
+AutofillModelAssociator2::AutofillModelAssociator2(
+ ProfileSyncService* sync_service,
+ WebDatabase* web_database,
+ PersonalDataManager* personal_data)
+ : sync_service_(sync_service),
+ web_database_(web_database),
+ personal_data_(personal_data),
+ autofill_node_id_(sync_api::kInvalidId),
+ abort_association_pending_(false) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ DCHECK(sync_service_);
+ DCHECK(web_database_);
+ DCHECK(personal_data_);
+}
+
+AutofillModelAssociator2::~AutofillModelAssociator2() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+}
+
+bool AutofillModelAssociator2::TraverseAndAssociateChromeAutofillEntries(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutofillEntry>& all_entries_from_db,
+ std::set<AutofillKey>* current_entries,
+ std::vector<AutofillEntry>* new_entries) {
+
+ const std::vector<AutofillEntry>& entries = all_entries_from_db;
+ for (std::vector<AutofillEntry>::const_iterator ix = entries.begin();
+ ix != entries.end(); ++ix) {
+ std::string tag = KeyToTag(ix->key().name(), ix->key().value());
+ if (id_map_.find(tag) != 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;
+ }
+
+ sync_api::ReadNode node(write_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)) {
+ AutofillEntry new_entry(ix->key(), timestamps);
+ new_entries->push_back(new_entry);
+
+ sync_api::WriteNode write_node(write_trans);
+ if (!write_node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
+ LOG(ERROR) << "Failed to write autofill sync node.";
+ return false;
+ }
+ AutofillChangeProcessor2::WriteAutofillEntry(new_entry, &write_node);
+ }
+
+ Associate(&tag, node.GetId());
+ } else {
+ sync_api::WriteNode node(write_trans);
+ if (!node.InitUniqueByCreation(syncable::AUTOFILL,
+ autofill_root, tag)) {
+ LOG(ERROR) << "Failed to create autofill sync node.";
+ return false;
+ }
+ node.SetTitle(UTF8ToWide(tag));
+ AutofillChangeProcessor2::WriteAutofillEntry(*ix, &node);
+ Associate(&tag, node.GetId());
+ }
+
+ current_entries->insert(ix->key());
+ }
+ return true;
+}
+
+bool AutofillModelAssociator2::TraverseAndAssociateChromeAutoFillProfiles(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ std::set<string16>* current_profiles,
+ std::vector<AutoFillProfile*>* updated_profiles) {
+ const std::vector<AutoFillProfile*>& profiles = all_profiles_from_db;
+ for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin();
+ ix != profiles.end(); ++ix) {
+ string16 label((*ix)->Label());
+ std::string tag(ProfileLabelToTag(label));
+
+ sync_api::ReadNode node(write_trans);
+ if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
+ const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics());
+ DCHECK(autofill.has_profile());
+ DCHECK_EQ(ProfileLabelToTag(UTF8ToUTF16(autofill.profile().label())),
+ tag);
+ int64 sync_id = node.GetId();
+ if (id_map_.find(tag) != id_map_.end()) {
+ // We just looked up something we already associated. Move aside.
+ label = MakeUniqueLabel(label, string16(), write_trans);
+ if (label.empty()) {
+ return false;
+ }
+ tag = ProfileLabelToTag(label);
+ // TODO(dhollowa): Replace with |AutoFillProfile::set_guid|.
+ // http://crbug.com/58813
+ (*ix)->set_label(label);
+ if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root,
+ tag, **ix, &sync_id)) {
+ return false;
+ }
+ updated_profiles->push_back(*ix);
+ } else {
+ // Overwrite local with cloud state.
+ if (OverwriteProfileWithServerData(*ix, autofill.profile()))
+ updated_profiles->push_back(*ix);
+ sync_id = node.GetId();
+ }
+
+ Associate(&tag, sync_id);
+ } else {
+ int64 id;
+ if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root,
+ tag, **ix, &id)) {
+ return false;
+ }
+ Associate(&tag, id);
+ }
+ current_profiles->insert(label);
+ }
+ return true;
+}
+
+// static
+string16 AutofillModelAssociator2::MakeUniqueLabel(
+ const string16& non_unique_label,
+ const string16& existing_unique_label,
+ sync_api::BaseTransaction* trans) {
+ if (!non_unique_label.empty() && non_unique_label == existing_unique_label) {
+ return existing_unique_label;
+ }
+ int unique_id = 1; // Priming so we start by appending "2".
+ while (unique_id++ < kMaxNumAttemptsToFindUniqueLabel) {
+ string16 suffix(base::IntToString16(unique_id));
+ string16 unique_label = non_unique_label + suffix;
+ if (unique_label == existing_unique_label)
+ return unique_label; // We'll use the one we already have.
+ sync_api::ReadNode node(trans);
+ if (node.InitByClientTagLookup(syncable::AUTOFILL,
+ ProfileLabelToTag(unique_label))) {
+ continue;
+ }
+ return unique_label;
+ }
+
+ LOG(ERROR) << "Couldn't create unique tag for autofill node. Srsly?!";
+ return string16();
+}
+
+bool AutofillModelAssociator2::MakeNewAutofillProfileSyncNode(
+ sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill_root,
+ const std::string& tag, const AutoFillProfile& profile, int64* sync_id) {
+ sync_api::WriteNode node(trans);
+ if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) {
+ LOG(ERROR) << "Failed to create autofill sync node.";
+ return false;
+ }
+ node.SetTitle(UTF8ToWide(tag));
+ AutofillChangeProcessor2::WriteAutofillProfile(profile, &node);
+ *sync_id = node.GetId();
+ return true;
+}
+
+
+bool AutofillModelAssociator2::LoadAutofillData(
+ std::vector<AutofillEntry>* entries,
+ std::vector<AutoFillProfile*>* profiles) {
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->GetAllAutofillEntries(entries))
+ return false;
+
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->GetAutoFillProfiles(profiles))
+ return false;
+
+ return true;
+}
+
+bool AutofillModelAssociator2::AssociateModels() {
+ VLOG(1) << "Associating Autofill Models";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ {
+ AutoLock lock(abort_association_pending_lock_);
+ abort_association_pending_ = false;
+ }
+
+ // TODO(zork): Attempt to load the model association from storage.
+ std::vector<AutofillEntry> entries;
+ ScopedVector<AutoFillProfile> profiles;
+
+ if (!LoadAutofillData(&entries, &profiles.get())) {
+ LOG(ERROR) << "Could not get the autofill data from WebDatabase.";
+ return false;
+ }
+
+ DataBundle bundle;
+ {
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+
+ sync_api::ReadNode autofill_root(&trans);
+ if (!autofill_root.InitByTagLookup(kAutofillTag)) {
+ LOG(ERROR) << "Server did not create the top-level autofill node. We "
+ << "might be running against an out-of-date server.";
+ return false;
+ }
+
+ if (!TraverseAndAssociateChromeAutofillEntries(&trans, autofill_root,
+ entries, &bundle.current_entries, &bundle.new_entries) ||
+ !TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root,
+ profiles.get(), &bundle.current_profiles,
+ &bundle.updated_profiles) ||
+ !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) {
+ return false;
+ }
+ }
+
+ // Since we're on the DB thread, we don't have to worry about updating
+ // the autofill database after closing the write transaction, since
+ // this is the only thread that writes to the database. We also don't have
+ // to worry about the sync model getting out of sync, because changes are
+ // propogated to the ChangeProcessor on this thread.
+ if (!SaveChangesToWebData(bundle)) {
+ LOG(ERROR) << "Failed to update autofill entries.";
+ return false;
+ }
+
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ new DoOptimisticRefreshTask(personal_data_));
+ return true;
+}
+
+bool AutofillModelAssociator2::SaveChangesToWebData(const DataBundle& bundle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (IsAbortPending())
+ return false;
+
+ if (bundle.new_entries.size() &&
+ !web_database_->UpdateAutofillEntries(bundle.new_entries)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i]))
+ return false;
+ }
+
+ for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i]))
+ return false;
+ }
+ return true;
+}
+
+bool AutofillModelAssociator2::TraverseAndAssociateAllSyncNodes(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ DataBundle* bundle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ int64 sync_child_id = autofill_root.GetFirstChildId();
+ while (sync_child_id != sync_api::kInvalidId) {
+ sync_api::ReadNode sync_child(write_trans);
+ if (!sync_child.InitByIdLookup(sync_child_id)) {
+ LOG(ERROR) << "Failed to fetch child node.";
+ return false;
+ }
+ const sync_pb::AutofillSpecifics& autofill(
+ sync_child.GetAutofillSpecifics());
+
+ if (autofill.has_value())
+ AddNativeEntryIfNeeded(autofill, bundle, sync_child);
+ else if (autofill.has_profile())
+ AddNativeProfileIfNeeded(autofill.profile(), bundle, sync_child);
+ else
+ NOTREACHED() << "AutofillSpecifics has no autofill data!";
+
+ sync_child_id = sync_child.GetSuccessorId();
+ }
+ return true;
+}
+
+void AutofillModelAssociator2::AddNativeEntryIfNeeded(
+ const sync_pb::AutofillSpecifics& autofill, DataBundle* bundle,
+ const sync_api::ReadNode& node) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillKey key(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
+
+ if (bundle->current_entries.find(key) == bundle->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)));
+ }
+ std::string tag(KeyToTag(key.name(), key.value()));
+ Associate(&tag, node.GetId());
+ bundle->new_entries.push_back(AutofillEntry(key, timestamps));
+ }
+}
+
+void AutofillModelAssociator2::AddNativeProfileIfNeeded(
+ const sync_pb::AutofillProfileSpecifics& profile, DataBundle* bundle,
+ const sync_api::ReadNode& node) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (bundle->current_profiles.find(UTF8ToUTF16(profile.label())) ==
+ bundle->current_profiles.end()) {
+ std::string tag(ProfileLabelToTag(UTF8ToUTF16(profile.label())));
+ Associate(&tag, node.GetId());
+ AutoFillProfile* p = personal_data_->
+ CreateNewEmptyAutoFillProfileForDBThread(UTF8ToUTF16(profile.label()));
+ OverwriteProfileWithServerData(p, profile);
+ bundle->new_profiles.push_back(p);
+ }
+}
+
+bool AutofillModelAssociator2::DisassociateModels() {
+ id_map_.clear();
+ id_map_inverse_.clear();
+ return true;
+}
+
+bool AutofillModelAssociator2::SyncModelHasUserCreatedNodes(bool* has_nodes) {
+ DCHECK(has_nodes);
+ *has_nodes = false;
+ int64 autofill_sync_id;
+ if (!GetSyncIdForTaggedNode(kAutofillTag, &autofill_sync_id)) {
+ 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)) {
+ 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.
+ *has_nodes = sync_api::kInvalidId != autofill_node.GetFirstChildId();
+ return true;
+}
+
+void AutofillModelAssociator2::AbortAssociation() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ AutoLock lock(abort_association_pending_lock_);
+ abort_association_pending_ = true;
+}
+
+const std::string*
+AutofillModelAssociator2::GetChromeNodeFromSyncId(int64 sync_id) {
+ return NULL;
+}
+
+bool AutofillModelAssociator2::InitSyncNodeFromChromeId(
+ std::string node_id,
+ sync_api::BaseNode* sync_node) {
+ return false;
+}
+
+int64 AutofillModelAssociator2::GetSyncIdFromChromeId(
+ const std::string autofill) {
+ AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill);
+ return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
+}
+
+void AutofillModelAssociator2::Associate(
+ const std::string* autofill, int64 sync_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ 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;
+}
+
+void AutofillModelAssociator2::Disassociate(int64 sync_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ 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);
+}
+
+bool AutofillModelAssociator2::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;
+}
+
+bool AutofillModelAssociator2::IsAbortPending() {
+ AutoLock lock(abort_association_pending_lock_);
+ return abort_association_pending_;
+}
+
+// static
+std::string AutofillModelAssociator2::KeyToTag(const string16& name,
+ const string16& value) {
+ std::string ns(kAutofillEntryNamespaceTag);
+ return ns + EscapePath(UTF16ToUTF8(name)) + "|" +
+ EscapePath(UTF16ToUTF8(value));
+}
+
+// static
+std::string AutofillModelAssociator2::ProfileLabelToTag(const string16& label) {
+ std::string ns(kAutofillProfileNamespaceTag);
+ return ns + EscapePath(UTF16ToUTF8(label));
+}
+
+// static
+bool AutofillModelAssociator2::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;
+}
+
+// Helper to compare the local value and cloud value of a field, merge into
+// the local value if they differ, and return whether the merge happened.
+bool MergeField2(FormGroup* f, AutoFillFieldType t,
+ const std::string& specifics_field) {
+ if (UTF16ToUTF8(f->GetFieldText(AutoFillType(t))) == specifics_field)
+ return false;
+ f->SetInfo(AutoFillType(t), UTF8ToUTF16(specifics_field));
+ return true;
+}
+
+// static
+bool AutofillModelAssociator2::OverwriteProfileWithServerData(
+ AutoFillProfile* merge_into,
+ const sync_pb::AutofillProfileSpecifics& specifics) {
+ bool diff = false;
+ AutoFillProfile* p = merge_into;
+ const sync_pb::AutofillProfileSpecifics& s(specifics);
+ diff = MergeField2(p, NAME_FIRST, s.name_first()) || diff;
+ diff = MergeField2(p, NAME_LAST, s.name_last()) || diff;
+ diff = MergeField2(p, NAME_MIDDLE, s.name_middle()) || diff;
+ diff = MergeField2(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff;
+ diff = MergeField2(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff;
+ diff = MergeField2(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff;
+ diff = MergeField2(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff;
+ diff = MergeField2(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff;
+ diff = MergeField2(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff;
+ diff = MergeField2(p, EMAIL_ADDRESS, s.email_address()) || diff;
+ diff = MergeField2(p, COMPANY_NAME, s.company_name()) || diff;
+ diff = MergeField2(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number())
+ || diff;
+ diff = MergeField2(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number())
+ || diff;
+ return diff;
+}
+
+} // namespace browser_sync
+
diff --git a/chrome/browser/sync/glue/autofill_model_associator2.h b/chrome/browser/sync/glue/autofill_model_associator2.h
new file mode 100644
index 0000000..0ad37de
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_model_associator2.h
@@ -0,0 +1,212 @@
+// 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_ASSOCIATOR2_H_
+#define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_MODEL_ASSOCIATOR2_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/sync/engine/syncapi.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 AutoFillProfile;
+
+class ProfileSyncService;
+class WebDatabase;
+
+namespace sync_api {
+class WriteTransaction;
+}
+
+namespace browser_sync {
+
+class AutofillChangeProcessor;
+class UnrecoverableErrorHandler;
+
+extern const char kAutofillTag[];
+extern const char kAutofillProfileNamespaceTag[];
+extern const char kAutofillEntryNamespaceTag[];
+
+// Contains all model association related logic:
+// * Algorithm to associate autofill model and sync model.
+// We do not check if we have local data before this run; we always
+// merge and sync.
+class AutofillModelAssociator2
+ : public PerDataTypeAssociatorInterface<std::string, std::string> {
+ public:
+ static syncable::ModelType model_type() { return syncable::AUTOFILL; }
+ AutofillModelAssociator2(ProfileSyncService* sync_service,
+ WebDatabase* web_database,
+ PersonalDataManager* data_manager);
+ virtual ~AutofillModelAssociator2();
+
+ // A task used by this class and the change processor to inform the
+ // PersonalDataManager living on the UI thread that it needs to refresh.
+ class DoOptimisticRefreshTask : public Task {
+ public:
+ explicit DoOptimisticRefreshTask(PersonalDataManager* pdm);
+ virtual ~DoOptimisticRefreshTask();
+ virtual void Run();
+ private:
+ scoped_refptr<PersonalDataManager> pdm_;
+ };
+
+ // PerDataTypeAssociatorInterface implementation.
+ //
+ // Iterates through the sync model looking for matched pairs of items.
+ virtual bool AssociateModels();
+
+ // Clears all associations.
+ virtual bool DisassociateModels();
+
+ // The has_nodes out param is true if the sync model has nodes other
+ // than the permanent tagged nodes.
+ virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes);
+
+ // See ModelAssociator interface.
+ virtual void AbortAssociation();
+
+ // Not implemented.
+ virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id);
+
+ // Not implemented.
+ virtual bool InitSyncNodeFromChromeId(std::string node_id,
+ sync_api::BaseNode* sync_node);
+
+ // 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(std::string node_id);
+
+ // Associates the given autofill name with the given sync id.
+ virtual void Associate(const std::string* 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.
+ virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id);
+
+ static std::string KeyToTag(const string16& name, const string16& value);
+ static std::string ProfileLabelToTag(const string16& label);
+
+ static bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill,
+ const std::vector<base::Time>& timestamps,
+ std::vector<base::Time>* new_timestamps);
+ static bool OverwriteProfileWithServerData(
+ AutoFillProfile* merge_into,
+ const sync_pb::AutofillProfileSpecifics& specifics);
+
+ // TODO(georgey) : add the same processing for CC info (already in protocol
+ // buffers).
+
+ // Returns sync service instance.
+ ProfileSyncService* sync_service() { return sync_service_; }
+
+ // Compute and apply suffix to a label so that the resulting label is
+ // unique in the sync database.
+ // |new_non_unique_label| is the colliding label which is to be uniquified.
+ // |existing_unique_label| is the current label of the object, if any; this
+ // is treated as a unique label even if colliding. If no such label is
+ // available, |existing_unique_label| may be empty.
+ static string16 MakeUniqueLabel(const string16& new_non_unique_label,
+ const string16& existing_unique_label,
+ sync_api::BaseTransaction* trans);
+
+ private:
+ typedef std::map<std::string, int64> AutofillToSyncIdMap;
+ typedef std::map<int64, std::string> SyncIdToAutofillMap;
+
+ // A convenience wrapper of a bunch of state we pass around while associating
+ // models, and send to the WebDatabase for persistence.
+ struct DataBundle;
+
+ // Helper to query WebDatabase for the current autofill state.
+ bool LoadAutofillData(std::vector<AutofillEntry>* entries,
+ std::vector<AutoFillProfile*>* profiles);
+
+ // We split up model association first by autofill sub-type (entries, and
+ // profiles. There is a Traverse* method for each of these.
+ bool TraverseAndAssociateChromeAutofillEntries(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutofillEntry>& all_entries_from_db,
+ std::set<AutofillKey>* current_entries,
+ std::vector<AutofillEntry>* new_entries);
+ bool TraverseAndAssociateChromeAutoFillProfiles(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ std::set<string16>* current_profiles,
+ std::vector<AutoFillProfile*>* updated_profiles);
+
+ // Once the above traversals are complete, we traverse the sync model to
+ // associate all remaining nodes.
+ bool TraverseAndAssociateAllSyncNodes(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ DataBundle* bundle);
+
+ // Helper to persist any changes that occured during model association to
+ // the WebDatabase.
+ bool SaveChangesToWebData(const DataBundle& bundle);
+
+ // Helper to insert an AutofillEntry into the WebDatabase (e.g. in response
+ // to encountering a sync node that doesn't exist yet locally).
+ void AddNativeEntryIfNeeded(const sync_pb::AutofillSpecifics& autofill,
+ DataBundle* bundle,
+ const sync_api::ReadNode& node);
+
+ // Helper to insert an AutoFillProfile into the WebDatabase (e.g. in response
+ // to encountering a sync node that doesn't exist yet locally).
+ void AddNativeProfileIfNeeded(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ DataBundle* bundle,
+ const sync_api::ReadNode& node);
+
+ // Helper to insert a sync node for the given AutoFillProfile (e.g. in
+ // response to encountering a native profile that doesn't exist yet in the
+ // cloud).
+ bool MakeNewAutofillProfileSyncNode(
+ sync_api::WriteTransaction* trans,
+ const sync_api::BaseNode& autofill_root,
+ const std::string& tag,
+ const AutoFillProfile& profile,
+ int64* sync_id);
+
+ // Called at various points in model association to determine if the
+ // user requested an abort.
+ bool IsAbortPending();
+
+ ProfileSyncService* sync_service_;
+ WebDatabase* web_database_;
+ PersonalDataManager* personal_data_;
+ int64 autofill_node_id_;
+
+ AutofillToSyncIdMap id_map_;
+ SyncIdToAutofillMap id_map_inverse_;
+
+ // Abort association pending flag and lock. If this is set to true
+ // (via the AbortAssociation method), return from the
+ // AssociateModels method as soon as possible.
+ Lock abort_association_pending_lock_;
+ bool abort_association_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillModelAssociator2);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_AUTOFILL_MODEL_ASSOCIATOR2_H_
+
diff --git a/chrome/browser/sync/glue/autofill_model_associator_unittest.cc b/chrome/browser/sync/glue/autofill_model_associator_unittest.cc
index cfd7e8d..a76f634 100644
--- a/chrome/browser/sync/glue/autofill_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/autofill_model_associator_unittest.cc
@@ -26,15 +26,3 @@ TEST_F(AutofillModelAssociatorTest, KeyToTag) {
AutofillModelAssociator::KeyToTag(UTF8ToUTF16(""),
UTF8ToUTF16("|")));
}
-
-TEST_F(AutofillModelAssociatorTest, ProfileLabelToTag) {
- string16 label(ASCIIToUTF16("awesome_address"));
- EXPECT_EQ("autofill_profile|awesome_address",
- AutofillModelAssociator::ProfileLabelToTag(label));
-
- EXPECT_EQ("autofill_profile|%7C%7C",
- AutofillModelAssociator::ProfileLabelToTag(ASCIIToUTF16("||")));
- EXPECT_NE(AutofillModelAssociator::KeyToTag(ASCIIToUTF16("autofill_profile"),
- ASCIIToUTF16("home")),
- AutofillModelAssociator::ProfileLabelToTag(ASCIIToUTF16("home")));
-}
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator.cc b/chrome/browser/sync/glue/autofill_profile_model_associator.cc
new file mode 100644
index 0000000..0680da8
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.cc
@@ -0,0 +1,371 @@
+// 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_profile_model_associator.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/webdata/web_database.h"
+
+using sync_api::ReadNode;
+namespace browser_sync {
+
+const char kAutofillProfileTag[] = "google_chrome_autofill_profile";
+
+AutofillProfileModelAssociator::AutofillProfileModelAssociator(
+ ProfileSyncService* sync_service,
+ WebDatabase* web_database,
+ PersonalDataManager* personal_data)
+ : sync_service_(sync_service),
+ web_database_(web_database),
+ personal_data_(personal_data),
+ autofill_node_id_(sync_api::kInvalidId),
+ abort_association_pending_(false) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ DCHECK(sync_service_);
+ DCHECK(web_database_);
+ DCHECK(personal_data_);
+}
+
+AutofillProfileModelAssociator::~AutofillProfileModelAssociator() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+}
+
+bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ std::set<std::string>* current_profiles,
+ std::vector<AutoFillProfile*>* updated_profiles,
+ std::vector<AutoFillProfile*>* new_profiles,
+ std::vector<std::string>* profiles_to_delete) {
+
+ // Alias the all_profiles_from_db so we fit in 80 characters
+ const std::vector<AutoFillProfile*>& profiles(all_profiles_from_db);
+ for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin();
+ ix != profiles.end();
+ ++ix) {
+ std::string guid((*ix)->guid());
+
+ ReadNode node(write_trans);
+ if (node.InitByClientTagLookup(syncable::AUTOFILL_PROFILE, guid)) {
+ const sync_pb::AutofillProfileSpecifics& autofill(
+ node.GetAutofillProfileSpecifics());
+ if (OverwriteProfileWithServerData(*ix, autofill)) {
+ updated_profiles->push_back(*ix);
+ }
+ Associate(&guid, node.GetId());
+ current_profiles->insert(guid);
+ } else {
+ MakeNewAutofillProfileSyncNodeIfNeeded(write_trans,
+ autofill_root,
+ (**ix),
+ new_profiles,
+ current_profiles,
+ profiles_to_delete);
+ }
+ }
+
+ return true;
+}
+
+bool AutofillProfileModelAssociator::LoadAutofillData(
+ std::vector<AutoFillProfile*>* profiles) {
+ if (IsAbortPending())
+ return false;
+
+ if (!web_database_->GetAutoFillProfiles(profiles))
+ return false;
+
+ return true;
+}
+
+bool AutofillProfileModelAssociator::AssociateModels() {
+ VLOG(1) << "Associating Autofill Models";
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ {
+ AutoLock lock(abort_association_pending_lock_);
+ abort_association_pending_ = false;
+ }
+
+ ScopedVector<AutoFillProfile> profiles;
+
+ if (!LoadAutofillData(&profiles.get())) {
+ LOG(ERROR) << "Could not get the autofill data from WebDatabase.";
+ return false;
+ }
+
+ DataBundle bundle;
+ {
+ // The write transaction lock is held inside this block.
+ // We do all the web db operations outside this block.
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+
+ sync_api::ReadNode autofill_root(&trans);
+ if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) {
+ LOG(ERROR) << "Server did not create the top-level autofill node. We "
+ << "might be running against an out-of-date server.";
+ return false;
+ }
+
+ if (!TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root,
+ profiles.get(), &bundle.current_profiles,
+ &bundle.updated_profiles,
+ &bundle.new_profiles,
+ &bundle.profiles_to_delete) ||
+ !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) {
+ return false;
+ }
+ }
+
+ if (!SaveChangesToWebData(bundle)) {
+ LOG(ERROR) << "Failed to update autofill entries.";
+ return false;
+ }
+
+ // TODO(lipalani) Bug 64111- split out the OptimisticRefreshTask
+ // into its own class
+ // from autofill_model_associator
+ // Will be done as part of the autofill_model_associator work.
+ // BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ // new DoOptimisticRefreshTask(personal_data_));
+ return true;
+}
+
+bool AutofillProfileModelAssociator::DisassociateModels() {
+ id_map_.clear();
+ id_map_inverse_.clear();
+ return true;
+}
+
+// Helper to compare the local value and cloud value of a field, merge into
+// the local value if they differ, and return whether the merge happened.
+bool AutofillProfileModelAssociator::MergeField(FormGroup* f,
+ AutoFillFieldType t,
+ const std::string& specifics_field) {
+ if (UTF16ToUTF8(f->GetFieldText(AutoFillType(t))) == specifics_field)
+ return false;
+ f->SetInfo(AutoFillType(t), UTF8ToUTF16(specifics_field));
+ return true;
+}
+bool AutofillProfileModelAssociator::SyncModelHasUserCreatedNodes(
+ bool *has_nodes) {
+ CHECK_NE(has_nodes, reinterpret_cast<bool*>(NULL));
+ sync_api::ReadTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+
+ sync_api::ReadNode node(&trans);
+
+ if (!node.InitByClientTagLookup(
+ syncable::AUTOFILL_PROFILE,
+ kAutofillProfileTag)) {
+ LOG(ERROR) << "Sever did not create a top level node"
+ << "Out of data server or autofill type not enabled";
+ return false;
+ }
+
+ *has_nodes = sync_api::kInvalidId != node.GetFirstChildId();
+ return true;
+}
+// static
+bool AutofillProfileModelAssociator::OverwriteProfileWithServerData(
+ AutoFillProfile* merge_into,
+ const sync_pb::AutofillProfileSpecifics& specifics) {
+ bool diff = false;
+ AutoFillProfile* p = merge_into;
+ const sync_pb::AutofillProfileSpecifics& s(specifics);
+ diff = MergeField(p, NAME_FIRST, s.name_first()) || diff;
+ diff = MergeField(p, NAME_LAST, s.name_last()) || diff;
+ diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff;
+ diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff;
+ diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff;
+ diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff;
+ diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff;
+ diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff;
+ diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff;
+ diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff;
+ diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff;
+ diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number())
+ || diff;
+ diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number())
+ || diff;
+ return diff;
+}
+
+
+int64 AutofillProfileModelAssociator::FindSyncNodeWithProfile(
+ sync_api::WriteTransaction* trans,
+ const sync_api::BaseNode& autofill_root,
+ const AutoFillProfile& profile_from_db) {
+ int64 sync_child_id = autofill_root.GetFirstChildId();
+ while (sync_child_id != sync_api::kInvalidId) {
+ ReadNode read_node(trans);
+ AutoFillProfile p;
+ if (read_node.InitByIdLookup(sync_child_id)) {
+ LOG(ERROR) << "unable to find the id given by getfirst child " <<
+ sync_child_id;
+ return sync_api::kInvalidId;
+ }
+ const sync_pb::AutofillProfileSpecifics& autofill_specifics(
+ read_node.GetAutofillProfileSpecifics());
+ OverwriteProfileWithServerData(&p, autofill_specifics);
+ if (p.Compare(profile_from_db) == 0) {
+ return sync_child_id;
+ }
+ sync_child_id = read_node.GetSuccessorId();
+ }
+
+ return sync_api::kInvalidId;
+}
+bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
+ sync_api::WriteTransaction* trans,
+ const sync_api::BaseNode& autofill_root,
+ const AutoFillProfile& profile,
+ std::vector<AutoFillProfile*>* new_profiles,
+ std::set<std::string>* current_profiles,
+ std::vector<std::string>* profiles_to_delete) {
+
+ int64 sync_node_id = FindSyncNodeWithProfile(trans, autofill_root, profile);
+ if (sync_node_id != sync_api::kInvalidId) {
+ // In case of duplicates throw away the local profile and apply the
+ // server profile.(The only difference between the 2 profiles are the guids)
+ profiles_to_delete->push_back(profile.guid());
+ sync_api::ReadNode read_node(trans);
+ if (!read_node.InitByIdLookup(sync_node_id)) {
+ LOG(ERROR);
+ return false;
+ }
+ const sync_pb::AutofillProfileSpecifics& autofill_specifics(
+ read_node.GetAutofillProfileSpecifics());
+ AutoFillProfile* p = new AutoFillProfile(autofill_specifics.guid());
+ OverwriteProfileWithServerData(p, autofill_specifics);
+ new_profiles->push_back(p);
+ std::string guid = autofill_specifics.guid();
+ Associate(&guid, sync_node_id);
+ current_profiles->insert(autofill_specifics.guid());
+ } else {
+ sync_api::WriteNode node(trans);
+ if (!node.InitUniqueByCreation(
+ syncable::AUTOFILL_PROFILE, autofill_root, profile.guid())) {
+ LOG(ERROR) << "Failed to create autofill sync node.";
+ return false;
+ }
+ node.SetTitle(UTF8ToWide(profile.guid()));
+
+ // TODO(lipalani) -Bug 64111 This needs rewriting. This will be tackled
+ // when rewriting autofill change processor.
+ // AutofillChangeProcessor::WriteAutofillProfile(profile, &node);
+ }
+ return true;
+}
+
+bool AutofillProfileModelAssociator::TraverseAndAssociateAllSyncNodes(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ DataBundle* bundle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ int64 sync_child_id = autofill_root.GetFirstChildId();
+ while (sync_child_id != sync_api::kInvalidId) {
+ ReadNode sync_child(write_trans);
+ if (!sync_child.InitByIdLookup(sync_child_id)) {
+ LOG(ERROR) << "Failed to fetch child node.";
+ return false;
+ }
+ const sync_pb::AutofillProfileSpecifics& autofill(
+ sync_child.GetAutofillProfileSpecifics());
+
+ AddNativeProfileIfNeeded(autofill, bundle, sync_child);
+
+ sync_child_id = sync_child.GetSuccessorId();
+ }
+ return true;
+}
+
+void AutofillProfileModelAssociator::AddNativeProfileIfNeeded(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ DataBundle* bundle,
+ const sync_api::ReadNode& node) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (bundle->current_profiles.find(profile.guid()) ==
+ bundle->current_profiles.end()) {
+ std::string guid(profile.guid());
+ Associate(&guid, node.GetId());
+ AutoFillProfile* p = new AutoFillProfile(profile.guid());
+ OverwriteProfileWithServerData(p, profile);
+ bundle->new_profiles.push_back(p);
+ }
+}
+
+bool AutofillProfileModelAssociator::SaveChangesToWebData(
+ const DataBundle& bundle) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (IsAbortPending())
+ return false;
+
+ for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i]))
+ return false;
+ }
+
+ for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i]))
+ return false;
+ }
+
+ for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) {
+ if (IsAbortPending())
+ return false;
+ if (!web_database_->RemoveAutoFillProfile(bundle.profiles_to_delete[i]))
+ return false;
+ }
+ return true;
+}
+
+void AutofillProfileModelAssociator::Associate(
+ const std::string* autofill,
+ int64 sync_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ 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;
+}
+
+void AutofillProfileModelAssociator::Disassociate(int64 sync_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ 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);
+}
+
+int64 AutofillProfileModelAssociator::GetSyncIdFromChromeId(
+ const std::string autofill) {
+ AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill);
+ return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
+}
+
+void AutofillProfileModelAssociator::AbortAssociation() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ AutoLock lock(abort_association_pending_lock_);
+ abort_association_pending_ = true;
+}
+
+bool AutofillProfileModelAssociator::IsAbortPending() {
+ AutoLock lock(abort_association_pending_lock_);
+ return abort_association_pending_;
+}
+
+} // namespace browser_sync
+
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator.h b/chrome/browser/sync/glue/autofill_profile_model_associator.h
new file mode 100644
index 0000000..75c75ef
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.h
@@ -0,0 +1,199 @@
+// 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_PROFILE_MODEL_ASSOCIATOR_H_
+#define CHROME_BROWSER_SYNC_GLUE_AUTOFILL_PROFILE_MODEL_ASSOCIATOR_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/sync/engine/syncapi.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 AutoFillProfile;
+
+class ProfileSyncService;
+class WebDatabase;
+
+namespace sync_api {
+class WriteTransaction;
+}
+
+namespace browser_sync {
+
+class AutofillChangeProcessor;
+class UnrecoverableErrorHandler;
+
+// Contains all model association related logic:
+// * Algorithm to associate autofill model and sync model.
+// We do not check if we have local data before this run; we always
+// merge and sync.
+class AutofillProfileModelAssociator
+ : public PerDataTypeAssociatorInterface<std::string, std::string> {
+ public:
+ AutofillProfileModelAssociator(ProfileSyncService* sync_service,
+ WebDatabase* web_database,
+ PersonalDataManager* data_manager);
+ virtual ~AutofillProfileModelAssociator();
+
+ // A convenience wrapper of a bunch of state we pass around while
+ // associating models, and send to the WebDatabase for persistence.
+ // We do this so we hold the write lock for only a small period.
+ // When storing the web db we are out of the write lock.
+ struct DataBundle;
+
+ static syncable::ModelType model_type() { return syncable::AUTOFILL_PROFILE; }
+
+ // PerDataTypeAssociatorInterface implementation.
+ //
+ // Iterates through the sync model looking for matched pairs of items.
+ virtual bool AssociateModels();
+
+ // Clears all associations.
+ virtual bool DisassociateModels();
+
+ // TODO(lipalani) Bug 64111.
+ // The has_nodes out param is true if the sync model has nodes other
+ // than the permanent tagged nodes.
+ virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes);
+
+ // See ModelAssociator interface.
+ virtual void AbortAssociation();
+
+ virtual const std::string* GetChromeNodeFromSyncId(
+ int64 sync_id) {
+ return NULL;
+ }
+
+ virtual bool InitSyncNodeFromChromeId(std::string 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(std::string node_id);
+
+ // Associates the given autofill name with the given sync id.
+ virtual void Associate(const std::string* node, int64 sync_id);
+
+ // Remove the association that corresponds to the given sync id.
+ virtual void Disassociate(int64 sync_id);
+
+ // TODO(lipalani) Bug 64111. Returns whether a node with the
+ // given permanent tag was found and update
+ // |sync_id| with that node's id. No current use. To Implement
+ // only for completeness.
+ virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id) {
+ return false;
+ }
+
+ // Returns sync service instance.
+ ProfileSyncService* sync_service() { return sync_service_; }
+
+ protected:
+ AutofillProfileModelAssociator() {}
+ bool TraverseAndAssociateChromeAutoFillProfiles(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ std::set<std::string>* current_profiles,
+ std::vector<AutoFillProfile*>* updated_profiles,
+ std::vector<AutoFillProfile*>* new_profiles,
+ std::vector<std::string>* profiles_to_delete);
+
+ // Helper to insert an AutoFillProfile into the WebDatabase (e.g. in response
+ // to encountering a sync node that doesn't exist yet locally).
+ virtual void AddNativeProfileIfNeeded(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ DataBundle* bundle,
+ const sync_api::ReadNode& node);
+
+ // Helper to insert a sync node for the given AutoFillProfile (e.g. in
+ // response to encountering a native profile that doesn't exist yet in the
+ // cloud).
+ virtual bool MakeNewAutofillProfileSyncNodeIfNeeded(
+ sync_api::WriteTransaction* trans,
+ const sync_api::BaseNode& autofill_root,
+ const AutoFillProfile& profile,
+ std::vector<AutoFillProfile*>* new_profiles,
+ std::set<std::string>* current_profiles,
+ std::vector<std::string>* profiles_to_delete);
+
+ // Once the above traversals are complete, we traverse the sync model to
+ // associate all remaining nodes.
+ bool TraverseAndAssociateAllSyncNodes(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ DataBundle* bundle);
+
+ static bool OverwriteProfileWithServerData(
+ AutoFillProfile* merge_into,
+ const sync_pb::AutofillProfileSpecifics& specifics);
+
+ private:
+ typedef std::map<std::string, int64> AutofillToSyncIdMap;
+ typedef std::map<int64, std::string> SyncIdToAutofillMap;
+
+ // A convenience wrapper of a bunch of state we pass around while associating
+ // models, and send to the WebDatabase for persistence.
+ // struct DataBundle;
+
+ // Helper to query WebDatabase for the current autofill state.
+ bool LoadAutofillData(std::vector<AutoFillProfile*>* profiles);
+
+ static bool MergeField(FormGroup* f,
+ AutoFillFieldType t,
+ const std::string& specifics_field);
+
+ // Helper to persist any changes that occured during model association to
+ // the WebDatabase.
+ bool SaveChangesToWebData(const DataBundle& bundle);
+
+ // Called at various points in model association to determine if the
+ // user requested an abort.
+ bool IsAbortPending();
+
+ int64 FindSyncNodeWithProfile(sync_api::WriteTransaction* trans,
+ const sync_api::BaseNode& autofill_root,
+ const AutoFillProfile& profile);
+
+ ProfileSyncService* sync_service_;
+ WebDatabase* web_database_;
+ PersonalDataManager* personal_data_;
+ int64 autofill_node_id_;
+
+ AutofillToSyncIdMap id_map_;
+ SyncIdToAutofillMap id_map_inverse_;
+
+ // Abort association pending flag and lock. If this is set to true
+ // (via the AbortAssociation method), return from the
+ // AssociateModels method as soon as possible.
+ Lock abort_association_pending_lock_;
+ bool abort_association_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillProfileModelAssociator);
+};
+
+struct AutofillProfileModelAssociator::DataBundle {
+ std::set<std::string> current_profiles;
+ std::vector<std::string> profiles_to_delete;
+ std::vector<AutoFillProfile*> updated_profiles;
+ std::vector<AutoFillProfile*> new_profiles; // We own these pointers.
+ ~DataBundle() { STLDeleteElements(&new_profiles); }
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_AUTOFILL_PROFILE_MODEL_ASSOCIATOR_H_
+
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc b/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc
new file mode 100644
index 0000000..ee6a9fc
--- /dev/null
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc
@@ -0,0 +1,261 @@
+// 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/utf_string_conversions.h"
+#include "chrome/browser/sync/engine/read_node_mock.h"
+#include "chrome/browser/sync/engine/syncapi_mock.h"
+#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/browser/sync/syncable/syncable_mock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::DoDefault;
+using ::testing::ReturnRef;
+using ::testing::Pointee;
+using ::testing::Ref;
+using ::testing::Invoke;
+class AutoFillProfile;
+
+using browser_sync::AutofillProfileModelAssociator;
+
+// Note this is not a generic mock. This is a mock used to
+// test AutofillProfileModelAssociator class itself by mocking
+// other functions that are called by the functions we need to test.
+class MockAutofillProfileModelAssociator
+ : public AutofillProfileModelAssociator {
+ public:
+ MockAutofillProfileModelAssociator() {
+ }
+ virtual ~MockAutofillProfileModelAssociator() {}
+ bool TraverseAndAssociateChromeAutoFillProfilesWrapper(
+ sync_api::WriteTransaction* write_trans,
+ const sync_api::ReadNode& autofill_root,
+ const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ std::set<std::string>* current_profiles,
+ std::vector<AutoFillProfile*>* updated_profiles,
+ std::vector<AutoFillProfile*>* new_profiles,
+ std::vector<std::string>* profiles_to_delete) {
+ return TraverseAndAssociateChromeAutoFillProfiles(write_trans,
+ autofill_root,
+ all_profiles_from_db,
+ current_profiles,
+ updated_profiles,
+ new_profiles,
+ profiles_to_delete);
+ }
+ MOCK_METHOD3(AddNativeProfileIfNeeded,
+ void(const sync_pb::AutofillProfileSpecifics&,
+ DataBundle*,
+ const sync_api::ReadNode&));
+ MOCK_METHOD2(OverwriteProfileWithServerData,
+ bool(AutoFillProfile*,
+ const sync_pb::AutofillProfileSpecifics&));
+ MOCK_METHOD6(MakeNewAutofillProfileSyncNodeIfNeeded,
+ bool(sync_api::WriteTransaction*,
+ const sync_api::BaseNode&,
+ const AutoFillProfile&,
+ std::vector<AutoFillProfile*>*,
+ std::set<std::string>*,
+ std::vector<std::string>*));
+ MOCK_METHOD2(Associate, void(const std::string*, int64));
+
+ bool TraverseAndAssociateAllSyncNodesWrapper(
+ sync_api::WriteTransaction *trans,
+ const sync_api::ReadNode &autofill_root,
+ browser_sync::AutofillProfileModelAssociator::DataBundle *bundle) {
+ return TraverseAndAssociateAllSyncNodes(trans, autofill_root, bundle);
+ }
+
+ void AddNativeProfileIfNeededWrapper(
+ const sync_pb::AutofillProfileSpecifics& profile,
+ DataBundle* bundle,
+ const sync_api::ReadNode& node) {
+ AutofillProfileModelAssociator::AddNativeProfileIfNeeded(
+ profile,
+ bundle,
+ node);
+ }
+};
+
+class AutofillProfileModelAssociatorTest : public testing::Test {
+ public:
+ AutofillProfileModelAssociatorTest()
+ : db_thread_(BrowserThread::DB, &message_loop_) {}
+
+ protected:
+ MessageLoop message_loop_;
+ BrowserThread db_thread_;
+ MockAutofillProfileModelAssociator associator_;
+};
+
+TEST_F(AutofillProfileModelAssociatorTest,
+ TestAssociateProfileInWebDBWithSyncDB) {
+ ScopedVector<AutoFillProfile> profiles_from_web_db;
+ std::string guid = "abc";
+
+ sync_pb::EntitySpecifics specifics;
+ MockDirectory mock_directory;
+ sync_pb::AutofillProfileSpecifics *profile_specifics =
+ specifics.MutableExtension(sync_pb::autofill_profile);
+
+ profile_specifics->set_guid(guid);
+
+ std::set<std::string> current_profiles;
+
+ // This will be released inside the function
+ // TraverseAndAssociateChromeAutofillProfiles
+ AutoFillProfile *profile = new AutoFillProfile(guid);
+
+ // Set up the entry kernel with what we want.
+ EntryKernel kernel;
+ kernel.put(syncable::SPECIFICS, specifics);
+ kernel.put(syncable::META_HANDLE, 1);
+
+ MockWriteTransaction write_trans(&mock_directory);
+ EXPECT_CALL(mock_directory, GetEntryByClientTag(_))
+ .WillOnce(Return(&kernel));
+
+ sync_api::ReadNode read_node(&write_trans);
+
+ EXPECT_CALL(associator_, Associate(Pointee(guid), 1));
+
+ profiles_from_web_db.push_back(profile);
+
+ associator_.TraverseAndAssociateChromeAutoFillProfilesWrapper(&write_trans,
+ read_node,
+ profiles_from_web_db.get(),
+ &current_profiles,
+ NULL,
+ NULL,
+ NULL);
+
+ EXPECT_EQ((unsigned int)1, current_profiles.size());
+}
+
+TEST_F(AutofillProfileModelAssociatorTest, TestAssociatingMissingWebDBProfile) {
+ ScopedVector<AutoFillProfile> profiles_from_web_db;
+ MockDirectory mock_directory;
+
+ MockWriteTransaction write_trans(&mock_directory);
+ EXPECT_CALL(mock_directory,
+ GetEntryByClientTag(_))
+ .WillOnce(Return(reinterpret_cast<EntryKernel*>(NULL)));
+
+ sync_api::ReadNode autofill_root(&write_trans);
+
+ std::string guid = "abc";
+ std::set<std::string> current_profiles;
+ AutoFillProfile *profile = new AutoFillProfile(guid);
+
+ EXPECT_CALL(associator_,
+ MakeNewAutofillProfileSyncNodeIfNeeded(&write_trans,
+ Ref(autofill_root),
+ Ref(*profile),
+ _,
+ &current_profiles,
+ NULL))
+ .WillOnce(Return(true));
+
+ profiles_from_web_db.push_back(profile);
+
+ associator_.TraverseAndAssociateChromeAutoFillProfilesWrapper(&write_trans,
+ autofill_root,
+ profiles_from_web_db.get(),
+ &current_profiles,
+ NULL,
+ NULL,
+ NULL);
+}
+
+TEST_F(AutofillProfileModelAssociatorTest,
+ TestAssociateProfileInSyncDBWithWebDB) {
+ ReadNodeMock autofill_root;
+
+ // The constrcutor itself will initialize the id to root.
+ syncable::Id root_id;
+
+ sync_pb::EntitySpecifics specifics;
+ MockDirectory mock_directory;
+ sync_pb::AutofillProfileSpecifics *profile_specifics =
+ specifics.MutableExtension(sync_pb::autofill_profile);
+
+ profile_specifics->set_guid("abc");
+
+ // Set up the entry kernel with what we want.
+ EntryKernel kernel;
+ kernel.put(syncable::SPECIFICS, specifics);
+ kernel.put(syncable::META_HANDLE, 1);
+ kernel.put(syncable::ID, root_id);
+
+ MockWriteTransaction write_trans(&mock_directory);
+
+ browser_sync::AutofillProfileModelAssociator::DataBundle bundle;
+
+ EXPECT_CALL(autofill_root, GetFirstChildId())
+ .WillOnce(Return(1));
+
+ EXPECT_CALL(mock_directory,
+ GetEntryByHandle(_))
+ .WillOnce(Return(&kernel));
+
+ EXPECT_CALL(associator_,
+ AddNativeProfileIfNeeded(_,
+ &bundle,
+ _));
+
+ associator_.TraverseAndAssociateAllSyncNodesWrapper(&write_trans,
+ autofill_root,
+ &bundle);
+}
+
+TEST_F(AutofillProfileModelAssociatorTest, TestDontNeedToAddNativeProfile) {
+ ::testing::StrictMock<MockAutofillProfileModelAssociator> associator;
+ sync_pb::AutofillProfileSpecifics profile_specifics;
+ ReadNodeMock read_node;
+ std::string guid = "abc";
+ std::set<std::string> current_profiles;
+ browser_sync::AutofillProfileModelAssociator::DataBundle bundle;
+
+ profile_specifics.set_guid(guid);
+
+ bundle.current_profiles.insert(guid);
+
+ // We have no expectations to set. We have used a strict mock.
+ // If the profile is already present no other function
+ // should be called.
+ associator.AddNativeProfileIfNeededWrapper(profile_specifics, &bundle,
+ read_node);
+}
+
+TEST_F(AutofillProfileModelAssociatorTest, TestNeedToAddNativeProfile) {
+ sync_pb::AutofillProfileSpecifics profile_specifics;
+ ReadNodeMock read_node;
+ std::string guid = "abc";
+ std::set<std::string> current_profiles;
+ browser_sync::AutofillProfileModelAssociator::DataBundle bundle;
+ std::string first_name = "lingesh";
+
+ profile_specifics.set_guid(guid);
+ profile_specifics.set_name_first(first_name);
+
+ EXPECT_CALL(read_node, GetId())
+ .WillOnce(Return(1));
+
+ EXPECT_CALL(associator_,
+ Associate(Pointee(guid), 1));
+
+ associator_.AddNativeProfileIfNeededWrapper(
+ profile_specifics,
+ &bundle,
+ read_node);
+
+ EXPECT_EQ(bundle.new_profiles.size(), (unsigned int)1);
+ EXPECT_EQ(
+ bundle.new_profiles.front()->GetFieldText(AutoFillType(NAME_FIRST)),
+ UTF8ToUTF16(first_name));
+}
+
diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc
index ed8f429..3e3945e5 100644
--- a/chrome/browser/sync/profile_sync_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_factory_impl.cc
@@ -7,8 +7,10 @@
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/glue/app_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_change_processor.h"
+#include "chrome/browser/sync/glue/autofill_change_processor2.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/autofill_model_associator2.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"
@@ -40,8 +42,10 @@
using browser_sync::AppDataTypeController;
using browser_sync::AutofillChangeProcessor;
+using browser_sync::AutofillChangeProcessor2;
using browser_sync::AutofillDataTypeController;
using browser_sync::AutofillModelAssociator;
+using browser_sync::AutofillModelAssociator2;
using browser_sync::BookmarkChangeProcessor;
using browser_sync::BookmarkDataTypeController;
using browser_sync::BookmarkModelAssociator;
@@ -172,16 +176,30 @@ ProfileSyncFactoryImpl::CreateAutofillSyncComponents(
WebDatabase* web_database,
PersonalDataManager* personal_data,
browser_sync::UnrecoverableErrorHandler* error_handler) {
- AutofillModelAssociator* model_associator =
- new AutofillModelAssociator(profile_sync_service,
- web_database,
- personal_data);
- AutofillChangeProcessor* change_processor =
- new AutofillChangeProcessor(model_associator,
- web_database,
- personal_data,
- error_handler);
- return SyncComponents(model_associator, change_processor);
+
+ if (command_line_->HasSwitch(switches::kEnableSyncNewAutofill)) {
+ AutofillModelAssociator* model_associator =
+ new AutofillModelAssociator(profile_sync_service,
+ web_database,
+ personal_data);
+ AutofillChangeProcessor* change_processor =
+ new AutofillChangeProcessor(model_associator,
+ web_database,
+ personal_data,
+ error_handler);
+ return SyncComponents(model_associator, change_processor);
+ } else {
+ AutofillModelAssociator2* model_associator =
+ new AutofillModelAssociator2(profile_sync_service,
+ web_database,
+ personal_data);
+ AutofillChangeProcessor2* change_processor =
+ new AutofillChangeProcessor2(model_associator,
+ web_database,
+ personal_data,
+ error_handler);
+ return SyncComponents(model_associator, change_processor);
+ }
}
ProfileSyncFactory::SyncComponents
diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
index 17354b0..a7b929a 100644
--- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
@@ -24,8 +24,10 @@
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/engine/syncer_util.h"
#include "chrome/browser/sync/glue/autofill_change_processor.h"
+#include "chrome/browser/sync/glue/autofill_change_processor2.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/autofill_model_associator2.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
@@ -45,9 +47,9 @@
using base::Time;
using base::WaitableEvent;
-using browser_sync::AutofillChangeProcessor;
+using browser_sync::AutofillChangeProcessor2;
using browser_sync::AutofillDataTypeController;
-using browser_sync::AutofillModelAssociator;
+using browser_sync::AutofillModelAssociator2;
using browser_sync::GROUP_DB;
using browser_sync::kAutofillTag;
using browser_sync::SyncBackendHostForProfileSyncTest;
@@ -128,10 +130,10 @@ ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!BrowserThread::CurrentlyOn(BrowserThread::DB))
return ProfileSyncFactory::SyncComponents(NULL, NULL);
- AutofillModelAssociator* model_associator =
- new AutofillModelAssociator(service, wd, pdm);
- AutofillChangeProcessor* change_processor =
- new AutofillChangeProcessor(model_associator, wd, pdm, dtc);
+ AutofillModelAssociator2* model_associator =
+ new AutofillModelAssociator2(service, wd, pdm);
+ AutofillChangeProcessor2* change_processor =
+ new AutofillChangeProcessor2(model_associator, wd, pdm, dtc);
return ProfileSyncFactory::SyncComponents(model_associator,
change_processor);
}
@@ -212,12 +214,12 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
return false;
sync_api::WriteNode node(&trans);
- std::string tag = AutofillModelAssociator::KeyToTag(entry.key().name(),
+ std::string tag = AutofillModelAssociator2::KeyToTag(entry.key().name(),
entry.key().value());
if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag))
return false;
- AutofillChangeProcessor::WriteAutofillEntry(entry, &node);
+ AutofillChangeProcessor2::WriteAutofillEntry(entry, &node);
return true;
}
@@ -228,11 +230,11 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag))
return false;
sync_api::WriteNode node(&trans);
- std::string tag = AutofillModelAssociator::ProfileLabelToTag(
+ std::string tag = browser_sync::AutofillModelAssociator2::ProfileLabelToTag(
profile.Label());
if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag))
return false;
- AutofillChangeProcessor::WriteAutofillProfile(profile, &node);
+ AutofillChangeProcessor2::WriteAutofillProfile(profile, &node);
sync_pb::AutofillSpecifics s(node.GetAutofillSpecifics());
s.mutable_profile()->set_label(UTF16ToUTF8(profile.Label()));
node.SetAutofillSpecifics(s);
@@ -267,7 +269,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
} else if (autofill.has_profile()) {
AutoFillProfile p;
p.set_label(UTF8ToUTF16(autofill.profile().label()));
- AutofillModelAssociator::OverwriteProfileWithServerData(&p,
+ AutofillModelAssociator2::OverwriteProfileWithServerData(&p,
autofill.profile());
profiles->push_back(p);
}
@@ -390,7 +392,7 @@ class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> {
ASSERT_TRUE(dir.good());
// Create autofill protobuf
- std::string tag = AutofillModelAssociator::KeyToTag(entry_.key().name(),
+ std::string tag = AutofillModelAssociator2::KeyToTag(entry_.key().name(),
entry_.key().value());
sync_pb::AutofillSpecifics new_autofill;
new_autofill.set_name(UTF16ToUTF8(entry_.key().name()));
diff --git a/chrome/browser/sync/syncable/model_type.cc b/chrome/browser/sync/syncable/model_type.cc
index 7a7d3f1..c95daa8 100644
--- a/chrome/browser/sync/syncable/model_type.cc
+++ b/chrome/browser/sync/syncable/model_type.cc
@@ -37,6 +37,9 @@ void AddDefaultExtensionValue(syncable::ModelType datatype,
case AUTOFILL:
specifics->MutableExtension(sync_pb::autofill);
break;
+ case AUTOFILL_PROFILE:
+ specifics->MutableExtension(sync_pb::autofill_profile);
+ break;
case THEMES:
specifics->MutableExtension(sync_pb::theme);
break;
@@ -104,6 +107,9 @@ ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
if (specifics.HasExtension(sync_pb::autofill))
return AUTOFILL;
+ if (specifics.HasExtension(sync_pb::autofill_profile))
+ return AUTOFILL_PROFILE;
+
if (specifics.HasExtension(sync_pb::theme))
return THEMES;
@@ -261,6 +267,9 @@ const char kExtensionNotificationType[] = "EXTENSION";
const char kNigoriNotificationType[] = "NIGORI";
const char kAppNotificationType[] = "APP";
const char kSessionNotificationType[] = "SESSION";
+// TODO(lipalani) Bug 64111.
+// talk to akalin to make sure this is what I understand this to be.
+const char kAutofillProfileType[] = "AUTOFILL_PROFILE";
// TODO(akalin): This is a hack to make new sync data types work with
// server-issued notifications. Remove this when it's not needed
// anymore.
@@ -300,6 +309,9 @@ bool RealModelTypeToNotificationType(ModelType model_type,
case SESSIONS:
*notification_type = kSessionNotificationType;
return true;
+ case AUTOFILL_PROFILE:
+ *notification_type = kAutofillProfileType;
+ return true;
// TODO(akalin): This is a hack to make new sync data types work with
// server-issued notifications. Remove this when it's not needed
// anymore.
diff --git a/chrome/browser/sync/syncable/model_type.h b/chrome/browser/sync/syncable/model_type.h
index 2831bd2..442e462 100644
--- a/chrome/browser/sync/syncable/model_type.h
+++ b/chrome/browser/sync/syncable/model_type.h
@@ -51,6 +51,8 @@ enum ModelType {
PASSWORDS,
// An autofill folder or an autofill object.
AUTOFILL,
+ // An autofill Profile Object
+ AUTOFILL_PROFILE,
// A themes folder or a themes object.
THEMES,
// A typed_url folder or a typed_url object.
diff --git a/chrome/browser/sync/syncable/syncable.cc b/chrome/browser/sync/syncable/syncable.cc
index 99cc2ba..903b71d 100644
--- a/chrome/browser/sync/syncable/syncable.cc
+++ b/chrome/browser/sync/syncable/syncable.cc
@@ -352,12 +352,12 @@ EntryKernel* Directory::GetEntryByServerTag(const string& tag) {
return NULL;
}
-EntryKernel* Directory::GetEntryByHandle(const int64 metahandle) {
+EntryKernel* Directory::GetEntryByHandle(int64 metahandle) {
ScopedKernelLock lock(this);
return GetEntryByHandle(metahandle, &lock);
}
-EntryKernel* Directory::GetEntryByHandle(const int64 metahandle,
+EntryKernel* Directory::GetEntryByHandle(int64 metahandle,
ScopedKernelLock* lock) {
// Look up in memory
kernel_->needle.put(META_HANDLE, metahandle);
@@ -998,12 +998,21 @@ void BaseTransaction::Lock() {
}
BaseTransaction::BaseTransaction(Directory* directory, const char* name,
- const char* source_file, int line, WriterTag writer)
+ const char* source_file, int line, WriterTag writer)
: directory_(directory), dirkernel_(directory->kernel_), name_(name),
source_file_(source_file), line_(line), writer_(writer) {
Lock();
}
+BaseTransaction::BaseTransaction(Directory* directory)
+ : directory_(directory),
+ dirkernel_(NULL),
+ name_(NULL),
+ source_file_(NULL),
+ line_(NULL),
+ writer_(INVALID) {
+}
+
BaseTransaction::~BaseTransaction() {}
void BaseTransaction::UnlockAndLog(OriginalEntries* originals_arg) {
@@ -1098,6 +1107,11 @@ WriteTransaction::WriteTransaction(const ScopedDirLookup& scoped_dir,
originals_(new OriginalEntries) {
}
+WriteTransaction::WriteTransaction(Directory *directory)
+ : BaseTransaction(directory),
+ originals_(new OriginalEntries) {
+}
+
void WriteTransaction::SaveOriginal(EntryKernel* entry) {
if (NULL == entry)
return;
diff --git a/chrome/browser/sync/syncable/syncable.h b/chrome/browser/sync/syncable/syncable.h
index c26232f7..19d98ee 100644
--- a/chrome/browser/sync/syncable/syncable.h
+++ b/chrome/browser/sync/syncable/syncable.h
@@ -726,7 +726,7 @@ class Directory {
// an account. We keep this for each datatype. It doesn't actually map
// to any time scale; its name is an historical artifact.
int64 last_download_timestamp(ModelType type) const;
- void set_last_download_timestamp(ModelType type, int64 value);
+ virtual void set_last_download_timestamp(ModelType type, int64 value);
// Find the model type or model types which have the least timestamp, and
// return them along with the types having that timestamp. This is done
@@ -773,11 +773,12 @@ class Directory {
browser_sync::ChannelEventHandler<DirectoryChangeEvent>* observer);
protected: // for friends, mainly used by Entry constructors
- EntryKernel* GetEntryByHandle(const int64 handle);
- EntryKernel* GetEntryByHandle(const int64 metahandle, ScopedKernelLock* lock);
- EntryKernel* GetEntryById(const Id& id);
+ virtual EntryKernel* GetEntryByHandle(int64 handle);
+ virtual EntryKernel* GetEntryByHandle(int64 metahandle,
+ ScopedKernelLock* lock);
+ virtual EntryKernel* GetEntryById(const Id& id);
EntryKernel* GetEntryByServerTag(const std::string& tag);
- EntryKernel* GetEntryByClientTag(const std::string& tag);
+ virtual EntryKernel* GetEntryByClientTag(const std::string& tag);
EntryKernel* GetRootEntry();
bool ReindexId(EntryKernel* const entry, const Id& new_id);
void ReindexParentId(EntryKernel* const entry, const Id& new_parent_id);
@@ -823,7 +824,7 @@ class Directory {
// Find the first or last child in the positional ordering under a parent,
// and return its id. Returns a root Id if parent has no children.
- Id GetFirstChildId(BaseTransaction* trans, const Id& parent_id);
+ virtual Id GetFirstChildId(BaseTransaction* trans, const Id& parent_id);
Id GetLastChildId(BaseTransaction* trans, const Id& parent_id);
// SaveChanges works by taking a consistent snapshot of the current Directory
@@ -1058,6 +1059,9 @@ class BaseTransaction {
BaseTransaction(Directory* directory, const char* name,
const char* source_file, int line, WriterTag writer);
+ // For unit testing. Everything will be mocked out no point initializing.
+ explicit BaseTransaction(Directory* directory);
+
void UnlockAndLog(OriginalEntries* entries);
bool NotifyTransactionChangingAndEnding(OriginalEntries* entries);
virtual void NotifyTransactionComplete();
@@ -1112,6 +1116,8 @@ class WriteTransaction : public BaseTransaction {
// is done.
OriginalEntries* const originals_;
+ explicit WriteTransaction(Directory *directory);
+
DISALLOW_COPY_AND_ASSIGN(WriteTransaction);
};
diff --git a/chrome/browser/sync/syncable/syncable_mock.h b/chrome/browser/sync/syncable/syncable_mock.h
new file mode 100644
index 0000000..9412fe0
--- /dev/null
+++ b/chrome/browser/sync/syncable/syncable_mock.h
@@ -0,0 +1,40 @@
+// 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_SYNCABLE_SYNCABLE_MOCK_H_
+#define CHROME_BROWSER_SYNC_SYNCABLE_SYNCABLE_MOCK_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/sync/syncable/syncable.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using syncable::Directory;
+using syncable::EntryKernel;
+
+class MockDirectory : public Directory {
+ public:
+ MockDirectory() {
+ init_kernel("myk");
+ }
+ MOCK_METHOD1(GetEntryByHandle, syncable::EntryKernel*(int64));
+
+ MOCK_METHOD2(set_last_downloadstamp, void(syncable::ModelType, int64));
+
+ MOCK_METHOD1(GetEntryByClientTag,
+ syncable::EntryKernel*(const std::string&));
+};
+
+class MockSyncableWriteTransaction : public syncable::WriteTransaction {
+ public:
+ explicit MockSyncableWriteTransaction(Directory *directory) :
+ WriteTransaction(directory, syncable::UNITTEST, "dontcare.cpp", 25) {
+ }
+};
+
+
+#endif // CHROME_BROWSER_SYNC_SYNCABLE_SYNCABLE_MOCK_H_
+