summaryrefslogtreecommitdiffstats
path: root/components/autofill/browser
diff options
context:
space:
mode:
Diffstat (limited to 'components/autofill/browser')
-rw-r--r--components/autofill/browser/DEPS3
-rw-r--r--components/autofill/browser/autocomplete_history_manager.h2
-rw-r--r--components/autofill/browser/autocomplete_history_manager_unittest.cc2
-rw-r--r--components/autofill/browser/personal_data_manager.cc4
-rw-r--r--components/autofill/browser/personal_data_manager.h2
-rw-r--r--components/autofill/browser/personal_data_manager_unittest.cc2
-rw-r--r--components/autofill/browser/webdata/autofill_change.cc34
-rw-r--r--components/autofill/browser/webdata/autofill_change.h72
-rw-r--r--components/autofill/browser/webdata/autofill_entry.cc125
-rw-r--r--components/autofill/browser/webdata/autofill_entry.h71
-rw-r--r--components/autofill/browser/webdata/autofill_entry_unittest.cc78
-rw-r--r--components/autofill/browser/webdata/autofill_table.cc2050
-rw-r--r--components/autofill/browser/webdata/autofill_table.h366
-rw-r--r--components/autofill/browser/webdata/autofill_table_unittest.cc1328
-rw-r--r--components/autofill/browser/webdata/autofill_webdata.h88
-rw-r--r--components/autofill/browser/webdata/autofill_webdata_service.cc458
-rw-r--r--components/autofill/browser/webdata/autofill_webdata_service.h132
-rw-r--r--components/autofill/browser/webdata/autofill_webdata_service_observer.h37
-rw-r--r--components/autofill/browser/webdata/web_data_service_unittest.cc532
19 files changed, 5379 insertions, 7 deletions
diff --git a/components/autofill/browser/DEPS b/components/autofill/browser/DEPS
index 90dd89e..d513e2b 100644
--- a/components/autofill/browser/DEPS
+++ b/components/autofill/browser/DEPS
@@ -1,10 +1,11 @@
include_rules = [
- "+components/webdata/autofill",
+ "+components/webdata/common",
"+content/public/browser",
"+crypto/random.h",
"+google_apis/gaia",
"+google_apis/google_api_keys.h",
"+net",
+ "+sql",
"+third_party/libjingle",
"+third_party/libphonenumber", # For phone number i18n.
"+webkit/plugins/webplugininfo.h",
diff --git a/components/autofill/browser/autocomplete_history_manager.h b/components/autofill/browser/autocomplete_history_manager.h
index 60a9f08..7cf00db 100644
--- a/components/autofill/browser/autocomplete_history_manager.h
+++ b/components/autofill/browser/autocomplete_history_manager.h
@@ -9,7 +9,7 @@
#include "base/gtest_prod_util.h"
#include "base/prefs/pref_member.h"
-#include "components/webdata/autofill/autofill_webdata_service.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service.h"
#include "components/webdata/common/web_data_service_consumer.h"
#include "content/public/browser/web_contents_observer.h"
diff --git a/components/autofill/browser/autocomplete_history_manager_unittest.cc b/components/autofill/browser/autocomplete_history_manager_unittest.cc
index 3b4bc38..08bc9e1 100644
--- a/components/autofill/browser/autocomplete_history_manager_unittest.cc
+++ b/components/autofill/browser/autocomplete_history_manager_unittest.cc
@@ -16,8 +16,8 @@
#include "components/autofill/browser/autofill_external_delegate.h"
#include "components/autofill/browser/autofill_manager.h"
#include "components/autofill/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/common/form_data.h"
-#include "components/webdata/autofill/autofill_webdata_service.h"
#include "components/webdata/common/web_data_service_test_util.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/autofill/browser/personal_data_manager.cc b/components/autofill/browser/personal_data_manager.cc
index 6fa3c45..9ce89c6 100644
--- a/components/autofill/browser/personal_data_manager.cc
+++ b/components/autofill/browser/personal_data_manager.cc
@@ -13,8 +13,8 @@
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/utf_string_conversions.h"
-#include "components/autofill/browser/autofill_country.h"
#include "components/autofill/browser/autofill-inl.h"
+#include "components/autofill/browser/autofill_country.h"
#include "components/autofill/browser/autofill_field.h"
#include "components/autofill/browser/autofill_metrics.h"
#include "components/autofill/browser/form_group.h"
@@ -23,9 +23,9 @@
#include "components/autofill/browser/phone_number.h"
#include "components/autofill/browser/phone_number_i18n.h"
#include "components/autofill/browser/validation.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/common/autofill_pref_names.h"
#include "components/user_prefs/user_prefs.h"
-#include "components/webdata/autofill/autofill_webdata_service.h"
#include "content/public/browser/browser_context.h"
using content::BrowserContext;
diff --git a/components/autofill/browser/personal_data_manager.h b/components/autofill/browser/personal_data_manager.h
index 311a52a..75d1e6b 100644
--- a/components/autofill/browser/personal_data_manager.h
+++ b/components/autofill/browser/personal_data_manager.h
@@ -16,7 +16,7 @@
#include "components/autofill/browser/autofill_profile.h"
#include "components/autofill/browser/credit_card.h"
#include "components/autofill/browser/field_types.h"
-#include "components/webdata/autofill/autofill_webdata_service_observer.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service_observer.h"
#include "components/webdata/common/web_data_service_consumer.h"
class AutofillMetrics;
diff --git a/components/autofill/browser/personal_data_manager_unittest.cc b/components/autofill/browser/personal_data_manager_unittest.cc
index d7e1061..dd62b87 100644
--- a/components/autofill/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/browser/personal_data_manager_unittest.cc
@@ -18,8 +18,8 @@
#include "components/autofill/browser/form_structure.h"
#include "components/autofill/browser/personal_data_manager.h"
#include "components/autofill/browser/personal_data_manager_observer.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/common/form_data.h"
-#include "components/webdata/autofill/autofill_webdata_service.h"
#include "components/webdata/encryptor/encryptor.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_registrar.h"
diff --git a/components/autofill/browser/webdata/autofill_change.cc b/components/autofill/browser/webdata/autofill_change.cc
new file mode 100644
index 0000000..0b58602
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_change.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 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 "components/autofill/browser/webdata/autofill_change.h"
+
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+
+AutofillChange::AutofillChange(Type type, const AutofillKey& key)
+ : GenericAutofillChange<AutofillKey>(type, key) {
+}
+
+AutofillChange::~AutofillChange() {
+}
+
+AutofillProfileChange::AutofillProfileChange(
+ Type type, const std::string& key, const AutofillProfile* profile)
+ : GenericAutofillChange<std::string>(type, key),
+ profile_(profile) {
+ DCHECK(type == ADD ? (profile && profile->guid() == key) : true);
+ DCHECK(type == UPDATE ? (profile && profile->guid() == key) : true);
+ DCHECK(type == REMOVE ? !profile : true);
+}
+
+AutofillProfileChange::~AutofillProfileChange() {
+}
+
+bool AutofillProfileChange::operator==(
+ const AutofillProfileChange& change) const {
+ return type() == change.type() &&
+ key() == change.key() &&
+ (type() != REMOVE) ? *profile() == *change.profile() : true;
+}
diff --git a/components/autofill/browser/webdata/autofill_change.h b/components/autofill/browser/webdata/autofill_change.h
new file mode 100644
index 0000000..386f540
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_change.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 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 COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
+#define COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
+
+#include <vector>
+
+#include "components/autofill/browser/webdata/autofill_entry.h"
+
+class AutofillProfile;
+class CreditCard;
+
+// For classic Autofill form fields, the KeyType is AutofillKey.
+// Autofill++ types such as AutofillProfile and CreditCard simply use an int.
+template <typename KeyType>
+class GenericAutofillChange {
+ public:
+ typedef enum {
+ ADD,
+ UPDATE,
+ REMOVE
+ } Type;
+
+ virtual ~GenericAutofillChange() {}
+
+ Type type() const { return type_; }
+ const KeyType& key() const { return key_; }
+
+ protected:
+ GenericAutofillChange(Type type, const KeyType& key)
+ : type_(type),
+ key_(key) {}
+ private:
+ Type type_;
+ KeyType key_;
+};
+
+class AutofillChange : public GenericAutofillChange<AutofillKey> {
+ public:
+ AutofillChange(Type type, const AutofillKey& key);
+ virtual ~AutofillChange();
+ bool operator==(const AutofillChange& change) const {
+ return type() == change.type() && key() == change.key();
+ }
+};
+
+typedef std::vector<AutofillChange> AutofillChangeList;
+
+// Change notification details for Autofill profile changes.
+class AutofillProfileChange : public GenericAutofillChange<std::string> {
+ public:
+ // The |type| input specifies the change type. The |key| input is the key,
+ // which is expected to be the GUID identifying the |profile|.
+ // When |type| == ADD, |profile| should be non-NULL.
+ // When |type| == UPDATE, |profile| should be non-NULL.
+ // When |type| == REMOVE, |profile| should be NULL.
+ AutofillProfileChange(Type type,
+ const std::string& key,
+ const AutofillProfile* profile);
+ virtual ~AutofillProfileChange();
+
+ const AutofillProfile* profile() const { return profile_; }
+ bool operator==(const AutofillProfileChange& change) const;
+
+ private:
+ // Weak reference, can be NULL.
+ const AutofillProfile* profile_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
diff --git a/components/autofill/browser/webdata/autofill_entry.cc b/components/autofill/browser/webdata/autofill_entry.cc
new file mode 100644
index 0000000..713f6bc
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_entry.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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 "components/autofill/browser/webdata/autofill_entry.h"
+
+#include <algorithm>
+#include <set>
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+
+namespace {
+
+// The period after which Autofill entries should expire in days.
+const int64 kExpirationPeriodInDays = 60;
+
+} // namespace
+
+AutofillKey::AutofillKey() {}
+
+AutofillKey::AutofillKey(const string16& name, const string16& value)
+ : name_(name),
+ value_(value) {
+}
+
+AutofillKey::AutofillKey(const char* name, const char* value)
+ : name_(UTF8ToUTF16(name)),
+ value_(UTF8ToUTF16(value)) {
+}
+
+AutofillKey::AutofillKey(const AutofillKey& key)
+ : name_(key.name()),
+ value_(key.value()) {
+}
+
+AutofillKey::~AutofillKey() {}
+
+bool AutofillKey::operator==(const AutofillKey& key) const {
+ return name_ == key.name() && value_ == key.value();
+}
+
+bool AutofillKey::operator<(const AutofillKey& key) const {
+ int diff = name_.compare(key.name());
+ if (diff < 0) {
+ return true;
+ } else if (diff == 0) {
+ return value_.compare(key.value()) < 0;
+ } else {
+ return false;
+ }
+}
+
+AutofillEntry::AutofillEntry(const AutofillKey& key,
+ const std::vector<base::Time>& timestamps)
+ : key_(key) {
+ timestamps_culled_ = CullTimeStamps(timestamps, &timestamps_);
+}
+
+AutofillEntry::~AutofillEntry() {}
+
+bool AutofillEntry::operator==(const AutofillEntry& entry) const {
+ if (!(key_ == entry.key()))
+ return false;
+
+ if (timestamps_.size() != entry.timestamps().size())
+ return false;
+
+ std::set<base::Time> other_timestamps(entry.timestamps().begin(),
+ entry.timestamps().end());
+ for (size_t i = 0; i < timestamps_.size(); i++) {
+ if (other_timestamps.count(timestamps_[i]) == 0)
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillEntry::operator<(const AutofillEntry& entry) const {
+ return key_ < entry.key();
+}
+
+bool AutofillEntry::IsExpired() const {
+ base::Time time = ExpirationTime();
+ // TODO(georgey): add DCHECK(!timestamps_.empty()) after conversion of the db
+ // is complete.
+ return (timestamps_.empty() || timestamps_.back() < time);
+}
+
+// static
+base::Time AutofillEntry::ExpirationTime() {
+ return base::Time::Now() - base::TimeDelta::FromDays(kExpirationPeriodInDays);
+}
+
+// Culls the list of timestamps to the first and last used.
+// If sync is enabled, at every browser restart, sync will do a match up of all
+// autofill items on the server with all items on the web db. When webdb loads
+// all the items in memory(for sync to process. The method is
+// |GetAllAutofillEntries|) they will pass through this method for culling. If
+// sync finds any of these items were culled it will updates the server AND the
+// web db with these new timestamps. However after restart if an autofill item
+// exceeds the |kMaxAutofillTimeStamps| it will NOT be written to web db until
+// restart. But sync when uploading to the server will only upload this culled
+// list. Meaning until restart there will be mis-match in timestamps but
+// it should correct itself at startup.
+bool AutofillEntry::CullTimeStamps(const std::vector<base::Time>& source,
+ std::vector<base::Time>* result) {
+ DCHECK(result);
+ DCHECK(&source != result);
+
+ // First copy the source to result.
+ result->clear();
+
+ if (source.size() <= 2) {
+ result->insert(result->begin(), source.begin(), source.end());
+ return false;
+ }
+
+ result->push_back(source.front());
+ result->push_back(source.back());
+
+ DVLOG(1) << "Culling timestamps. Current count is : " << source.size();
+
+ return true;
+}
diff --git a/components/autofill/browser/webdata/autofill_entry.h b/components/autofill/browser/webdata/autofill_entry.h
new file mode 100644
index 0000000..14509eca
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_entry.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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 COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__
+#define COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__
+
+#include <stddef.h>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "base/time.h"
+
+class AutofillKey {
+ public:
+ AutofillKey();
+ AutofillKey(const string16& name, const string16& value);
+ AutofillKey(const char* name, const char* value);
+ AutofillKey(const AutofillKey& key);
+ virtual ~AutofillKey();
+
+ const string16& name() const { return name_; }
+ const string16& value() const { return value_; }
+
+ bool operator==(const AutofillKey& key) const;
+ bool operator<(const AutofillKey& key) const;
+
+ private:
+ string16 name_;
+ string16 value_;
+};
+
+class AutofillEntry {
+ public:
+ AutofillEntry(const AutofillKey& key,
+ const std::vector<base::Time>& timestamps);
+ ~AutofillEntry();
+
+ const AutofillKey& key() const { return key_; }
+ const std::vector<base::Time>& timestamps() const { return timestamps_; }
+
+ bool operator==(const AutofillEntry& entry) const;
+ bool operator<(const AutofillEntry& entry) const;
+
+ bool timestamps_culled() const { return timestamps_culled_; }
+
+ // Checks if last of the timestamps are older than ExpirationTime().
+ bool IsExpired() const;
+
+ // The entries last accessed before this time should expire.
+ static base::Time ExpirationTime();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AutofillEntryTest, NoCulling);
+ FRIEND_TEST_ALL_PREFIXES(AutofillEntryTest, Culling);
+ FRIEND_TEST_ALL_PREFIXES(AutofillEntryTest, CullByTime);
+
+ // Culls the list of timestamps to 2 - the oldest and most recent. This is a
+ // precursor to getting rid of the timestamps db altogether.
+ // See http://crbug.com/118696.
+ // |source| is expected to be sorted from oldest to newest.
+ static bool CullTimeStamps(const std::vector<base::Time>& source,
+ std::vector<base::Time>* result);
+
+ AutofillKey key_;
+ std::vector<base::Time> timestamps_;
+ bool timestamps_culled_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__
diff --git a/components/autofill/browser/webdata/autofill_entry_unittest.cc b/components/autofill/browser/webdata/autofill_entry_unittest.cc
new file mode 100644
index 0000000..c5940bb
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_entry_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 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 <algorithm>
+
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/webdata/autofill_entry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const unsigned int kMaxAutofillTimeStamps = 2;
+
+TEST(AutofillEntryTest, NoCulling) {
+ std::vector<base::Time> source, result;
+ base::Time current = base::Time::Now();
+ for (size_t i = 0; i < kMaxAutofillTimeStamps; ++i)
+ source.push_back(current);
+
+ EXPECT_FALSE(AutofillEntry::CullTimeStamps(source, &result));
+ EXPECT_EQ(result.size(), kMaxAutofillTimeStamps);
+ for (std::vector<base::Time>::const_iterator it = result.begin();
+ it != result.end(); ++it) {
+ EXPECT_EQ(*it, current);
+ }
+}
+
+TEST(AutofillEntryTest, Culling) {
+ std::vector<base::Time> source, result;
+ base::Time current = base::Time::Now();
+ const int offset = 10000;
+
+ int64 internal_value = current.ToInternalValue();
+ for (size_t i = 0; i < kMaxAutofillTimeStamps * 2 ; ++i) {
+ source.push_back(base::Time::FromInternalValue(
+ internal_value + i * offset));
+ }
+ std::sort(source.begin(), source.end());
+ EXPECT_TRUE(AutofillEntry::CullTimeStamps(source, &result));
+
+ EXPECT_EQ(result.size(), kMaxAutofillTimeStamps);
+ EXPECT_EQ(result.front(), base::Time::FromInternalValue(internal_value));
+ int last_offset = (kMaxAutofillTimeStamps * 2 - 1) * offset;
+ EXPECT_EQ(result.back(),
+ base::Time::FromInternalValue(last_offset + internal_value));
+}
+
+TEST(AutofillEntryTest, CullByTime) {
+ base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
+
+ std::vector<base::Time> timestamps;
+ base::Time cutoff_time = AutofillEntry::ExpirationTime();
+
+ // Within the time limit.
+ timestamps.push_back(cutoff_time + one_hour);
+
+ AutofillKey key(UTF8ToUTF16("test_key"), UTF8ToUTF16("test_value"));
+
+ AutofillEntry entry_within_the_limits(key, timestamps);
+ EXPECT_FALSE(entry_within_the_limits.IsExpired());
+
+ // One within the time limit, one outside.
+ timestamps.push_back(cutoff_time - one_hour);
+
+ AutofillEntry entry_partially_within_the_limits(key, timestamps);
+ EXPECT_TRUE(
+ entry_partially_within_the_limits.IsExpired());
+
+ // All outside the time limit.
+ timestamps.clear();
+ timestamps.push_back(cutoff_time - one_hour);
+ timestamps.push_back(cutoff_time - one_hour * 2);
+ timestamps.push_back(cutoff_time - one_hour * 3);
+
+ AutofillEntry entry_outside_the_limits(key, timestamps);
+ EXPECT_TRUE(entry_outside_the_limits.IsExpired());
+ EXPECT_TRUE(entry_outside_the_limits.timestamps_culled());
+}
diff --git a/components/autofill/browser/webdata/autofill_table.cc b/components/autofill/browser/webdata/autofill_table.cc
new file mode 100644
index 0000000..9b76aae
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_table.cc
@@ -0,0 +1,2050 @@
+// Copyright (c) 2012 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 "components/autofill/browser/webdata/autofill_table.h"
+
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time.h"
+#include "base/tuple.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/browser/webdata/autofill_change.h"
+#include "components/autofill/browser/webdata/autofill_entry.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/encryptor/encryptor.h"
+#include "sql/statement.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using base::Time;
+
+namespace {
+
+typedef std::vector<Tuple3<int64, string16, string16> > AutofillElementList;
+
+// TODO(dhollowa): Find a common place for this. It is duplicated in
+// personal_data_manager.cc.
+template<typename T>
+T* address_of(T& v) {
+ return &v;
+}
+
+string16 LimitDataSize(const string16& data) {
+ if (data.size() > AutofillTable::kMaxDataLength)
+ return data.substr(0, AutofillTable::kMaxDataLength);
+
+ return data;
+}
+
+void BindAutofillProfileToStatement(const AutofillProfile& profile,
+ sql::Statement* s,
+ const std::string& app_locale) {
+ DCHECK(base::IsValidGUID(profile.guid()));
+ s->BindString(0, profile.guid());
+
+ string16 text = profile.GetRawInfo(COMPANY_NAME);
+ s->BindString16(1, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_LINE1);
+ s->BindString16(2, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_LINE2);
+ s->BindString16(3, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_CITY);
+ s->BindString16(4, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_STATE);
+ s->BindString16(5, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_ZIP);
+ s->BindString16(6, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_COUNTRY, app_locale);
+ s->BindString16(7, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
+ s->BindString16(8, LimitDataSize(text));
+ s->BindInt64(9, Time::Now().ToTimeT());
+}
+
+AutofillProfile* AutofillProfileFromStatement(const sql::Statement& s,
+ const std::string& app_locale) {
+ AutofillProfile* profile = new AutofillProfile;
+ profile->set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(profile->guid()));
+
+ profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(1));
+ profile->SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(2));
+ profile->SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(3));
+ profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(4));
+ profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(5));
+ profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(6));
+ // Intentionally skip column 7, which stores the localized country name.
+ profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(8));
+ // Intentionally skip column 9, which stores the profile's modification date.
+
+ return profile;
+}
+
+void BindCreditCardToStatement(const CreditCard& credit_card,
+ sql::Statement* s) {
+ DCHECK(base::IsValidGUID(credit_card.guid()));
+ s->BindString(0, credit_card.guid());
+
+ string16 text = credit_card.GetRawInfo(CREDIT_CARD_NAME);
+ s->BindString16(1, LimitDataSize(text));
+ text = credit_card.GetRawInfo(CREDIT_CARD_EXP_MONTH);
+ s->BindString16(2, LimitDataSize(text));
+ text = credit_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ s->BindString16(3, LimitDataSize(text));
+ text = credit_card.GetRawInfo(CREDIT_CARD_NUMBER);
+ std::string encrypted_data;
+ Encryptor::EncryptString16(text, &encrypted_data);
+ s->BindBlob(4, encrypted_data.data(),
+ static_cast<int>(encrypted_data.length()));
+ s->BindInt64(5, Time::Now().ToTimeT());
+}
+
+CreditCard* CreditCardFromStatement(const sql::Statement& s) {
+ CreditCard* credit_card = new CreditCard;
+
+ credit_card->set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(credit_card->guid()));
+
+ credit_card->SetRawInfo(CREDIT_CARD_NAME, s.ColumnString16(1));
+ credit_card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(2));
+ credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(3));
+ int encrypted_number_len = s.ColumnByteLength(4);
+ string16 credit_card_number;
+ if (encrypted_number_len) {
+ std::string encrypted_number;
+ encrypted_number.resize(encrypted_number_len);
+ memcpy(&encrypted_number[0], s.ColumnBlob(4), encrypted_number_len);
+ Encryptor::DecryptString16(encrypted_number, &credit_card_number);
+ }
+ credit_card->SetRawInfo(CREDIT_CARD_NUMBER, credit_card_number);
+ // Intentionally skip column 5, which stores the modification date.
+
+ return credit_card;
+}
+
+bool AddAutofillProfileNamesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name "
+ "FROM autofill_profile_names "
+ "WHERE guid=?"));
+ s.BindString(0, profile->guid());
+
+ if (!s.is_valid())
+ return false;
+
+ std::vector<string16> first_names;
+ std::vector<string16> middle_names;
+ std::vector<string16> last_names;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ first_names.push_back(s.ColumnString16(1));
+ middle_names.push_back(s.ColumnString16(2));
+ last_names.push_back(s.ColumnString16(3));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ profile->SetRawMultiInfo(NAME_FIRST, first_names);
+ profile->SetRawMultiInfo(NAME_MIDDLE, middle_names);
+ profile->SetRawMultiInfo(NAME_LAST, last_names);
+ return true;
+}
+
+bool AddAutofillProfileEmailsToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, email "
+ "FROM autofill_profile_emails "
+ "WHERE guid=?"));
+ s.BindString(0, profile->guid());
+
+ if (!s.is_valid())
+ return false;
+
+ std::vector<string16> emails;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ emails.push_back(s.ColumnString16(1));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ profile->SetRawMultiInfo(EMAIL_ADDRESS, emails);
+ return true;
+}
+
+bool AddAutofillProfilePhonesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, type, number "
+ "FROM autofill_profile_phones "
+ "WHERE guid=? AND type=?"));
+
+ // Value used to be either [(0, phone), (1, fax)] but fax has been removed.
+ s.BindString(0, profile->guid());
+ s.BindInt(1, 0);
+
+ if (!s.is_valid())
+ return false;
+
+ std::vector<string16> numbers;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ numbers.push_back(s.ColumnString16(2));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ profile->SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, numbers);
+ return true;
+}
+
+bool AddAutofillProfileNames(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<string16> first_names;
+ profile.GetRawMultiInfo(NAME_FIRST, &first_names);
+ std::vector<string16> middle_names;
+ profile.GetRawMultiInfo(NAME_MIDDLE, &middle_names);
+ std::vector<string16> last_names;
+ profile.GetRawMultiInfo(NAME_LAST, &last_names);
+ DCHECK_EQ(first_names.size(), middle_names.size());
+ DCHECK_EQ(middle_names.size(), last_names.size());
+
+ for (size_t i = 0; i < first_names.size(); ++i) {
+ // Add the new name.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_names"
+ " (guid, first_name, middle_name, last_name) "
+ "VALUES (?,?,?,?)"));
+ s.BindString(0, profile.guid());
+ s.BindString16(1, first_names[i]);
+ s.BindString16(2, middle_names[i]);
+ s.BindString16(3, last_names[i]);
+
+ if (!s.Run())
+ return false;
+ }
+ return true;
+}
+
+bool AddAutofillProfileEmails(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<string16> emails;
+ profile.GetRawMultiInfo(EMAIL_ADDRESS, &emails);
+
+ for (size_t i = 0; i < emails.size(); ++i) {
+ // Add the new email.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_emails"
+ " (guid, email) "
+ "VALUES (?,?)"));
+ s.BindString(0, profile.guid());
+ s.BindString16(1, emails[i]);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AddAutofillProfilePhones(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<string16> numbers;
+ profile.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &numbers);
+
+ for (size_t i = 0; i < numbers.size(); ++i) {
+ // Add the new number.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_phones"
+ " (guid, type, number) "
+ "VALUES (?,?,?)"));
+ s.BindString(0, profile.guid());
+ // Value used to be either [(0, phone), (1, fax)] but fax has been removed.
+ s.BindInt(1, 0);
+ s.BindString16(2, numbers[i]);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AddAutofillProfilePieces(const AutofillProfile& profile,
+ sql::Connection* db) {
+ if (!AddAutofillProfileNames(profile, db))
+ return false;
+
+ if (!AddAutofillProfileEmails(profile, db))
+ return false;
+
+ if (!AddAutofillProfilePhones(profile, db))
+ return false;
+
+ return true;
+}
+
+bool RemoveAutofillProfilePieces(const std::string& guid, sql::Connection* db) {
+ sql::Statement s1(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_names WHERE guid = ?"));
+ s1.BindString(0, guid);
+
+ if (!s1.Run())
+ return false;
+
+ sql::Statement s2(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_emails WHERE guid = ?"));
+ s2.BindString(0, guid);
+
+ if (!s2.Run())
+ return false;
+
+ sql::Statement s3(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_phones WHERE guid = ?"));
+ s3.BindString(0, guid);
+
+ return s3.Run();
+}
+
+WebDatabaseTable::TypeKey GetKey() {
+ // We just need a unique constant. Use the address of a static that
+ // COMDAT folding won't touch in an optimizing linker.
+ static int table_key = 0;
+ return reinterpret_cast<void*>(&table_key);
+}
+
+} // namespace
+
+// The maximum length allowed for form data.
+const size_t AutofillTable::kMaxDataLength = 1024;
+
+AutofillTable::AutofillTable(const std::string& app_locale)
+ : app_locale_(app_locale) {
+}
+
+AutofillTable::~AutofillTable() {
+}
+
+AutofillTable* AutofillTable::FromWebDatabase(WebDatabase* db) {
+ return static_cast<AutofillTable*>(db->GetTable(GetKey()));
+}
+
+WebDatabaseTable::TypeKey AutofillTable::GetTypeKey() const {
+ return GetKey();
+}
+
+bool AutofillTable::Init(sql::Connection* db, sql::MetaTable* meta_table) {
+ WebDatabaseTable::Init(db, meta_table);
+ return (InitMainTable() && InitCreditCardsTable() && InitDatesTable() &&
+ InitProfilesTable() && InitProfileNamesTable() &&
+ InitProfileEmailsTable() && InitProfilePhonesTable() &&
+ InitProfileTrashTable());
+}
+
+bool AutofillTable::IsSyncable() {
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion(int version,
+ bool* update_compatible_version) {
+ // Migrate if necessary.
+ switch (version) {
+ case 22:
+ return ClearAutofillEmptyValueElements();
+ case 23:
+ return MigrateToVersion23AddCardNumberEncryptedColumn();
+ case 24:
+ return MigrateToVersion24CleanupOversizedStringFields();
+ case 27:
+ *update_compatible_version = true;
+ return MigrateToVersion27UpdateLegacyCreditCards();
+ case 30:
+ *update_compatible_version = true;
+ return MigrateToVersion30AddDateModifed();
+ case 31:
+ *update_compatible_version = true;
+ return MigrateToVersion31AddGUIDToCreditCardsAndProfiles();
+ case 32:
+ *update_compatible_version = true;
+ return MigrateToVersion32UpdateProfilesAndCreditCards();
+ case 33:
+ *update_compatible_version = true;
+ return MigrateToVersion33ProfilesBasedOnFirstName();
+ case 34:
+ *update_compatible_version = true;
+ return MigrateToVersion34ProfilesBasedOnCountryCode();
+ case 35:
+ *update_compatible_version = true;
+ return MigrateToVersion35GreatBritainCountryCodes();
+ // Combine migrations 36 and 37. This is due to enhancements to the merge
+ // step when migrating profiles. The original migration from 35 to 36 did
+ // not merge profiles with identical addresses, but the migration from 36 to
+ // 37 does. The step from 35 to 36 should only happen on the Chrome 12 dev
+ // channel. Chrome 12 beta and release users will jump from 35 to 37
+ // directly getting the full benefits of the multi-valued merge as well as
+ // the culling of bad data.
+ case 37:
+ *update_compatible_version = true;
+ return MigrateToVersion37MergeAndCullOlderProfiles();
+ }
+ return true;
+}
+
+bool AutofillTable::AddFormFieldValues(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes) {
+ return AddFormFieldValuesTime(elements, changes, Time::Now());
+}
+
+bool AutofillTable::AddFormFieldValue(const FormFieldData& element,
+ std::vector<AutofillChange>* changes) {
+ return AddFormFieldValueTime(element, changes, Time::Now());
+}
+
+bool AutofillTable::GetFormValuesForElementName(const string16& name,
+ const string16& prefix,
+ std::vector<string16>* values,
+ int limit) {
+ DCHECK(values);
+ sql::Statement s;
+
+ if (prefix.empty()) {
+ s.Assign(db_->GetUniqueStatement(
+ "SELECT value FROM autofill "
+ "WHERE name = ? "
+ "ORDER BY count DESC "
+ "LIMIT ?"));
+ s.BindString16(0, name);
+ s.BindInt(1, limit);
+ } else {
+ string16 prefix_lower = base::i18n::ToLower(prefix);
+ string16 next_prefix = prefix_lower;
+ next_prefix[next_prefix.length() - 1]++;
+
+ s.Assign(db_->GetUniqueStatement(
+ "SELECT value FROM autofill "
+ "WHERE name = ? AND "
+ "value_lower >= ? AND "
+ "value_lower < ? "
+ "ORDER BY count DESC "
+ "LIMIT ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, prefix_lower);
+ s.BindString16(2, next_prefix);
+ s.BindInt(3, limit);
+ }
+
+ values->clear();
+ while (s.Step())
+ values->push_back(s.ColumnString16(0));
+ return s.Succeeded();
+}
+
+bool AutofillTable::RemoveFormElementsAddedBetween(
+ const Time& delete_begin,
+ const Time& delete_end,
+ std::vector<AutofillChange>* changes) {
+ DCHECK(changes);
+ // Query for the pair_id, name, and value of all form elements that
+ // were used between the given times.
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT DISTINCT a.pair_id, a.name, a.value "
+ "FROM autofill_dates ad JOIN autofill a ON ad.pair_id = a.pair_id "
+ "WHERE ad.date_created >= ? AND ad.date_created < ?"));
+ s.BindInt64(0, delete_begin.ToTimeT());
+ s.BindInt64(1,
+ (delete_end.is_null() || delete_end == base::Time::Max()) ?
+ std::numeric_limits<int64>::max() :
+ delete_end.ToTimeT());
+
+ AutofillElementList elements;
+ while (s.Step()) {
+ elements.push_back(MakeTuple(s.ColumnInt64(0),
+ s.ColumnString16(1),
+ s.ColumnString16(2)));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ for (AutofillElementList::iterator itr = elements.begin();
+ itr != elements.end(); ++itr) {
+ int how_many = 0;
+ if (!RemoveFormElementForTimeRange(itr->a, delete_begin, delete_end,
+ &how_many)) {
+ return false;
+ }
+ // We store at most 2 time stamps. If we remove both of them we should
+ // delete the corresponding data. If we delete only one it could still be
+ // the last timestamp for the data, so check how many timestamps do remain.
+ bool should_remove = (CountTimestampsData(itr->a) == 0);
+ if (should_remove) {
+ if (!RemoveFormElementForID(itr->a))
+ return false;
+ } else {
+ if (!AddToCountOfFormElement(itr->a, -how_many))
+ return false;
+ }
+ AutofillChange::Type change_type =
+ should_remove ? AutofillChange::REMOVE : AutofillChange::UPDATE;
+ changes->push_back(AutofillChange(change_type,
+ AutofillKey(itr->b, itr->c)));
+ }
+
+ return true;
+}
+
+bool AutofillTable::RemoveExpiredFormElements(
+ std::vector<AutofillChange>* changes) {
+ DCHECK(changes);
+
+ base::Time delete_end = AutofillEntry::ExpirationTime();
+ // Query for the pair_id, name, and value of all form elements that
+ // were last used before the |delete_end|.
+ sql::Statement select_for_delete(db_->GetUniqueStatement(
+ "SELECT DISTINCT pair_id, name, value "
+ "FROM autofill WHERE pair_id NOT IN "
+ "(SELECT DISTINCT pair_id "
+ "FROM autofill_dates WHERE date_created >= ?)"));
+ select_for_delete.BindInt64(0, delete_end.ToTimeT());
+ AutofillElementList entries_to_delete;
+ while (select_for_delete.Step()) {
+ entries_to_delete.push_back(MakeTuple(select_for_delete.ColumnInt64(0),
+ select_for_delete.ColumnString16(1),
+ select_for_delete.ColumnString16(2)));
+ }
+
+ if (!select_for_delete.Succeeded())
+ return false;
+
+ sql::Statement delete_data_statement(db_->GetUniqueStatement(
+ "DELETE FROM autofill WHERE pair_id NOT IN ("
+ "SELECT pair_id FROM autofill_dates WHERE date_created >= ?)"));
+ delete_data_statement.BindInt64(0, delete_end.ToTimeT());
+ if (!delete_data_statement.Run())
+ return false;
+
+ sql::Statement delete_times_statement(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates WHERE pair_id NOT IN ("
+ "SELECT pair_id FROM autofill_dates WHERE date_created >= ?)"));
+ delete_times_statement.BindInt64(0, delete_end.ToTimeT());
+ if (!delete_times_statement.Run())
+ return false;
+
+ // Cull remaining entries' timestamps.
+ std::vector<AutofillEntry> entries;
+ if (!GetAllAutofillEntries(&entries))
+ return false;
+ sql::Statement cull_date_entry(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates "
+ "WHERE pair_id == (SELECT pair_id FROM autofill "
+ "WHERE name = ? and value = ?)"
+ "AND date_created != ? AND date_created != ?"));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ cull_date_entry.BindString16(0, entries[i].key().name());
+ cull_date_entry.BindString16(1, entries[i].key().value());
+ cull_date_entry.BindInt64(2,
+ entries[i].timestamps().empty() ? 0 :
+ entries[i].timestamps().front().ToTimeT());
+ cull_date_entry.BindInt64(3,
+ entries[i].timestamps().empty() ? 0 :
+ entries[i].timestamps().back().ToTimeT());
+ if (!cull_date_entry.Run())
+ return false;
+ cull_date_entry.Reset(true);
+ }
+
+ changes->clear();
+ changes->reserve(entries_to_delete.size());
+
+ for (AutofillElementList::iterator it = entries_to_delete.begin();
+ it != entries_to_delete.end(); ++it) {
+ changes->push_back(AutofillChange(
+ AutofillChange::REMOVE, AutofillKey(it->b, it->c)));
+ }
+ return true;
+}
+
+bool AutofillTable::RemoveFormElementForTimeRange(int64 pair_id,
+ const Time& delete_begin,
+ const Time& delete_end,
+ int* how_many) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates WHERE pair_id = ? AND "
+ "date_created >= ? AND date_created < ?"));
+ s.BindInt64(0, pair_id);
+ s.BindInt64(1, delete_begin.is_null() ? 0 : delete_begin.ToTimeT());
+ s.BindInt64(2, delete_end.is_null() ? std::numeric_limits<int64>::max() :
+ delete_end.ToTimeT());
+
+ bool result = s.Run();
+ if (how_many)
+ *how_many = db_->GetLastChangeCount();
+
+ return result;
+}
+
+int AutofillTable::CountTimestampsData(int64 pair_id) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT COUNT(*) FROM autofill_dates WHERE pair_id = ?"));
+ s.BindInt64(0, pair_id);
+ if (!s.Step()) {
+ NOTREACHED();
+ return 0;
+ } else {
+ return s.ColumnInt(0);
+ }
+}
+
+bool AutofillTable::AddToCountOfFormElement(int64 pair_id,
+ int delta) {
+ int count = 0;
+
+ if (!GetCountOfFormElement(pair_id, &count))
+ return false;
+
+ if (count + delta == 0) {
+ // Should remove the element earlier in the code.
+ NOTREACHED();
+ return false;
+ } else {
+ if (!SetCountOfFormElement(pair_id, count + delta))
+ return false;
+ }
+ return true;
+}
+
+bool AutofillTable::GetIDAndCountOfFormElement(
+ const FormFieldData& element,
+ int64* pair_id,
+ int* count) {
+ DCHECK(pair_id);
+ DCHECK(count);
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT pair_id, count FROM autofill "
+ "WHERE name = ? AND value = ?"));
+ s.BindString16(0, element.name);
+ s.BindString16(1, element.value);
+
+ if (!s.is_valid())
+ return false;
+
+ *pair_id = 0;
+ *count = 0;
+
+ if (s.Step()) {
+ *pair_id = s.ColumnInt64(0);
+ *count = s.ColumnInt(1);
+ }
+
+ return true;
+}
+
+bool AutofillTable::GetCountOfFormElement(int64 pair_id, int* count) {
+ DCHECK(count);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT count FROM autofill WHERE pair_id = ?"));
+ s.BindInt64(0, pair_id);
+
+ if (s.Step()) {
+ *count = s.ColumnInt(0);
+ return true;
+ }
+ return false;
+}
+
+bool AutofillTable::SetCountOfFormElement(int64 pair_id, int count) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill SET count = ? WHERE pair_id = ?"));
+ s.BindInt(0, count);
+ s.BindInt64(1, pair_id);
+
+ return s.Run();
+}
+
+bool AutofillTable::InsertFormElement(const FormFieldData& element,
+ int64* pair_id) {
+ DCHECK(pair_id);
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill (name, value, value_lower) VALUES (?,?,?)"));
+ s.BindString16(0, element.name);
+ s.BindString16(1, element.value);
+ s.BindString16(2, base::i18n::ToLower(element.value));
+
+ if (!s.Run())
+ return false;
+
+ *pair_id = db_->GetLastInsertRowId();
+ return true;
+}
+
+bool AutofillTable::InsertPairIDAndDate(int64 pair_id,
+ const Time& date_created) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_dates "
+ "(pair_id, date_created) VALUES (?, ?)"));
+ s.BindInt64(0, pair_id);
+ s.BindInt64(1, date_created.ToTimeT());
+
+ return s.Run();
+}
+
+bool AutofillTable::DeleteLastAccess(int64 pair_id) {
+ // Inner SELECT selects the newest |date_created| for a given |pair_id|.
+ // DELETE deletes only that entry.
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates WHERE pair_id = ? and date_created IN "
+ "(SELECT date_created FROM autofill_dates WHERE pair_id = ? "
+ "ORDER BY date_created DESC LIMIT 1)"));
+ s.BindInt64(0, pair_id);
+ s.BindInt64(1, pair_id);
+
+ return s.Run();
+}
+
+bool AutofillTable::AddFormFieldValuesTime(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes,
+ Time time) {
+ // Only add one new entry for each unique element name. Use |seen_names| to
+ // track this. Add up to |kMaximumUniqueNames| unique entries per form.
+ const size_t kMaximumUniqueNames = 256;
+ std::set<string16> seen_names;
+ bool result = true;
+ for (std::vector<FormFieldData>::const_iterator itr = elements.begin();
+ itr != elements.end(); ++itr) {
+ if (seen_names.size() >= kMaximumUniqueNames)
+ break;
+ if (seen_names.find(itr->name) != seen_names.end())
+ continue;
+ result = result && AddFormFieldValueTime(*itr, changes, time);
+ seen_names.insert(itr->name);
+ }
+ return result;
+}
+
+bool AutofillTable::ClearAutofillEmptyValueElements() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT pair_id FROM autofill WHERE TRIM(value)= \"\""));
+ if (!s.is_valid())
+ return false;
+
+ std::set<int64> ids;
+ while (s.Step())
+ ids.insert(s.ColumnInt64(0));
+ if (!s.Succeeded())
+ return false;
+
+ bool success = true;
+ for (std::set<int64>::const_iterator iter = ids.begin(); iter != ids.end();
+ ++iter) {
+ if (!RemoveFormElementForID(*iter))
+ success = false;
+ }
+
+ return success;
+}
+
+bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) {
+ DCHECK(entries);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT name, value, date_created FROM autofill a JOIN "
+ "autofill_dates ad ON a.pair_id=ad.pair_id"));
+
+ bool first_entry = true;
+ AutofillKey* current_key_ptr = NULL;
+ std::vector<Time>* timestamps_ptr = NULL;
+ string16 name, value;
+ Time time;
+ while (s.Step()) {
+ name = s.ColumnString16(0);
+ value = s.ColumnString16(1);
+ time = Time::FromTimeT(s.ColumnInt64(2));
+
+ if (first_entry) {
+ current_key_ptr = new AutofillKey(name, value);
+
+ timestamps_ptr = new std::vector<Time>;
+ timestamps_ptr->push_back(time);
+
+ first_entry = false;
+ } else {
+ // we've encountered the next entry
+ if (current_key_ptr->name().compare(name) != 0 ||
+ current_key_ptr->value().compare(value) != 0) {
+ AutofillEntry entry(*current_key_ptr, *timestamps_ptr);
+ entries->push_back(entry);
+
+ delete current_key_ptr;
+ delete timestamps_ptr;
+
+ current_key_ptr = new AutofillKey(name, value);
+ timestamps_ptr = new std::vector<Time>;
+ }
+ timestamps_ptr->push_back(time);
+ }
+ }
+
+ // If there is at least one result returned, first_entry will be false.
+ // For this case we need to do a final cleanup step.
+ if (!first_entry) {
+ AutofillEntry entry(*current_key_ptr, *timestamps_ptr);
+ entries->push_back(entry);
+ delete current_key_ptr;
+ delete timestamps_ptr;
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::GetAutofillTimestamps(const string16& name,
+ const string16& value,
+ std::vector<Time>* timestamps) {
+ DCHECK(timestamps);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT date_created FROM autofill a JOIN "
+ "autofill_dates ad ON a.pair_id=ad.pair_id "
+ "WHERE a.name = ? AND a.value = ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, value);
+
+ while (s.Step())
+ timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0)));
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::UpdateAutofillEntries(
+ const std::vector<AutofillEntry>& entries) {
+ if (!entries.size())
+ return true;
+
+ // Remove all existing entries.
+ for (size_t i = 0; i < entries.size(); i++) {
+ std::string sql = "SELECT pair_id FROM autofill "
+ "WHERE name = ? AND value = ?";
+ sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
+ s.BindString16(0, entries[i].key().name());
+ s.BindString16(1, entries[i].key().value());
+
+ if (!s.is_valid())
+ return false;
+
+ if (s.Step()) {
+ if (!RemoveFormElementForID(s.ColumnInt64(0)))
+ return false;
+ }
+ }
+
+ // Insert all the supplied autofill entries.
+ for (size_t i = 0; i < entries.size(); i++) {
+ if (!InsertAutofillEntry(entries[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) {
+ std::string sql = "INSERT INTO autofill (name, value, value_lower, count) "
+ "VALUES (?, ?, ?, ?)";
+ sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
+ s.BindString16(0, entry.key().name());
+ s.BindString16(1, entry.key().value());
+ s.BindString16(2, base::i18n::ToLower(entry.key().value()));
+ s.BindInt(3, entry.timestamps().size());
+
+ if (!s.Run())
+ return false;
+
+ int64 pair_id = db_->GetLastInsertRowId();
+ for (size_t i = 0; i < entry.timestamps().size(); i++) {
+ if (!InsertPairIDAndDate(pair_id, entry.timestamps()[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element,
+ std::vector<AutofillChange>* changes,
+ Time time) {
+ int count = 0;
+ int64 pair_id;
+
+ if (!GetIDAndCountOfFormElement(element, &pair_id, &count))
+ return false;
+
+ if (count == 0 && !InsertFormElement(element, &pair_id))
+ return false;
+
+ if (!SetCountOfFormElement(pair_id, count + 1))
+ return false;
+
+ // If we already have more than 2 times delete last one, before adding new
+ // one.
+ if (count >= 2 && !DeleteLastAccess(pair_id))
+ return false;
+
+ if (!InsertPairIDAndDate(pair_id, time))
+ return false;
+
+ AutofillChange::Type change_type =
+ count == 0 ? AutofillChange::ADD : AutofillChange::UPDATE;
+ changes->push_back(
+ AutofillChange(change_type,
+ AutofillKey(element.name, element.value)));
+ return true;
+}
+
+
+bool AutofillTable::RemoveFormElement(const string16& name,
+ const string16& value) {
+ // Find the id for that pair.
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT pair_id FROM autofill WHERE name = ? AND value= ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, value);
+
+ if (s.Step())
+ return RemoveFormElementForID(s.ColumnInt64(0));
+ return false;
+}
+
+bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
+ if (IsAutofillGUIDInTrash(profile.guid()))
+ return true;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles"
+ "(guid, company_name, address_line_1, address_line_2, city, state,"
+ " zipcode, country, country_code, date_modified)"
+ "VALUES (?,?,?,?,?,?,?,?,?,?)"));
+ BindAutofillProfileToStatement(profile, &s, app_locale_);
+
+ if (!s.Run())
+ return false;
+
+ return AddAutofillProfilePieces(profile, db_);
+}
+
+bool AutofillTable::GetAutofillProfile(const std::string& guid,
+ AutofillProfile** profile) {
+ DCHECK(base::IsValidGUID(guid));
+ DCHECK(profile);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, company_name, address_line_1, address_line_2, city, state,"
+ " zipcode, country, country_code, date_modified "
+ "FROM autofill_profiles "
+ "WHERE guid=?"));
+ s.BindString(0, guid);
+
+ if (!s.Step())
+ return false;
+
+ scoped_ptr<AutofillProfile> p(AutofillProfileFromStatement(s, app_locale_));
+
+ // Get associated name info.
+ AddAutofillProfileNamesToProfile(db_, p.get());
+
+ // Get associated email info.
+ AddAutofillProfileEmailsToProfile(db_, p.get());
+
+ // Get associated phone info.
+ AddAutofillProfilePhonesToProfile(db_, p.get());
+
+ *profile = p.release();
+ return true;
+}
+
+bool AutofillTable::GetAutofillProfiles(
+ std::vector<AutofillProfile*>* profiles) {
+ DCHECK(profiles);
+ profiles->clear();
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ AutofillProfile* profile = NULL;
+ if (!GetAutofillProfile(guid, &profile))
+ return false;
+ profiles->push_back(profile);
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) {
+ DCHECK(base::IsValidGUID(profile.guid()));
+
+ // Don't update anything until the trash has been emptied. There may be
+ // pending modifications to process.
+ if (!IsAutofillProfilesTrashEmpty())
+ return true;
+
+ AutofillProfile* tmp_profile = NULL;
+ if (!GetAutofillProfile(profile.guid(), &tmp_profile))
+ return false;
+
+ // Preserve appropriate modification dates by not updating unchanged profiles.
+ scoped_ptr<AutofillProfile> old_profile(tmp_profile);
+ if (old_profile->Compare(profile) == 0)
+ return true;
+
+ AutofillProfile new_profile(profile);
+ std::vector<string16> values;
+
+ old_profile->GetRawMultiInfo(NAME_FULL, &values);
+ values[0] = new_profile.GetRawInfo(NAME_FULL);
+ new_profile.SetRawMultiInfo(NAME_FULL, values);
+
+ old_profile->GetRawMultiInfo(EMAIL_ADDRESS, &values);
+ values[0] = new_profile.GetRawInfo(EMAIL_ADDRESS);
+ new_profile.SetRawMultiInfo(EMAIL_ADDRESS, values);
+
+ old_profile->GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &values);
+ values[0] = new_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER);
+ new_profile.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, values);
+
+ return UpdateAutofillProfileMulti(new_profile);
+}
+
+bool AutofillTable::UpdateAutofillProfileMulti(const AutofillProfile& profile) {
+ DCHECK(base::IsValidGUID(profile.guid()));
+
+ // Don't update anything until the trash has been emptied. There may be
+ // pending modifications to process.
+ if (!IsAutofillProfilesTrashEmpty())
+ return true;
+
+ AutofillProfile* tmp_profile = NULL;
+ if (!GetAutofillProfile(profile.guid(), &tmp_profile))
+ return false;
+
+ // Preserve appropriate modification dates by not updating unchanged profiles.
+ scoped_ptr<AutofillProfile> old_profile(tmp_profile);
+ if (old_profile->Compare(profile) == 0)
+ return true;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles "
+ "SET guid=?, company_name=?, address_line_1=?, address_line_2=?, "
+ " city=?, state=?, zipcode=?, country=?, country_code=?, "
+ " date_modified=? "
+ "WHERE guid=?"));
+ BindAutofillProfileToStatement(profile, &s, app_locale_);
+ s.BindString(10, profile.guid());
+
+ bool result = s.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ if (!result)
+ return result;
+
+ // Remove the old names, emails, and phone numbers.
+ if (!RemoveAutofillProfilePieces(profile.guid(), db_))
+ return false;
+
+ return AddAutofillProfilePieces(profile, db_);
+}
+
+bool AutofillTable::RemoveAutofillProfile(const std::string& guid) {
+ DCHECK(base::IsValidGUID(guid));
+
+ if (IsAutofillGUIDInTrash(guid)) {
+ sql::Statement s_trash(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles_trash WHERE guid = ?"));
+ s_trash.BindString(0, guid);
+
+ bool success = s_trash.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0) << "Expected item in trash";
+ return success;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ if (!s.Run())
+ return false;
+
+ return RemoveAutofillProfilePieces(guid, db_);
+}
+
+bool AutofillTable::ClearAutofillProfiles() {
+ sql::Statement s1(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles"));
+
+ if (!s1.Run())
+ return false;
+
+ sql::Statement s2(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profile_names"));
+
+ if (!s2.Run())
+ return false;
+
+ sql::Statement s3(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profile_emails"));
+
+ if (!s3.Run())
+ return false;
+
+ sql::Statement s4(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profile_phones"));
+
+ return s4.Run();
+}
+
+bool AutofillTable::AddCreditCard(const CreditCard& credit_card) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO credit_cards"
+ "(guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified)"
+ "VALUES (?,?,?,?,?,?)"));
+ BindCreditCardToStatement(credit_card, &s);
+
+ if (!s.Run())
+ return false;
+
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ return true;
+}
+
+bool AutofillTable::GetCreditCard(const std::string& guid,
+ CreditCard** credit_card) {
+ DCHECK(base::IsValidGUID(guid));
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards "
+ "WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ if (!s.Step())
+ return false;
+
+ *credit_card = CreditCardFromStatement(s);
+ return true;
+}
+
+bool AutofillTable::GetCreditCards(
+ std::vector<CreditCard*>* credit_cards) {
+ DCHECK(credit_cards);
+ credit_cards->clear();
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM credit_cards"));
+
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ CreditCard* credit_card = NULL;
+ if (!GetCreditCard(guid, &credit_card))
+ return false;
+ credit_cards->push_back(credit_card);
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) {
+ DCHECK(base::IsValidGUID(credit_card.guid()));
+
+ CreditCard* tmp_credit_card = NULL;
+ if (!GetCreditCard(credit_card.guid(), &tmp_credit_card))
+ return false;
+
+ // Preserve appropriate modification dates by not updating unchanged cards.
+ scoped_ptr<CreditCard> old_credit_card(tmp_credit_card);
+ if (*old_credit_card == credit_card)
+ return true;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE credit_cards "
+ "SET guid=?, name_on_card=?, expiration_month=?, "
+ " expiration_year=?, card_number_encrypted=?, date_modified=? "
+ "WHERE guid=?"));
+ BindCreditCardToStatement(credit_card, &s);
+ s.BindString(6, credit_card.guid());
+
+ bool result = s.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ return result;
+}
+
+bool AutofillTable::RemoveCreditCard(const std::string& guid) {
+ DCHECK(base::IsValidGUID(guid));
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM credit_cards WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ return s.Run();
+}
+
+bool AutofillTable::RemoveAutofillDataModifiedBetween(
+ const Time& delete_begin,
+ const Time& delete_end,
+ std::vector<std::string>* profile_guids,
+ std::vector<std::string>* credit_card_guids) {
+ DCHECK(delete_end.is_null() || delete_begin < delete_end);
+
+ time_t delete_begin_t = delete_begin.ToTimeT();
+ time_t delete_end_t =
+ (delete_end.is_null() || delete_end == base::Time::Max()) ?
+ std::numeric_limits<time_t>::max() : delete_end.ToTimeT();
+
+ // Remember Autofill profiles in the time range.
+ sql::Statement s_profiles_get(db_->GetUniqueStatement(
+ "SELECT guid FROM autofill_profiles "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_profiles_get.BindInt64(0, delete_begin_t);
+ s_profiles_get.BindInt64(1, delete_end_t);
+
+ profile_guids->clear();
+ while (s_profiles_get.Step()) {
+ std::string guid = s_profiles_get.ColumnString(0);
+ profile_guids->push_back(guid);
+ }
+ if (!s_profiles_get.Succeeded())
+ return false;
+
+ // Remove Autofill profiles in the time range.
+ sql::Statement s_profiles(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_profiles.BindInt64(0, delete_begin_t);
+ s_profiles.BindInt64(1, delete_end_t);
+
+ if (!s_profiles.Run())
+ return false;
+
+ // Remember Autofill credit cards in the time range.
+ sql::Statement s_credit_cards_get(db_->GetUniqueStatement(
+ "SELECT guid FROM credit_cards "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_credit_cards_get.BindInt64(0, delete_begin_t);
+ s_credit_cards_get.BindInt64(1, delete_end_t);
+
+ credit_card_guids->clear();
+ while (s_credit_cards_get.Step()) {
+ std::string guid = s_credit_cards_get.ColumnString(0);
+ credit_card_guids->push_back(guid);
+ }
+ if (!s_credit_cards_get.Succeeded())
+ return false;
+
+ // Remove Autofill credit cards in the time range.
+ sql::Statement s_credit_cards(db_->GetUniqueStatement(
+ "DELETE FROM credit_cards "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_credit_cards.BindInt64(0, delete_begin_t);
+ s_credit_cards.BindInt64(1, delete_end_t);
+
+ return s_credit_cards.Run();
+}
+
+bool AutofillTable::GetAutofillProfilesInTrash(
+ std::vector<std::string>* guids) {
+ guids->clear();
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles_trash"));
+
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ guids->push_back(guid);
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::EmptyAutofillProfilesTrash() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles_trash"));
+
+ return s.Run();
+}
+
+
+bool AutofillTable::RemoveFormElementForID(int64 pair_id) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill WHERE pair_id = ?"));
+ s.BindInt64(0, pair_id);
+
+ if (s.Run())
+ return RemoveFormElementForTimeRange(pair_id, Time(), Time(), NULL);
+
+ return false;
+}
+
+bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles_trash"
+ " (guid) "
+ "VALUES (?)"));
+ s.BindString(0, guid);
+
+ return s.Run();
+}
+
+bool AutofillTable::IsAutofillProfilesTrashEmpty() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles_trash"));
+
+ return !s.Step();
+}
+
+bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles_trash "
+ "WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ return s.Step();
+}
+
+bool AutofillTable::InitMainTable() {
+ if (!db_->DoesTableExist("autofill")) {
+ if (!db_->Execute("CREATE TABLE autofill ("
+ "name VARCHAR, "
+ "value VARCHAR, "
+ "value_lower VARCHAR, "
+ "pair_id INTEGER PRIMARY KEY, "
+ "count INTEGER DEFAULT 1)")) {
+ NOTREACHED();
+ return false;
+ }
+ if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)")) {
+ NOTREACHED();
+ return false;
+ }
+ if (!db_->Execute("CREATE INDEX autofill_name_value_lower ON "
+ "autofill (name, value_lower)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitCreditCardsTable() {
+ if (!db_->DoesTableExist("credit_cards")) {
+ if (!db_->Execute("CREATE TABLE credit_cards ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AutofillTable::InitDatesTable() {
+ if (!db_->DoesTableExist("autofill_dates")) {
+ if (!db_->Execute("CREATE TABLE autofill_dates ( "
+ "pair_id INTEGER DEFAULT 0, "
+ "date_created INTEGER DEFAULT 0)")) {
+ NOTREACHED();
+ return false;
+ }
+ if (!db_->Execute("CREATE INDEX autofill_dates_pair_id ON "
+ "autofill_dates (pair_id)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfilesTable() {
+ if (!db_->DoesTableExist("autofill_profiles")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "country_code VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileNamesTable() {
+ if (!db_->DoesTableExist("autofill_profile_names")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_names ( "
+ "guid VARCHAR, "
+ "first_name VARCHAR, "
+ "middle_name VARCHAR, "
+ "last_name VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileEmailsTable() {
+ if (!db_->DoesTableExist("autofill_profile_emails")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_emails ( "
+ "guid VARCHAR, "
+ "email VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfilePhonesTable() {
+ if (!db_->DoesTableExist("autofill_profile_phones")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_phones ( "
+ "guid VARCHAR, "
+ "type INTEGER DEFAULT 0, "
+ "number VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileTrashTable() {
+ if (!db_->DoesTableExist("autofill_profiles_trash")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_trash ( "
+ "guid VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+// Add the card_number_encrypted column if credit card table was not
+// created in this build (otherwise the column already exists).
+// WARNING: Do not change the order of the execution of the SQL
+// statements in this case! Profile corruption and data migration
+// issues WILL OCCUR. See http://crbug.com/10913
+//
+// The problem is that if a user has a profile which was created before
+// r37036, when the credit_cards table was added, and then failed to
+// update this profile between the credit card addition and the addition
+// of the "encrypted" columns (44963), the next data migration will put
+// the user's profile in an incoherent state: The user will update from
+// a data profile set to be earlier than 22, and therefore pass through
+// this update case. But because the user did not have a credit_cards
+// table before starting Chrome, it will have just been initialized
+// above, and so already have these columns -- and thus this data
+// update step will have failed.
+//
+// The false assumption in this case is that at this step in the
+// migration, the user has a credit card table, and that this
+// table does not include encrypted columns!
+// Because this case does not roll back the complete set of SQL
+// transactions properly in case of failure (that is, it does not
+// roll back the table initialization done above), the incoherent
+// profile will now see itself as being at version 22 -- but include a
+// fully initialized credit_cards table. Every time Chrome runs, it
+// will try to update the web database and fail at this step, unless
+// we allow for the faulty assumption described above by checking for
+// the existence of the columns only AFTER we've executed the commands
+// to add them.
+bool AutofillTable::MigrateToVersion23AddCardNumberEncryptedColumn() {
+ if (!db_->DoesColumnExist("credit_cards", "card_number_encrypted")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "card_number_encrypted BLOB DEFAULT NULL")) {
+ LOG(WARNING) << "Could not add card_number_encrypted to "
+ "credit_cards table.";
+ return false;
+ }
+ }
+
+ if (!db_->DoesColumnExist("credit_cards", "verification_code_encrypted")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "verification_code_encrypted BLOB DEFAULT NULL")) {
+ LOG(WARNING) << "Could not add verification_code_encrypted to "
+ "credit_cards table.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// One-time cleanup for http://crbug.com/38364 - In the presence of
+// multi-byte UTF-8 characters, that bug could cause Autofill strings
+// to grow larger and more corrupt with each save. The cleanup removes
+// any row with a string field larger than a reasonable size. The string
+// fields examined here are precisely the ones that were subject to
+// corruption by the original bug.
+bool AutofillTable::MigrateToVersion24CleanupOversizedStringFields() {
+ const std::string autofill_is_too_big =
+ "max(length(name), length(value)) > 500";
+
+ const std::string credit_cards_is_too_big =
+ "max(length(label), length(name_on_card), length(type), "
+ " length(expiration_month), length(expiration_year), "
+ " length(billing_address), length(shipping_address) "
+ ") > 500";
+
+ const std::string autofill_profiles_is_too_big =
+ "max(length(label), length(first_name), "
+ " length(middle_name), length(last_name), length(email), "
+ " length(company_name), length(address_line_1), "
+ " length(address_line_2), length(city), length(state), "
+ " length(zipcode), length(country), length(phone)) > 500";
+
+ std::string query = "DELETE FROM autofill_dates WHERE pair_id IN ("
+ "SELECT pair_id FROM autofill WHERE " + autofill_is_too_big + ")";
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+
+ query = "DELETE FROM autofill WHERE " + autofill_is_too_big;
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+
+ // Only delete from legacy credit card tables where specific columns exist.
+ if (db_->DoesColumnExist("credit_cards", "label") &&
+ db_->DoesColumnExist("credit_cards", "name_on_card") &&
+ db_->DoesColumnExist("credit_cards", "type") &&
+ db_->DoesColumnExist("credit_cards", "expiration_month") &&
+ db_->DoesColumnExist("credit_cards", "expiration_year") &&
+ db_->DoesColumnExist("credit_cards", "billing_address") &&
+ db_->DoesColumnExist("credit_cards", "shipping_address") &&
+ db_->DoesColumnExist("autofill_profiles", "label")) {
+ query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big +
+ ") OR label IN (SELECT label FROM autofill_profiles WHERE " +
+ autofill_profiles_is_too_big + ")";
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+ }
+
+ if (db_->DoesColumnExist("autofill_profiles", "label")) {
+ query = "DELETE FROM autofill_profiles WHERE " +
+ autofill_profiles_is_too_big;
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+ }
+
+ return true;
+}
+
+// Change the credit_cards.billing_address column from a string to an
+// int. The stored string is the label of an address, so we have to
+// select the unique ID of this address using the label as a foreign
+// key into the |autofill_profiles| table.
+bool AutofillTable::MigrateToVersion27UpdateLegacyCreditCards() {
+ // Only migrate from legacy credit card tables where specific columns
+ // exist.
+ if (!(db_->DoesColumnExist("credit_cards", "unique_id") &&
+ db_->DoesColumnExist("credit_cards", "billing_address") &&
+ db_->DoesColumnExist("autofill_profiles", "unique_id"))) {
+ return true;
+ }
+
+ std::string stmt =
+ "SELECT credit_cards.unique_id, autofill_profiles.unique_id "
+ "FROM autofill_profiles, credit_cards "
+ "WHERE credit_cards.billing_address = autofill_profiles.label";
+ sql::Statement s(db_->GetUniqueStatement(stmt.c_str()));
+
+ std::map<int, int> cc_billing_map;
+ while (s.Step())
+ cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1);
+ if (!s.Succeeded())
+ return false;
+
+ // Windows already stores the IDs as strings in |billing_address|. Try
+ // to convert those.
+ if (cc_billing_map.empty()) {
+ std::string stmt = "SELECT unique_id,billing_address FROM credit_cards";
+ sql::Statement s(db_->GetUniqueStatement(stmt.c_str()));
+
+ while (s.Step()) {
+ int id = 0;
+ if (base::StringToInt(s.ColumnString(1), &id))
+ cc_billing_map[s.ColumnInt(0)] = id;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "label VARCHAR, "
+ "unique_id INTEGER PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "type VARCHAR, "
+ "card_number VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "verification_code VARCHAR, "
+ "billing_address INTEGER, "
+ "shipping_address VARCHAR, "
+ "card_number_encrypted BLOB, "
+ "verification_code_encrypted BLOB)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT label,unique_id,name_on_card,type,card_number,"
+ "expiration_month,expiration_year,verification_code,0,"
+ "shipping_address,card_number_encrypted,"
+ "verification_code_encrypted FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+
+ for (std::map<int, int>::const_iterator iter = cc_billing_map.begin();
+ iter != cc_billing_map.end(); ++iter) {
+ sql::Statement s(db_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "UPDATE credit_cards SET billing_address=? WHERE unique_id=?"));
+ s.BindInt(0, (*iter).second);
+ s.BindInt(1, (*iter).first);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion30AddDateModifed() {
+ // Add date_modified to autofill_profiles.
+ if (!db_->DoesColumnExist("autofill_profiles", "date_modified")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "date_modified INTEGER NON NULL DEFAULT 0")) {
+ return false;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified=?"));
+ s.BindInt64(0, Time::Now().ToTimeT());
+
+ if (!s.Run())
+ return false;
+ }
+
+ // Add date_modified to credit_cards.
+ if (!db_->DoesColumnExist("credit_cards", "date_modified")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "date_modified INTEGER NON NULL DEFAULT 0")) {
+ return false;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified=?"));
+ s.BindInt64(0, Time::Now().ToTimeT());
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion31AddGUIDToCreditCardsAndProfiles() {
+ // Note that we need to check for the guid column's existence due to the
+ // fact that for a version 22 database the |autofill_profiles| table
+ // gets created fresh with |InitAutofillProfilesTable|.
+ if (!db_->DoesColumnExist("autofill_profiles", "guid")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "guid VARCHAR NOT NULL DEFAULT \"\"")) {
+ return false;
+ }
+
+ // Set all the |guid| fields to valid values.
+
+ sql::Statement s(db_->GetUniqueStatement("SELECT unique_id "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE autofill_profiles "
+ "SET guid=? WHERE unique_id=?"));
+ update_s.BindString(0, base::GenerateGUID());
+ update_s.BindInt(1, s.ColumnInt(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ // Note that we need to check for the guid column's existence due to the
+ // fact that for a version 22 database the |autofill_profiles| table
+ // gets created fresh with |InitAutofillProfilesTable|.
+ if (!db_->DoesColumnExist("credit_cards", "guid")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "guid VARCHAR NOT NULL DEFAULT \"\"")) {
+ return false;
+ }
+
+ // Set all the |guid| fields to valid values.
+
+ sql::Statement s(db_->GetUniqueStatement("SELECT unique_id "
+ "FROM credit_cards"));
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE credit_cards "
+ "set guid=? WHERE unique_id=?"));
+ update_s.BindString(0, base::GenerateGUID());
+ update_s.BindInt(1, s.ColumnInt(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion32UpdateProfilesAndCreditCards() {
+ if (db_->DoesColumnExist("autofill_profiles", "unique_id")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "label VARCHAR, "
+ "first_name VARCHAR, "
+ "middle_name VARCHAR, "
+ "last_name VARCHAR, "
+ "email VARCHAR, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "phone VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO autofill_profiles_temp "
+ "SELECT guid, label, first_name, middle_name, last_name, email, "
+ "company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, date_modified "
+ "FROM autofill_profiles")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE autofill_profiles"))
+ return false;
+
+ if (!db_->Execute(
+ "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
+ return false;
+ }
+ }
+
+ if (db_->DoesColumnExist("credit_cards", "unique_id")) {
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "label VARCHAR, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT guid, label, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+ }
+
+ return true;
+}
+
+// Test the existence of the |first_name| column as an indication that
+// we need a migration. It is possible that the new |autofill_profiles|
+// schema is in place because the table was newly created when migrating
+// from a pre-version-22 database.
+bool AutofillTable::MigrateToVersion33ProfilesBasedOnFirstName() {
+ if (db_->DoesColumnExist("autofill_profiles", "first_name")) {
+ // Create autofill_profiles_temp table that will receive the data.
+ if (!db_->DoesTableExist("autofill_profiles_temp")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name, email, "
+ "company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, date_modified "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ AutofillProfile profile;
+ profile.set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(profile.guid()));
+
+ profile.SetRawInfo(NAME_FIRST, s.ColumnString16(1));
+ profile.SetRawInfo(NAME_MIDDLE, s.ColumnString16(2));
+ profile.SetRawInfo(NAME_LAST, s.ColumnString16(3));
+ profile.SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(4));
+ profile.SetRawInfo(COMPANY_NAME, s.ColumnString16(5));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(6));
+ profile.SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(7));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(8));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(9));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(10));
+ profile.SetInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(11), app_locale_);
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(12));
+ int64 date_modified = s.ColumnInt64(13);
+
+ sql::Statement s_insert(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles_temp"
+ "(guid, company_name, address_line_1, address_line_2, city,"
+ " state, zipcode, country, date_modified)"
+ "VALUES (?,?,?,?,?,?,?,?,?)"));
+ s_insert.BindString(0, profile.guid());
+ s_insert.BindString16(1, profile.GetRawInfo(COMPANY_NAME));
+ s_insert.BindString16(2, profile.GetRawInfo(ADDRESS_HOME_LINE1));
+ s_insert.BindString16(3, profile.GetRawInfo(ADDRESS_HOME_LINE2));
+ s_insert.BindString16(4, profile.GetRawInfo(ADDRESS_HOME_CITY));
+ s_insert.BindString16(5, profile.GetRawInfo(ADDRESS_HOME_STATE));
+ s_insert.BindString16(6, profile.GetRawInfo(ADDRESS_HOME_ZIP));
+ s_insert.BindString16(7, profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ s_insert.BindInt64(8, date_modified);
+
+ if (!s_insert.Run())
+ return false;
+
+ // Add the other bits: names, emails, and phone numbers.
+ if (!AddAutofillProfilePieces(profile, db_))
+ return false;
+ } // endwhile
+ if (!s.Succeeded())
+ return false;
+
+ if (!db_->Execute("DROP TABLE autofill_profiles"))
+ return false;
+
+ if (!db_->Execute(
+ "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
+ return false;
+ }
+ }
+
+ // Remove the labels column from the credit_cards table.
+ if (db_->DoesColumnExist("credit_cards", "label")) {
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT guid, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+ }
+
+ return true;
+}
+
+// Test the existence of the |country_code| column as an indication that
+// we need a migration. It is possible that the new |autofill_profiles|
+// schema is in place because the table was newly created when migrating
+// from a pre-version-22 database.
+bool AutofillTable::MigrateToVersion34ProfilesBasedOnCountryCode() {
+ if (!db_->DoesColumnExist("autofill_profiles", "country_code")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "country_code VARCHAR")) {
+ return false;
+ }
+
+ // Set all the |country_code| fields to match existing |country| values.
+ sql::Statement s(db_->GetUniqueStatement("SELECT guid, country "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE autofill_profiles "
+ "SET country_code=? WHERE guid=?"));
+
+ string16 country = s.ColumnString16(1);
+ update_s.BindString(0, AutofillCountry::GetCountryCode(country,
+ app_locale_));
+ update_s.BindString(1, s.ColumnString(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ return true;
+}
+
+// Correct all country codes with value "UK" to be "GB". This data
+// was mistakenly introduced in build 686.0. This migration is to clean
+// it up. See http://crbug.com/74511 for details.
+bool AutofillTable::MigrateToVersion35GreatBritainCountryCodes() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET country_code=\"GB\" "
+ "WHERE country_code=\"UK\""));
+
+ return s.Run();
+}
+
+// Merge and cull older profiles where possible.
+bool AutofillTable::MigrateToVersion37MergeAndCullOlderProfiles() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, date_modified FROM autofill_profiles"));
+
+ // Accumulate the good profiles.
+ std::vector<AutofillProfile> accumulated_profiles;
+ std::vector<AutofillProfile*> accumulated_profiles_p;
+ std::map<std::string, int64> modification_map;
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ int64 date_modified = s.ColumnInt64(1);
+ modification_map.insert(
+ std::pair<std::string, int64>(guid, date_modified));
+ AutofillProfile* profile = NULL;
+ if (!GetAutofillProfile(guid, &profile))
+ return false;
+
+ scoped_ptr<AutofillProfile> p(profile);
+
+ if (PersonalDataManager::IsValidLearnableProfile(*p, app_locale_)) {
+ std::vector<AutofillProfile> merged_profiles;
+ bool merged = PersonalDataManager::MergeProfile(
+ *p, accumulated_profiles_p, app_locale_, &merged_profiles);
+
+ std::swap(accumulated_profiles, merged_profiles);
+
+ accumulated_profiles_p.clear();
+ accumulated_profiles_p.resize(accumulated_profiles.size());
+ std::transform(accumulated_profiles.begin(),
+ accumulated_profiles.end(),
+ accumulated_profiles_p.begin(),
+ address_of<AutofillProfile>);
+
+ // If the profile got merged trash the original.
+ if (merged)
+ AddAutofillGUIDToTrash(p->guid());
+
+ } else {
+ // An invalid profile, so trash it.
+ AddAutofillGUIDToTrash(p->guid());
+ }
+ } // endwhile
+ if (!s.Succeeded())
+ return false;
+
+ // Drop the current profiles.
+ if (!ClearAutofillProfiles())
+ return false;
+
+ // Add the newly merged profiles back in.
+ for (std::vector<AutofillProfile>::const_iterator
+ iter = accumulated_profiles.begin();
+ iter != accumulated_profiles.end();
+ ++iter) {
+ if (!AddAutofillProfile(*iter))
+ return false;
+
+ // Fix up the original modification date.
+ std::map<std::string, int64>::const_iterator date_item =
+ modification_map.find(iter->guid());
+ if (date_item == modification_map.end())
+ return false;
+
+ sql::Statement s_date(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified=? "
+ "WHERE guid=?"));
+ s_date.BindInt64(0, date_item->second);
+ s_date.BindString(1, iter->guid());
+
+ if (!s_date.Run())
+ return false;
+ }
+
+ return true;
+}
diff --git a/components/autofill/browser/webdata/autofill_table.h b/components/autofill/browser/webdata/autofill_table.h
new file mode 100644
index 0000000..24afba8
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_table.h
@@ -0,0 +1,366 @@
+// Copyright (c) 2012 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 COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_TABLE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_TABLE_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "components/webdata/common/web_database_table.h"
+
+class AutofillChange;
+class AutofillEntry;
+class AutofillProfile;
+class AutofillTableTest;
+class CreditCard;
+class WebDatabase;
+
+struct FormFieldData;
+
+namespace base {
+class Time;
+}
+
+// This class manages the various Autofill tables within the SQLite database
+// passed to the constructor. It expects the following schemas:
+//
+// Note: The database stores time in seconds, UTC.
+//
+// autofill
+// name The name of the input as specified in the html.
+// value The literal contents of the text field.
+// value_lower The contents of the text field made lower_case.
+// pair_id An ID number unique to the row in the table.
+// count How many times the user has entered the string |value|
+// in a field of name |name|.
+//
+// autofill_dates This table associates a row to each separate time the
+// user submits a form containing a certain name/value
+// pair. The |pair_id| should match the |pair_id| field
+// in the appropriate row of the autofill table.
+// pair_id
+// date_created
+//
+// autofill_profiles This table contains Autofill profile data added by the
+// user with the Autofill dialog. Most of the columns are
+// standard entries in a contact information form.
+//
+// guid A guid string to uniquely identify the profile.
+// Added in version 31.
+// company_name
+// address_line_1
+// address_line_2
+// city
+// state
+// zipcode
+// country The country name. Deprecated, should be removed once
+// the stable channel reaches version 11.
+// country_code
+// date_modified The date on which this profile was last modified.
+// Added in version 30.
+//
+// autofill_profile_names
+// This table contains the multi-valued name fields
+// associated with a profile.
+//
+// guid The guid string that identifies the profile to which
+// the name belongs.
+// first_name
+// middle_name
+// last_name
+//
+// autofill_profile_emails
+// This table contains the multi-valued email fields
+// associated with a profile.
+//
+// guid The guid string that identifies the profile to which
+// the email belongs.
+// email
+//
+// autofill_profile_phones
+// This table contains the multi-valued phone fields
+// associated with a profile.
+//
+// guid The guid string that identifies the profile to which
+// the phone number belongs.
+// type An integer constant designating either phone type of the
+// number.
+// TODO(jhawkins): Remove the type column and migrate the
+// database.
+// number
+//
+// autofill_profiles_trash
+// This table contains guids of "trashed" autofill
+// profiles. When a profile is removed its guid is added
+// to this table so that Sync can perform deferred removal.
+//
+// guid The guid string that identifies the trashed profile.
+//
+// credit_cards This table contains credit card data added by the user
+// with the Autofill dialog. Most of the columns are
+// standard entries in a credit card form.
+//
+// guid A guid string to uniquely identify the profile.
+// Added in version 31.
+// name_on_card
+// expiration_month
+// expiration_year
+// card_number_encrypted Stores encrypted credit card number.
+// date_modified The date on which this entry was last modified.
+// Added in version 30.
+//
+class AutofillTable : public WebDatabaseTable {
+ public:
+ explicit AutofillTable(const std::string& app_locale);
+ virtual ~AutofillTable();
+
+ // Retrieves the AutofillTable* owned by |database|.
+ static AutofillTable* FromWebDatabase(WebDatabase* db);
+
+ virtual WebDatabaseTable::TypeKey GetTypeKey() const OVERRIDE;
+ virtual bool Init(sql::Connection* db, sql::MetaTable* meta_table) OVERRIDE;
+ virtual bool IsSyncable() OVERRIDE;
+ virtual bool MigrateToVersion(int version,
+ bool* update_compatible_version) OVERRIDE;
+
+ // Records the form elements in |elements| in the database in the
+ // autofill table. A list of all added and updated autofill entries
+ // is returned in the changes out parameter.
+ bool AddFormFieldValues(const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes);
+
+ // Records a single form element in the database in the autofill table. A list
+ // of all added and updated autofill entries is returned in the changes out
+ // parameter.
+ bool AddFormFieldValue(const FormFieldData& element,
+ std::vector<AutofillChange>* changes);
+
+ // Retrieves a vector of all values which have been recorded in the autofill
+ // table as the value in a form element with name |name| and which start with
+ // |prefix|. The comparison of the prefix is case insensitive.
+ bool GetFormValuesForElementName(const string16& name,
+ const string16& prefix,
+ std::vector<string16>* values,
+ int limit);
+
+ // Removes rows from autofill_dates if they were created on or after
+ // |delete_begin| and strictly before |delete_end|. Decrements the
+ // count of the corresponding rows in the autofill table, and
+ // removes those rows if the count goes to 0. A list of all changed
+ // keys and whether each was updater or removed is returned in the
+ // changes out parameter.
+ bool RemoveFormElementsAddedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end,
+ std::vector<AutofillChange>* changes);
+
+ // Removes rows from autofill_dates if they were accessed strictly before
+ // |AutofillEntry::ExpirationTime()|. Removes the corresponding row from the
+ // autofill table. Also culls timestamps to only two. TODO(georgey): remove
+ // culling in future versions.
+ bool RemoveExpiredFormElements(std::vector<AutofillChange>* changes);
+
+ // Removes from autofill_dates rows with given pair_id where date_created lies
+ // between |delete_begin| and |delete_end|.
+ bool RemoveFormElementForTimeRange(int64 pair_id,
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ int* how_many);
+
+ // Increments the count in the row corresponding to |pair_id| by |delta|.
+ bool AddToCountOfFormElement(int64 pair_id, int delta);
+
+ // Counts how many timestamp data rows are in the |autofill_dates| table for
+ // a given |pair_id|. GetCountOfFormElement() on the other hand gives the
+ // |count| property for a given id.
+ int CountTimestampsData(int64 pair_id);
+
+ // Gets the pair_id and count entries from name and value specified in
+ // |element|. Sets *pair_id and *count to 0 if there is no such row in
+ // the table.
+ bool GetIDAndCountOfFormElement(const FormFieldData& element,
+ int64* pair_id,
+ int* count);
+
+ // Gets the count only given the pair_id.
+ bool GetCountOfFormElement(int64 pair_id, int* count);
+
+ // Updates the count entry in the row corresponding to |pair_id| to |count|.
+ bool SetCountOfFormElement(int64 pair_id, int count);
+
+ // Adds a new row to the autofill table with name and value given in
+ // |element|. Sets *pair_id to the pair_id of the new row.
+ bool InsertFormElement(const FormFieldData& element,
+ int64* pair_id);
+
+ // Adds a new row to the autofill_dates table.
+ bool InsertPairIDAndDate(int64 pair_id, const base::Time& date_created);
+
+ // Deletes last access to the Autofill data from the autofill_dates table.
+ bool DeleteLastAccess(int64 pair_id);
+
+ // Removes row from the autofill tables given |pair_id|.
+ bool RemoveFormElementForID(int64 pair_id);
+
+ // Removes row from the autofill tables for the given |name| |value| pair.
+ virtual bool RemoveFormElement(const string16& name, const string16& value);
+
+ // Retrieves all of the entries in the autofill table.
+ virtual bool GetAllAutofillEntries(std::vector<AutofillEntry>* entries);
+
+ // Retrieves a single entry from the autofill table.
+ virtual bool GetAutofillTimestamps(const string16& name,
+ const string16& value,
+ std::vector<base::Time>* timestamps);
+
+ // Replaces existing autofill entries with the entries supplied in
+ // the argument. If the entry does not already exist, it will be
+ // added.
+ virtual bool UpdateAutofillEntries(const std::vector<AutofillEntry>& entries);
+
+ // Records a single Autofill profile in the autofill_profiles table.
+ virtual bool AddAutofillProfile(const AutofillProfile& profile);
+
+ // Updates the database values for the specified profile.
+ // DEPRECATED: Use |UpdateAutofillProfileMulti| instead.
+ virtual bool UpdateAutofillProfile(const AutofillProfile& profile);
+
+ // Updates the database values for the specified profile. Mulit-value aware.
+ virtual bool UpdateAutofillProfileMulti(const AutofillProfile& profile);
+
+ // Removes a row from the autofill_profiles table. |guid| is the identifier
+ // of the profile to remove.
+ virtual bool RemoveAutofillProfile(const std::string& guid);
+
+ // Retrieves a profile with guid |guid|. The caller owns |profile|.
+ bool GetAutofillProfile(const std::string& guid, AutofillProfile** profile);
+
+ // Retrieves all profiles in the database. Caller owns the returned profiles.
+ virtual bool GetAutofillProfiles(std::vector<AutofillProfile*>* profiles);
+
+ // Records a single credit card in the credit_cards table.
+ bool AddCreditCard(const CreditCard& credit_card);
+
+ // Updates the database values for the specified credit card.
+ bool UpdateCreditCard(const CreditCard& credit_card);
+
+ // Removes a row from the credit_cards table. |guid| is the identifer of the
+ // credit card to remove.
+ bool RemoveCreditCard(const std::string& guid);
+
+ // Retrieves a credit card with guid |guid|. The caller owns
+ // |credit_card_id|.
+ bool GetCreditCard(const std::string& guid, CreditCard** credit_card);
+
+ // Retrieves all credit cards in the database. Caller owns the returned
+ // credit cards.
+ virtual bool GetCreditCards(std::vector<CreditCard*>* credit_cards);
+
+ // Removes rows from autofill_profiles and credit_cards if they were created
+ // on or after |delete_begin| and strictly before |delete_end|. Returns lists
+ // of deleted guids in |profile_guids| and |credit_card_guids|. Return value
+ // is true if all rows were successfully removed. Returns false on database
+ // error. In that case, the output vector state is undefined, and may be
+ // partially filled.
+ bool RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ std::vector<std::string>* profile_guids,
+ std::vector<std::string>* credit_card_guids);
+
+ // Retrieves all profiles in the database that have been deleted since last
+ // "empty" of the trash.
+ bool GetAutofillProfilesInTrash(std::vector<std::string>* guids);
+
+ // Empties the Autofill profiles "trash can".
+ bool EmptyAutofillProfilesTrash();
+
+ // Removes empty values for autofill that were incorrectly stored in the DB
+ // See bug http://crbug.com/6111
+ bool ClearAutofillEmptyValueElements();
+
+ // Retrieves all profiles in the database that have been deleted since last
+ // "empty" of the trash.
+ bool AddAutofillGUIDToTrash(const std::string& guid);
+
+ // Clear all profiles.
+ bool ClearAutofillProfiles();
+
+ // Table migration functions.
+ bool MigrateToVersion23AddCardNumberEncryptedColumn();
+ bool MigrateToVersion24CleanupOversizedStringFields();
+ bool MigrateToVersion27UpdateLegacyCreditCards();
+ bool MigrateToVersion30AddDateModifed();
+ bool MigrateToVersion31AddGUIDToCreditCardsAndProfiles();
+ bool MigrateToVersion32UpdateProfilesAndCreditCards();
+ bool MigrateToVersion33ProfilesBasedOnFirstName();
+ bool MigrateToVersion34ProfilesBasedOnCountryCode();
+ bool MigrateToVersion35GreatBritainCountryCodes();
+ bool MigrateToVersion37MergeAndCullOlderProfiles();
+
+ // Max data length saved in the table;
+ static const size_t kMaxDataLength;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddChanges);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_RemoveBetweenChanges);
+
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_UpdateDontReplace);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddFormFieldValues);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfile);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateAutofillProfile);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfileTrash);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfileTrashInteraction);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ RemoveAutofillDataModifiedBetween);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, CreditCard);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetAllAutofillEntries_OneResult);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetAllAutofillEntries_TwoDistinct);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetAllAutofillEntries_TwoSame);
+
+ // Methods for adding autofill entries at a specified time. For
+ // testing only.
+ bool AddFormFieldValuesTime(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes,
+ base::Time time);
+ bool AddFormFieldValueTime(const FormFieldData& element,
+ std::vector<AutofillChange>* changes,
+ base::Time time);
+
+ // Insert a single AutofillEntry into the autofill/autofill_dates tables.
+ bool InsertAutofillEntry(const AutofillEntry& entry);
+
+ // Checks if the trash is empty.
+ bool IsAutofillProfilesTrashEmpty();
+
+ // Checks if the guid is in the trash.
+ bool IsAutofillGUIDInTrash(const std::string& guid);
+
+ bool InitMainTable();
+ bool InitCreditCardsTable();
+ bool InitDatesTable();
+ bool InitProfilesTable();
+ bool InitProfileNamesTable();
+ bool InitProfileEmailsTable();
+ bool InitProfilePhonesTable();
+ bool InitProfileTrashTable();
+
+ // The application locale. The locale is needed for the migration to version
+ // 35. Since it must be read on the UI thread, it is set when the table is
+ // created (on the UI thread), and cached here so that it can be used for
+ // migrations (on the DB thread).
+ std::string app_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillTable);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_TABLE_H_
diff --git a/components/autofill/browser/webdata/autofill_table_unittest.cc b/components/autofill/browser/webdata/autofill_table_unittest.cc
new file mode 100644
index 0000000..deb2314
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_table_unittest.cc
@@ -0,0 +1,1328 @@
+// Copyright (c) 2012 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 <vector>
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/webdata/autofill_change.h"
+#include "components/autofill/browser/webdata/autofill_entry.h"
+#include "components/autofill/browser/webdata/autofill_table.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/encryptor/encryptor.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+
+// So we can compare AutofillKeys with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillKey& key) {
+ return os << UTF16ToASCII(key.name()) << ", " << UTF16ToASCII(key.value());
+}
+
+// So we can compare AutofillChanges with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillChange& change) {
+ switch (change.type()) {
+ case AutofillChange::ADD: {
+ os << "ADD";
+ break;
+ }
+ case AutofillChange::UPDATE: {
+ os << "UPDATE";
+ break;
+ }
+ case AutofillChange::REMOVE: {
+ os << "REMOVE";
+ break;
+ }
+ }
+ return os << " " << change.key();
+}
+
+namespace {
+
+bool CompareAutofillEntries(const AutofillEntry& a, const AutofillEntry& b) {
+ std::set<Time> timestamps1(a.timestamps().begin(), a.timestamps().end());
+ std::set<Time> timestamps2(b.timestamps().begin(), b.timestamps().end());
+
+ int compVal = a.key().name().compare(b.key().name());
+ if (compVal != 0) {
+ return compVal < 0;
+ }
+
+ compVal = a.key().value().compare(b.key().value());
+ if (compVal != 0) {
+ return compVal < 0;
+ }
+
+ if (timestamps1.size() != timestamps2.size()) {
+ return timestamps1.size() < timestamps2.size();
+ }
+
+ std::set<Time>::iterator it;
+ for (it = timestamps1.begin(); it != timestamps1.end(); it++) {
+ timestamps2.erase(*it);
+ }
+
+ return !timestamps2.empty();
+}
+
+} // anonymous namespace
+
+class AutofillTableTest : public testing::Test {
+ public:
+ AutofillTableTest() {}
+ virtual ~AutofillTableTest() {}
+
+ protected:
+ typedef std::set<AutofillEntry,
+ bool (*)(const AutofillEntry&, const AutofillEntry&)> AutofillEntrySet;
+ typedef std::set<AutofillEntry, bool (*)(const AutofillEntry&,
+ const AutofillEntry&)>::iterator AutofillEntrySetIterator;
+
+ virtual void SetUp() {
+#if defined(OS_MACOSX)
+ Encryptor::UseMockKeychain(true);
+#endif
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ file_ = temp_dir_.path().AppendASCII("TestWebDatabase");
+
+ table_.reset(new AutofillTable("en-US"));
+ db_.reset(new WebDatabase);
+ db_->AddTable(table_.get());
+ ASSERT_EQ(sql::INIT_OK, db_->Init(file_));
+ }
+
+ static AutofillEntry MakeAutofillEntry(const char* name,
+ const char* value,
+ time_t timestamp0,
+ time_t timestamp1) {
+ std::vector<Time> timestamps;
+ if (timestamp0 >= 0)
+ timestamps.push_back(Time::FromTimeT(timestamp0));
+ if (timestamp1 >= 0)
+ timestamps.push_back(Time::FromTimeT(timestamp1));
+ return AutofillEntry(
+ AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)), timestamps);
+ }
+
+ base::FilePath file_;
+ base::ScopedTempDir temp_dir_;
+ scoped_ptr<AutofillTable> table_;
+ scoped_ptr<WebDatabase> db_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillTableTest);
+};
+
+TEST_F(AutofillTableTest, Autofill) {
+ Time t1 = Time::Now();
+
+ // Simulate the submission of a handful of entries in a field called "Name",
+ // some more often than others.
+ AutofillChangeList changes;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ base::Time now = base::Time::Now();
+ base::TimeDelta two_seconds = base::TimeDelta::FromSeconds(2);
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ std::vector<string16> v;
+ for (int i = 0; i < 5; i++) {
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ now + i * two_seconds));
+ }
+ for (int i = 0; i < 3; i++) {
+ field.value = ASCIIToUTF16("Clark Sutter");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ now + i * two_seconds));
+ }
+ for (int i = 0; i < 2; i++) {
+ field.name = ASCIIToUTF16("Favorite Color");
+ field.value = ASCIIToUTF16("Green");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ now + i * two_seconds));
+ }
+
+ int count = 0;
+ int64 pair_id = 0;
+
+ // We have added the name Clark Kent 5 times, so count should be 5 and pair_id
+ // should be somthing non-zero.
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(5, count);
+ EXPECT_NE(0, pair_id);
+
+ // Storing in the data base should be case sensitive, so there should be no
+ // database entry for clark kent lowercase.
+ field.value = ASCIIToUTF16("clark kent");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(0, count);
+
+ field.name = ASCIIToUTF16("Favorite Color");
+ field.value = ASCIIToUTF16("Green");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(2, count);
+
+ // This is meant to get a list of suggestions for Name. The empty prefix
+ // in the second argument means it should return all suggestions for a name
+ // no matter what they start with. The order that the names occur in the list
+ // should be decreasing order by count.
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), string16(), &v, 6));
+ EXPECT_EQ(3U, v.size());
+ if (v.size() == 3) {
+ EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]);
+ EXPECT_EQ(ASCIIToUTF16("Clark Sutter"), v[1]);
+ EXPECT_EQ(ASCIIToUTF16("Superman"), v[2]);
+ }
+
+ // If we query again limiting the list size to 1, we should only get the most
+ // frequent entry.
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), string16(), &v, 1));
+ EXPECT_EQ(1U, v.size());
+ if (v.size() == 1) {
+ EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]);
+ }
+
+ // Querying for suggestions given a prefix is case-insensitive, so the prefix
+ // "cLa" shoud get suggestions for both Clarks.
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), ASCIIToUTF16("cLa"), &v, 6));
+ EXPECT_EQ(2U, v.size());
+ if (v.size() == 2) {
+ EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]);
+ EXPECT_EQ(ASCIIToUTF16("Clark Sutter"), v[1]);
+ }
+
+ // Removing all elements since the beginning of this function should remove
+ // everything from the database.
+ changes.clear();
+ EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, Time(), &changes));
+
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Clark Kent"))),
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Clark Sutter"))),
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Favorite Color"),
+ ASCIIToUTF16("Green"))),
+ };
+ EXPECT_EQ(arraysize(expected_changes), changes.size());
+ for (size_t i = 0; i < arraysize(expected_changes); i++) {
+ EXPECT_EQ(expected_changes[i], changes[i]);
+ }
+
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(0, count);
+
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), string16(), &v, 6));
+ EXPECT_EQ(0U, v.size());
+
+ // Now add some values with empty strings.
+ const string16 kValue = ASCIIToUTF16(" toto ");
+ field.name = ASCIIToUTF16("blank");
+ field.value = string16();
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ field.name = ASCIIToUTF16("blank");
+ field.value = ASCIIToUTF16(" ");
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ field.name = ASCIIToUTF16("blank");
+ field.value = ASCIIToUTF16(" ");
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ field.name = ASCIIToUTF16("blank");
+ field.value = kValue;
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+
+ // They should be stored normally as the DB layer does not check for empty
+ // values.
+ v.clear();
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("blank"), string16(), &v, 10));
+ EXPECT_EQ(4U, v.size());
+
+ // Now we'll check that ClearAutofillEmptyValueElements() works as expected.
+ table_->ClearAutofillEmptyValueElements();
+
+ v.clear();
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("blank"), string16(), &v, 10));
+ ASSERT_EQ(1U, v.size());
+
+ EXPECT_EQ(kValue, v[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_RemoveBetweenChanges) {
+ TimeDelta one_day(TimeDelta::FromDays(1));
+ Time t1 = Time::Now();
+ Time t2 = t1 + one_day;
+
+ AutofillChangeList changes;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1));
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t2));
+
+ changes.clear();
+ EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, t2, &changes));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::UPDATE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+ changes.clear();
+
+ EXPECT_TRUE(
+ table_->RemoveFormElementsAddedBetween(t2, t2 + one_day, &changes));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_AddChanges) {
+ TimeDelta one_day(TimeDelta::FromDays(1));
+ Time t1 = Time::Now();
+ Time t2 = t1 + one_day;
+
+ AutofillChangeList changes;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::ADD,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+
+ changes.clear();
+ EXPECT_TRUE(
+ table_->AddFormFieldValueTime(field, &changes, t2));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::UPDATE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateOneWithOneTimestamp) {
+ AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, -1));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ FormFieldData field;
+ field.name = ASCIIToUTF16("foo");
+ field.value = ASCIIToUTF16("bar");
+ int64 pair_id;
+ int count;
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(1, count);
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(1U, all_entries.size());
+ EXPECT_TRUE(entry == all_entries[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateOneWithTwoTimestamps) {
+ AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ FormFieldData field;
+ field.name = ASCIIToUTF16("foo");
+ field.value = ASCIIToUTF16("bar");
+ int64 pair_id;
+ int count;
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(2, count);
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(1U, all_entries.size());
+ EXPECT_TRUE(entry == all_entries[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAutofillTimestamps) {
+ AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ std::vector<Time> timestamps;
+ ASSERT_TRUE(table_->GetAutofillTimestamps(ASCIIToUTF16("foo"),
+ ASCIIToUTF16("bar"),
+ &timestamps));
+ ASSERT_EQ(2U, timestamps.size());
+ EXPECT_TRUE(Time::FromTimeT(1) == timestamps[0]);
+ EXPECT_TRUE(Time::FromTimeT(2) == timestamps[1]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateTwo) {
+ AutofillEntry entry0(MakeAutofillEntry("foo", "bar0", 1, -1));
+ AutofillEntry entry1(MakeAutofillEntry("foo", "bar1", 2, 3));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry0);
+ entries.push_back(entry1);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ FormFieldData field0;
+ field0.name = ASCIIToUTF16("foo");
+ field0.value = ASCIIToUTF16("bar0");
+ int64 pair_id;
+ int count;
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field0, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(1, count);
+
+ FormFieldData field1;
+ field1.name = ASCIIToUTF16("foo");
+ field1.value = ASCIIToUTF16("bar1");
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field1, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(2, count);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateReplace) {
+ AutofillChangeList changes;
+ // Add a form field. This will be replaced.
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+
+ AutofillEntry entry(MakeAutofillEntry("Name", "Superman", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(1U, all_entries.size());
+ EXPECT_TRUE(entry == all_entries[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateDontReplace) {
+ Time t = Time::Now();
+ AutofillEntry existing(
+ MakeAutofillEntry("Name", "Superman", t.ToTimeT(), -1));
+
+ AutofillChangeList changes;
+ // Add a form field. This will NOT be replaced.
+ FormFieldData field;
+ field.name = existing.key().name();
+ field.value = existing.key().value();
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t));
+ AutofillEntry entry(MakeAutofillEntry("Name", "Clark Kent", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(2U, all_entries.size());
+ AutofillEntrySet expected_entries(all_entries.begin(),
+ all_entries.end(),
+ CompareAutofillEntries);
+ EXPECT_EQ(1U, expected_entries.count(existing));
+ EXPECT_EQ(1U, expected_entries.count(entry));
+}
+
+TEST_F(AutofillTableTest, Autofill_AddFormFieldValues) {
+ Time t = Time::Now();
+
+ // Add multiple values for "firstname" and "lastname" names. Test that only
+ // first value of each gets added. Related to security issue:
+ // http://crbug.com/51727.
+ std::vector<FormFieldData> elements;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("firstname");
+ field.value = ASCIIToUTF16("Joe");
+ elements.push_back(field);
+
+ field.name = ASCIIToUTF16("firstname");
+ field.value = ASCIIToUTF16("Jane");
+ elements.push_back(field);
+
+ field.name = ASCIIToUTF16("lastname");
+ field.value = ASCIIToUTF16("Smith");
+ elements.push_back(field);
+
+ field.name = ASCIIToUTF16("lastname");
+ field.value = ASCIIToUTF16("Jones");
+ elements.push_back(field);
+
+ std::vector<AutofillChange> changes;
+ table_->AddFormFieldValuesTime(elements, &changes, t);
+
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(changes[0], AutofillChange(AutofillChange::ADD,
+ AutofillKey(ASCIIToUTF16("firstname"),
+ ASCIIToUTF16("Joe"))));
+ EXPECT_EQ(changes[1], AutofillChange(AutofillChange::ADD,
+ AutofillKey(ASCIIToUTF16("lastname"),
+ ASCIIToUTF16("Smith"))));
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(2U, all_entries.size());
+}
+
+TEST_F(AutofillTableTest, AutofillProfile) {
+ // Add a 'Home' profile.
+ AutofillProfile home_profile;
+ home_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ home_profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ home_profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ home_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz"));
+ home_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google"));
+ home_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way"));
+ home_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5"));
+ home_profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ home_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ home_profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ home_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567"));
+
+ Time pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddAutofillProfile(home_profile));
+ Time post_creation_time = Time::Now();
+
+ // Get the 'Home' profile.
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(home_profile.guid(), &db_profile));
+ EXPECT_EQ(home_profile, *db_profile);
+ sql::Statement s_home(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified "
+ "FROM autofill_profiles WHERE guid=?"));
+ s_home.BindString(0, home_profile.guid());
+ ASSERT_TRUE(s_home.is_valid());
+ ASSERT_TRUE(s_home.Step());
+ EXPECT_GE(s_home.ColumnInt64(0), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_home.ColumnInt64(0), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_home.Step());
+ delete db_profile;
+
+ // Add a 'Billing' profile.
+ AutofillProfile billing_profile = home_profile;
+ billing_profile.set_guid(base::GenerateGUID());
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE1,
+ ASCIIToUTF16("5678 Bottom Street"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("suite 3"));
+
+ pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddAutofillProfile(billing_profile));
+ post_creation_time = Time::Now();
+
+ // Get the 'Billing' profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+ EXPECT_EQ(billing_profile, *db_profile);
+ sql::Statement s_billing(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles WHERE guid=?"));
+ s_billing.BindString(0, billing_profile.guid());
+ ASSERT_TRUE(s_billing.is_valid());
+ ASSERT_TRUE(s_billing.Step());
+ EXPECT_GE(s_billing.ColumnInt64(0), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_billing.ColumnInt64(0), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_billing.Step());
+ delete db_profile;
+
+ // Update the 'Billing' profile, name only.
+ billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
+ Time pre_modification_time = Time::Now();
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(billing_profile));
+ Time post_modification_time = Time::Now();
+ ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+ EXPECT_EQ(billing_profile, *db_profile);
+ sql::Statement s_billing_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles WHERE guid=?"));
+ s_billing_updated.BindString(0, billing_profile.guid());
+ ASSERT_TRUE(s_billing_updated.is_valid());
+ ASSERT_TRUE(s_billing_updated.Step());
+ EXPECT_GE(s_billing_updated.ColumnInt64(0),
+ pre_modification_time.ToTimeT());
+ EXPECT_LE(s_billing_updated.ColumnInt64(0),
+ post_modification_time.ToTimeT());
+ EXPECT_FALSE(s_billing_updated.Step());
+ delete db_profile;
+
+ // Update the 'Billing' profile.
+ billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Janice"));
+ billing_profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("C."));
+ billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Joplin"));
+ billing_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("jane@singer.com"));
+ billing_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Indy"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("Open Road"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Route 66"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("NFA"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("NY"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("10011"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ billing_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("18181230000"));
+ Time pre_modification_time_2 = Time::Now();
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(billing_profile));
+ Time post_modification_time_2 = Time::Now();
+ ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+ EXPECT_EQ(billing_profile, *db_profile);
+ sql::Statement s_billing_updated_2(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles WHERE guid=?"));
+ s_billing_updated_2.BindString(0, billing_profile.guid());
+ ASSERT_TRUE(s_billing_updated_2.is_valid());
+ ASSERT_TRUE(s_billing_updated_2.Step());
+ EXPECT_GE(s_billing_updated_2.ColumnInt64(0),
+ pre_modification_time_2.ToTimeT());
+ EXPECT_LE(s_billing_updated_2.ColumnInt64(0),
+ post_modification_time_2.ToTimeT());
+ EXPECT_FALSE(s_billing_updated_2.Step());
+ delete db_profile;
+
+ // Remove the 'Billing' profile.
+ EXPECT_TRUE(table_->RemoveAutofillProfile(billing_profile.guid()));
+ EXPECT_FALSE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+}
+
+TEST_F(AutofillTableTest, AutofillProfileMultiValueNames) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("John Doe"));
+ const string16 kJohnPDoe(ASCIIToUTF16("John P. Doe"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Update the values.
+ const string16 kNoOne(ASCIIToUTF16("No One"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ EXPECT_EQ(string16(), db_profile->GetRawInfo(NAME_FULL));
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileSingleValue) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("John Doe"));
+ const string16 kJohnPDoe(ASCIIToUTF16("John P. Doe"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ const string16 kNoOne(ASCIIToUTF16("No One"));
+ set_values.resize(1);
+ set_values[0] = kNoOne;
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p.PrimaryValue(), db_profile->PrimaryValue());
+ EXPECT_EQ(p.guid(), db_profile->guid());
+ EXPECT_NE(0, p.Compare(*db_profile));
+ db_profile->GetRawMultiInfo(NAME_FULL, &set_values);
+ ASSERT_EQ(2UL, set_values.size());
+ EXPECT_EQ(kNoOne, set_values[0]);
+ EXPECT_EQ(kJohnPDoe, set_values[1]);
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileMultiValueEmails) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("john@doe.com"));
+ const string16 kJohnPDoe(ASCIIToUTF16("john_p@doe.com"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Update the values.
+ const string16 kNoOne(ASCIIToUTF16("no@one.com"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ EXPECT_EQ(string16(), db_profile->GetRawInfo(EMAIL_ADDRESS));
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileMultiValuePhone) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("4151112222"));
+ const string16 kJohnPDoe(ASCIIToUTF16("4151113333"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Update the values.
+ const string16 kNoOne(ASCIIToUTF16("4151110000"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ EXPECT_EQ(string16(), db_profile->GetRawInfo(EMAIL_ADDRESS));
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileTrash) {
+ std::vector<std::string> guids;
+ table_->GetAutofillProfilesInTrash(&guids);
+ EXPECT_TRUE(guids.empty());
+
+ ASSERT_TRUE(table_->AddAutofillGUIDToTrash(
+ "00000000-0000-0000-0000-000000000000"));
+ ASSERT_TRUE(table_->AddAutofillGUIDToTrash(
+ "00000000-0000-0000-0000-000000000001"));
+ ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
+ EXPECT_EQ(2UL, guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000000", guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000001", guids[1]);
+
+ ASSERT_TRUE(table_->EmptyAutofillProfilesTrash());
+ ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
+ EXPECT_TRUE(guids.empty());
+}
+
+TEST_F(AutofillTableTest, AutofillProfileTrashInteraction) {
+ std::vector<std::string> guids;
+ table_->GetAutofillProfilesInTrash(&guids);
+ EXPECT_TRUE(guids.empty());
+
+ AutofillProfile profile;
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ profile.SetRawInfo(EMAIL_ADDRESS,ASCIIToUTF16("js@smith.xyz"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 Main St"));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+
+ // Mark this profile as in the trash. This stops |AddAutofillProfile| from
+ // adding it.
+ EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid()));
+ EXPECT_TRUE(table_->AddAutofillProfile(profile));
+ AutofillProfile* added_profile = NULL;
+ EXPECT_FALSE(table_->GetAutofillProfile(profile.guid(), &added_profile));
+ EXPECT_EQ(static_cast<AutofillProfile*>(NULL), added_profile);
+
+ // Add the profile for real this time.
+ EXPECT_TRUE(table_->EmptyAutofillProfilesTrash());
+ EXPECT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
+ EXPECT_TRUE(guids.empty());
+ EXPECT_TRUE(table_->AddAutofillProfile(profile));
+ EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(),
+ &added_profile));
+ ASSERT_NE(static_cast<AutofillProfile*>(NULL), added_profile);
+ delete added_profile;
+
+ // Mark this profile as in the trash. This stops |UpdateAutofillProfileMulti|
+ // from updating it. In normal operation a profile should not be both in the
+ // trash and in the profiles table simultaneously.
+ EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid()));
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
+ EXPECT_TRUE(table_->UpdateAutofillProfileMulti(profile));
+ AutofillProfile* updated_profile = NULL;
+ EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), &updated_profile));
+ ASSERT_NE(static_cast<AutofillProfile*>(NULL), added_profile);
+ EXPECT_EQ(ASCIIToUTF16("John"), updated_profile->GetRawInfo(NAME_FIRST));
+ delete updated_profile;
+
+ // Try to delete the trashed profile. This stops |RemoveAutofillProfile| from
+ // deleting it. In normal operation deletion is done by migration step, and
+ // removal from trash is done by |WebDataService|. |RemoveAutofillProfile|
+ // does remove the item from the trash if it is found however, so that if
+ // other clients remove it (via Sync say) then it is gone and doesn't need to
+ // be processed further by |WebDataService|.
+ EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid()));
+ AutofillProfile* removed_profile = NULL;
+ EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), &removed_profile));
+ EXPECT_FALSE(table_->IsAutofillGUIDInTrash(profile.guid()));
+ ASSERT_NE(static_cast<AutofillProfile*>(NULL), removed_profile);
+ delete removed_profile;
+
+ // Check that emptying the trash now allows removal to occur.
+ EXPECT_TRUE(table_->EmptyAutofillProfilesTrash());
+ EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid()));
+ removed_profile = NULL;
+ EXPECT_FALSE(table_->GetAutofillProfile(profile.guid(), &removed_profile));
+ EXPECT_EQ(static_cast<AutofillProfile*>(NULL), removed_profile);
+}
+
+TEST_F(AutofillTableTest, CreditCard) {
+ // Add a 'Work' credit card.
+ CreditCard work_creditcard;
+ work_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ work_creditcard.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("1234567890123456"));
+ work_creditcard.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04"));
+ work_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ ASCIIToUTF16("2013"));
+
+ Time pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddCreditCard(work_creditcard));
+ Time post_creation_time = Time::Now();
+
+ // Get the 'Work' credit card.
+ CreditCard* db_creditcard;
+ ASSERT_TRUE(table_->GetCreditCard(work_creditcard.guid(), &db_creditcard));
+ EXPECT_EQ(work_creditcard, *db_creditcard);
+ sql::Statement s_work(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards WHERE guid=?"));
+ s_work.BindString(0, work_creditcard.guid());
+ ASSERT_TRUE(s_work.is_valid());
+ ASSERT_TRUE(s_work.Step());
+ EXPECT_GE(s_work.ColumnInt64(5), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_work.ColumnInt64(5), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_work.Step());
+ delete db_creditcard;
+
+ // Add a 'Target' credit card.
+ CreditCard target_creditcard;
+ target_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ target_creditcard.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("1111222233334444"));
+ target_creditcard.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("06"));
+ target_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ ASCIIToUTF16("2012"));
+
+ pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddCreditCard(target_creditcard));
+ post_creation_time = Time::Now();
+ ASSERT_TRUE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard));
+ EXPECT_EQ(target_creditcard, *db_creditcard);
+ sql::Statement s_target(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards WHERE guid=?"));
+ s_target.BindString(0, target_creditcard.guid());
+ ASSERT_TRUE(s_target.is_valid());
+ ASSERT_TRUE(s_target.Step());
+ EXPECT_GE(s_target.ColumnInt64(5), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_target.ColumnInt64(5), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_target.Step());
+ delete db_creditcard;
+
+ // Update the 'Target' credit card.
+ target_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Charles Grady"));
+ Time pre_modification_time = Time::Now();
+ EXPECT_TRUE(table_->UpdateCreditCard(target_creditcard));
+ Time post_modification_time = Time::Now();
+ ASSERT_TRUE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard));
+ EXPECT_EQ(target_creditcard, *db_creditcard);
+ sql::Statement s_target_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards WHERE guid=?"));
+ s_target_updated.BindString(0, target_creditcard.guid());
+ ASSERT_TRUE(s_target_updated.is_valid());
+ ASSERT_TRUE(s_target_updated.Step());
+ EXPECT_GE(s_target_updated.ColumnInt64(5), pre_modification_time.ToTimeT());
+ EXPECT_LE(s_target_updated.ColumnInt64(5), post_modification_time.ToTimeT());
+ EXPECT_FALSE(s_target_updated.Step());
+ delete db_creditcard;
+
+ // Remove the 'Target' credit card.
+ EXPECT_TRUE(table_->RemoveCreditCard(target_creditcard.guid()));
+ EXPECT_FALSE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard));
+}
+
+TEST_F(AutofillTableTest, UpdateAutofillProfile) {
+ // Add a profile to the db.
+ AutofillProfile profile;
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@example.com"));
+ profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5"));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567"));
+ table_->AddAutofillProfile(profile);
+
+ // Set a mocked value for the profile's creation time.
+ const time_t mock_creation_date = Time::Now().ToTimeT() - 13;
+ sql::Statement s_mock_creation_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_creation_date.is_valid());
+ s_mock_creation_date.BindInt64(0, mock_creation_date);
+ ASSERT_TRUE(s_mock_creation_date.Run());
+
+ // Get the profile.
+ AutofillProfile* tmp_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ scoped_ptr<AutofillProfile> db_profile(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_original.is_valid());
+ ASSERT_TRUE(s_original.Step());
+ EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0));
+ EXPECT_FALSE(s_original.Step());
+
+ // Now, update the profile and save the update to the database.
+ // The modification date should change to reflect the update.
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz"));
+ table_->UpdateAutofillProfileMulti(profile);
+
+ // Get the profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ db_profile.reset(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_updated.is_valid());
+ ASSERT_TRUE(s_updated.Step());
+ EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0));
+ EXPECT_FALSE(s_updated.Step());
+
+ // Set a mocked value for the profile's modification time.
+ const time_t mock_modification_date = Time::Now().ToTimeT() - 7;
+ sql::Statement s_mock_modification_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_modification_date.is_valid());
+ s_mock_modification_date.BindInt64(0, mock_modification_date);
+ ASSERT_TRUE(s_mock_modification_date.Run());
+
+ // Finally, call into |UpdateAutofillProfileMulti()| without changing the
+ // profile. The modification date should not change.
+ table_->UpdateAutofillProfileMulti(profile);
+
+ // Get the profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ db_profile.reset(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_unchanged(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_unchanged.is_valid());
+ ASSERT_TRUE(s_unchanged.Step());
+ EXPECT_EQ(mock_modification_date, s_unchanged.ColumnInt64(0));
+ EXPECT_FALSE(s_unchanged.Step());
+}
+
+TEST_F(AutofillTableTest, UpdateCreditCard) {
+ // Add a credit card to the db.
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1234567890123456"));
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04"));
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2013"));
+ table_->AddCreditCard(credit_card);
+
+ // Set a mocked value for the credit card's creation time.
+ const time_t mock_creation_date = Time::Now().ToTimeT() - 13;
+ sql::Statement s_mock_creation_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_creation_date.is_valid());
+ s_mock_creation_date.BindInt64(0, mock_creation_date);
+ ASSERT_TRUE(s_mock_creation_date.Run());
+
+ // Get the credit card.
+ CreditCard* tmp_credit_card;
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ scoped_ptr<CreditCard> db_credit_card(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_original.is_valid());
+ ASSERT_TRUE(s_original.Step());
+ EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0));
+ EXPECT_FALSE(s_original.Step());
+
+ // Now, update the credit card and save the update to the database.
+ // The modification date should change to reflect the update.
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ table_->UpdateCreditCard(credit_card);
+
+ // Get the credit card.
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ db_credit_card.reset(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_updated.is_valid());
+ ASSERT_TRUE(s_updated.Step());
+ EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0));
+ EXPECT_FALSE(s_updated.Step());
+
+ // Set a mocked value for the credit card's modification time.
+ const time_t mock_modification_date = Time::Now().ToTimeT() - 7;
+ sql::Statement s_mock_modification_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_modification_date.is_valid());
+ s_mock_modification_date.BindInt64(0, mock_modification_date);
+ ASSERT_TRUE(s_mock_modification_date.Run());
+
+ // Finally, call into |UpdateCreditCard()| without changing the credit card.
+ // The modification date should not change.
+ table_->UpdateCreditCard(credit_card);
+
+ // Get the profile.
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ db_credit_card.reset(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_unchanged(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_unchanged.is_valid());
+ ASSERT_TRUE(s_unchanged.Step());
+ EXPECT_EQ(mock_modification_date, s_unchanged.ColumnInt64(0));
+ EXPECT_FALSE(s_unchanged.Step());
+}
+
+TEST_F(AutofillTableTest, RemoveAutofillDataModifiedBetween) {
+ // Populate the autofill_profiles and credit_cards tables.
+ ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000000', 11);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000001', 21);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000002', 31);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000003', 41);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000004', 51);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000005', 61);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000006', 17);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000007', 27);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000008', 37);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000009', 47);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000010', 57);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000011', 67);"));
+
+ // Remove all entries modified in the bounded time range [17,41).
+ std::vector<std::string> profile_guids;
+ std::vector<std::string> credit_card_guids;
+ table_->RemoveAutofillDataModifiedBetween(
+ Time::FromTimeT(17), Time::FromTimeT(41),
+ &profile_guids, &credit_card_guids);
+ ASSERT_EQ(2UL, profile_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000001", profile_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000002", profile_guids[1]);
+ sql::Statement s_autofill_profiles_bounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_bounded.is_valid());
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0));
+ EXPECT_FALSE(s_autofill_profiles_bounded.Step());
+ ASSERT_EQ(3UL, credit_card_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000006", credit_card_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000007", credit_card_guids[1]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000008", credit_card_guids[2]);
+ sql::Statement s_credit_cards_bounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_bounded.is_valid());
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(47, s_credit_cards_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(57, s_credit_cards_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(67, s_credit_cards_bounded.ColumnInt64(0));
+ EXPECT_FALSE(s_credit_cards_bounded.Step());
+
+ // Remove all entries modified on or after time 51 (unbounded range).
+ table_->RemoveAutofillDataModifiedBetween(
+ Time::FromTimeT(51), Time(),
+ &profile_guids, &credit_card_guids);
+ ASSERT_EQ(2UL, profile_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000004", profile_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000005", profile_guids[1]);
+ sql::Statement s_autofill_profiles_unbounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_unbounded.is_valid());
+ ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
+ EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
+ EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0));
+ EXPECT_FALSE(s_autofill_profiles_unbounded.Step());
+ ASSERT_EQ(2UL, credit_card_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000010", credit_card_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000011", credit_card_guids[1]);
+ sql::Statement s_credit_cards_unbounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_unbounded.is_valid());
+ ASSERT_TRUE(s_credit_cards_unbounded.Step());
+ EXPECT_EQ(47, s_credit_cards_unbounded.ColumnInt64(0));
+ EXPECT_FALSE(s_credit_cards_unbounded.Step());
+
+ // Remove all remaining entries.
+ table_->RemoveAutofillDataModifiedBetween(
+ Time(), Time(),
+ &profile_guids, &credit_card_guids);
+ ASSERT_EQ(2UL, profile_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000000", profile_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000003", profile_guids[1]);
+ sql::Statement s_autofill_profiles_empty(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_empty.is_valid());
+ EXPECT_FALSE(s_autofill_profiles_empty.Step());
+ ASSERT_EQ(1UL, credit_card_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000009", credit_card_guids[0]);
+ sql::Statement s_credit_cards_empty(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_empty.is_valid());
+ EXPECT_FALSE(s_credit_cards_empty.Step());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_NoResults) {
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+
+ EXPECT_EQ(0U, entries.size());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_OneResult) {
+ AutofillChangeList changes;
+ std::map<std::string, std::vector<Time> > name_value_times_map;
+
+ time_t start = 0;
+ std::vector<Time> timestamps1;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps1.push_back(Time::FromTimeT(start));
+ std::string key1("NameSuperman");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key1, timestamps1));
+
+ AutofillEntrySet expected_entries(CompareAutofillEntries);
+ AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"));
+ AutofillEntry ae1(ak1, timestamps1);
+
+ expected_entries.insert(ae1);
+
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+ AutofillEntrySet entry_set(entries.begin(), entries.end(),
+ CompareAutofillEntries);
+
+ // make sure the lists of entries match
+ ASSERT_EQ(expected_entries.size(), entry_set.size());
+ AutofillEntrySetIterator it;
+ for (it = entry_set.begin(); it != entry_set.end(); it++) {
+ expected_entries.erase(*it);
+ }
+
+ EXPECT_EQ(0U, expected_entries.size());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoDistinct) {
+ AutofillChangeList changes;
+ std::map<std::string, std::vector<Time> > name_value_times_map;
+ time_t start = 0;
+
+ std::vector<Time> timestamps1;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps1.push_back(Time::FromTimeT(start));
+ std::string key1("NameSuperman");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key1, timestamps1));
+
+ start++;
+ std::vector<Time> timestamps2;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps2.push_back(Time::FromTimeT(start));
+ std::string key2("NameClark Kent");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key2, timestamps2));
+
+ AutofillEntrySet expected_entries(CompareAutofillEntries);
+ AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"));
+ AutofillKey ak2(ASCIIToUTF16("Name"), ASCIIToUTF16("Clark Kent"));
+ AutofillEntry ae1(ak1, timestamps1);
+ AutofillEntry ae2(ak2, timestamps2);
+
+ expected_entries.insert(ae1);
+ expected_entries.insert(ae2);
+
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+ AutofillEntrySet entry_set(entries.begin(), entries.end(),
+ CompareAutofillEntries);
+
+ // make sure the lists of entries match
+ ASSERT_EQ(expected_entries.size(), entry_set.size());
+ AutofillEntrySetIterator it;
+ for (it = entry_set.begin(); it != entry_set.end(); it++) {
+ expected_entries.erase(*it);
+ }
+
+ EXPECT_EQ(0U, expected_entries.size());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoSame) {
+ AutofillChangeList changes;
+ std::map<std::string, std::vector<Time> > name_value_times_map;
+
+ time_t start = 0;
+ std::vector<Time> timestamps;
+ for (int i = 0; i < 2; i++) {
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps.push_back(Time::FromTimeT(start));
+ start++;
+ }
+
+ std::string key("NameSuperman");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key, timestamps));
+
+ AutofillEntrySet expected_entries(CompareAutofillEntries);
+ AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"));
+ AutofillEntry ae1(ak1, timestamps);
+
+ expected_entries.insert(ae1);
+
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+ AutofillEntrySet entry_set(entries.begin(), entries.end(),
+ CompareAutofillEntries);
+
+ // make sure the lists of entries match
+ ASSERT_EQ(expected_entries.size(), entry_set.size());
+ AutofillEntrySetIterator it;
+ for (it = entry_set.begin(); it != entry_set.end(); it++) {
+ expected_entries.erase(*it);
+ }
+
+ EXPECT_EQ(0U, expected_entries.size());
+}
diff --git a/components/autofill/browser/webdata/autofill_webdata.h b/components/autofill/browser/webdata/autofill_webdata.h
new file mode 100644
index 0000000..58f683b
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_webdata.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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 COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "components/webdata/common/web_data_service_base.h"
+
+class AutofillProfile;
+class CreditCard;
+class Profile;
+class WebDataServiceConsumer;
+struct FormFieldData;
+
+// Pure virtual interface for retrieving Autofill data. API users
+// should use AutofillWebDataService.
+class AutofillWebData {
+ public:
+ virtual ~AutofillWebData() {}
+
+ // Schedules a task to add form fields to the web database.
+ virtual void AddFormFields(
+ const std::vector<FormFieldData>& fields) = 0;
+
+ // Initiates the request for a vector of values which have been entered in
+ // form input fields named |name|. The method OnWebDataServiceRequestDone of
+ // |consumer| gets called back when the request is finished, with the vector
+ // included in the argument |result|.
+ virtual WebDataServiceBase::Handle GetFormValuesForElementName(
+ const string16& name,
+ const string16& prefix,
+ int limit,
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Removes form elements recorded for Autocomplete from the database.
+ virtual void RemoveFormElementsAddedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) = 0;
+
+ virtual void RemoveExpiredFormElements() = 0;
+ virtual void RemoveFormValueForElementName(const string16& name,
+ const string16& value) = 0;
+
+ // Schedules a task to add an Autofill profile to the web database.
+ virtual void AddAutofillProfile(const AutofillProfile& profile) = 0;
+
+ // Schedules a task to update an Autofill profile in the web database.
+ virtual void UpdateAutofillProfile(const AutofillProfile& profile) = 0;
+
+ // Schedules a task to remove an Autofill profile from the web database.
+ // |guid| is the identifer of the profile to remove.
+ virtual void RemoveAutofillProfile(const std::string& guid) = 0;
+
+ // Initiates the request for all Autofill profiles. The method
+ // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+ // finished, with the profiles included in the argument |result|. The
+ // consumer owns the profiles.
+ virtual WebDataServiceBase::Handle GetAutofillProfiles(
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Schedules a task to add credit card to the web database.
+ virtual void AddCreditCard(const CreditCard& credit_card) = 0;
+
+ // Schedules a task to update credit card in the web database.
+ virtual void UpdateCreditCard(const CreditCard& credit_card) = 0;
+
+ // Schedules a task to remove a credit card from the web database.
+ // |guid| is identifer of the credit card to remove.
+ virtual void RemoveCreditCard(const std::string& guid) = 0;
+
+ // Initiates the request for all credit cards. The method
+ // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+ // finished, with the credit cards included in the argument |result|. The
+ // consumer owns the credit cards.
+ virtual WebDataServiceBase::Handle GetCreditCards(
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Removes Autofill records from the database.
+ virtual void RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) = 0;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
diff --git a/components/autofill/browser/webdata/autofill_webdata_service.cc b/components/autofill/browser/webdata/autofill_webdata_service.cc
new file mode 100644
index 0000000..2ec7f4d
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_webdata_service.cc
@@ -0,0 +1,458 @@
+// Copyright (c) 2012 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 "components/autofill/browser/webdata/autofill_webdata_service.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/webdata/autofill_change.h"
+#include "components/autofill/browser/webdata/autofill_entry.h"
+#include "components/autofill/browser/webdata/autofill_table.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/webdata/common/web_database_service.h"
+
+using base::Bind;
+using base::Time;
+using content::BrowserThread;
+
+// static
+void AutofillWebDataService::NotifyOfMultipleAutofillChanges(
+ AutofillWebDataService* web_data_service) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (!web_data_service)
+ return;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ Bind(&AutofillWebDataService::NotifyAutofillMultipleChangedOnUIThread,
+ make_scoped_refptr(web_data_service)));
+}
+
+AutofillWebDataService::AutofillWebDataService(
+ scoped_refptr<WebDatabaseService> wdbs,
+ const ProfileErrorCallback& callback)
+ : WebDataServiceBase(wdbs, callback) {
+}
+
+AutofillWebDataService::AutofillWebDataService()
+ : WebDataServiceBase(NULL,
+ WebDataServiceBase::ProfileErrorCallback()) {
+}
+
+void AutofillWebDataService::AddFormFields(
+ const std::vector<FormFieldData>& fields) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::AddFormElementsImpl, this, fields));
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::GetFormValuesForElementName(
+ const string16& name, const string16& prefix, int limit,
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataService::GetFormValuesForElementNameImpl,
+ this, name, prefix, limit), consumer);
+}
+
+void AutofillWebDataService::RemoveFormElementsAddedBetween(
+ const Time& delete_begin, const Time& delete_end) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::RemoveFormElementsAddedBetweenImpl,
+ this, delete_begin, delete_end));
+}
+
+void AutofillWebDataService::RemoveExpiredFormElements() {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::RemoveExpiredFormElementsImpl, this));
+}
+
+void AutofillWebDataService::RemoveFormValueForElementName(
+ const string16& name, const string16& value) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::RemoveFormValueForElementNameImpl,
+ this, name, value));
+}
+
+void AutofillWebDataService::AddAutofillProfile(
+ const AutofillProfile& profile) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::AddAutofillProfileImpl, this, profile));
+}
+
+void AutofillWebDataService::UpdateAutofillProfile(
+ const AutofillProfile& profile) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::UpdateAutofillProfileImpl,
+ this, profile));
+}
+
+void AutofillWebDataService::RemoveAutofillProfile(
+ const std::string& guid) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataService::RemoveAutofillProfileImpl, this, guid));
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::GetAutofillProfiles(
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataService::GetAutofillProfilesImpl, this),
+ consumer);
+}
+
+void AutofillWebDataService::AddCreditCard(const CreditCard& credit_card) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataService::AddCreditCardImpl, this, credit_card));
+}
+
+void AutofillWebDataService::UpdateCreditCard(
+ const CreditCard& credit_card) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataService::UpdateCreditCardImpl, this, credit_card));
+}
+
+void AutofillWebDataService::RemoveCreditCard(const std::string& guid) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataService::RemoveCreditCardImpl, this, guid));
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::GetCreditCards(
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataService::GetCreditCardsImpl, this), consumer);
+}
+
+void AutofillWebDataService::RemoveAutofillDataModifiedBetween(
+ const Time& delete_begin,
+ const Time& delete_end) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataService::RemoveAutofillDataModifiedBetweenImpl,
+ this, delete_begin, delete_end));
+}
+
+void AutofillWebDataService::AddObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ db_observer_list_.AddObserver(observer);
+}
+
+void AutofillWebDataService::RemoveObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ db_observer_list_.RemoveObserver(observer);
+}
+
+void AutofillWebDataService::AddObserver(
+ AutofillWebDataServiceObserverOnUIThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ui_observer_list_.AddObserver(observer);
+}
+
+void AutofillWebDataService::RemoveObserver(
+ AutofillWebDataServiceObserverOnUIThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ui_observer_list_.RemoveObserver(observer);
+}
+
+AutofillWebDataService::~AutofillWebDataService() {}
+
+void AutofillWebDataService::NotifyDatabaseLoadedOnUIThread() {
+ // Notify that the database has been initialized.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnUIThread,
+ ui_observer_list_,
+ WebDatabaseLoaded());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Autofill implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+WebDatabase::State AutofillWebDataService::AddFormElementsImpl(
+ const std::vector<FormFieldData>& fields, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillChangeList changes;
+ if (!AutofillTable::FromWebDatabase(db)->AddFormFieldValues(
+ fields, &changes)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ // Post the notifications including the list of affected keys.
+ // This is sent here so that work resulting from this notification will be
+ // done on the DB thread, and not the UI thread.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult>
+AutofillWebDataService::GetFormValuesForElementNameImpl(
+ const string16& name, const string16& prefix, int limit, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<string16> values;
+ AutofillTable::FromWebDatabase(db)->GetFormValuesForElementName(
+ name, prefix, &values, limit);
+ return scoped_ptr<WDTypedResult>(
+ new WDResult<std::vector<string16> >(AUTOFILL_VALUE_RESULT, values));
+}
+
+WebDatabase::State AutofillWebDataService::RemoveFormElementsAddedBetweenImpl(
+ const base::Time& delete_begin, const base::Time& delete_end,
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillChangeList changes;
+
+ if (AutofillTable::FromWebDatabase(db)->RemoveFormElementsAddedBetween(
+ delete_begin, delete_end, &changes)) {
+ if (!changes.empty()) {
+ // Post the notifications including the list of affected keys.
+ // This is sent here so that work resulting from this notification
+ // will be done on the DB thread, and not the UI thread.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+ }
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::RemoveExpiredFormElementsImpl(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillChangeList changes;
+
+ if (AutofillTable::FromWebDatabase(db)->RemoveExpiredFormElements(&changes)) {
+ if (!changes.empty()) {
+ // Post the notifications including the list of affected keys.
+ // This is sent here so that work resulting from this notification
+ // will be done on the DB thread, and not the UI thread.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+ }
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::RemoveFormValueForElementNameImpl(
+ const string16& name, const string16& value, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (AutofillTable::FromWebDatabase(db)->RemoveFormElement(name, value)) {
+ AutofillChangeList changes;
+ changes.push_back(
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name, value)));
+
+ // Post the notifications including the list of affected keys.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::AddAutofillProfileImpl(
+ const AutofillProfile& profile, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!AutofillTable::FromWebDatabase(db)->AddAutofillProfile(profile)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ // Send GUID-based notification.
+ AutofillProfileChange change(
+ AutofillProfileChange::ADD, profile.guid(), &profile);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::UpdateAutofillProfileImpl(
+ const AutofillProfile& profile, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // Only perform the update if the profile exists. It is currently
+ // valid to try to update a missing profile. We simply drop the write and
+ // the caller will detect this on the next refresh.
+ AutofillProfile* original_profile = NULL;
+ if (!AutofillTable::FromWebDatabase(db)->GetAutofillProfile(profile.guid(),
+ &original_profile)) {
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ scoped_ptr<AutofillProfile> scoped_profile(original_profile);
+
+ if (!AutofillTable::FromWebDatabase(db)->UpdateAutofillProfileMulti(
+ profile)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NEEDED;
+ }
+
+ // Send GUID-based notification.
+ AutofillProfileChange change(
+ AutofillProfileChange::UPDATE, profile.guid(), &profile);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::RemoveAutofillProfileImpl(
+ const std::string& guid, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillProfile* profile = NULL;
+ if (!AutofillTable::FromWebDatabase(db)->GetAutofillProfile(guid, &profile)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ scoped_ptr<AutofillProfile> scoped_profile(profile);
+
+ if (!AutofillTable::FromWebDatabase(db)->RemoveAutofillProfile(guid)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ // Send GUID-based notification.
+ AutofillProfileChange change(AutofillProfileChange::REMOVE, guid, NULL);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult> AutofillWebDataService::GetAutofillProfilesImpl(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<AutofillProfile*> profiles;
+ AutofillTable::FromWebDatabase(db)->GetAutofillProfiles(&profiles);
+ return scoped_ptr<WDTypedResult>(
+ new WDDestroyableResult<std::vector<AutofillProfile*> >(
+ AUTOFILL_PROFILES_RESULT,
+ profiles,
+ base::Bind(&AutofillWebDataService::DestroyAutofillProfileResult,
+ base::Unretained(this))));
+}
+
+WebDatabase::State AutofillWebDataService::AddCreditCardImpl(
+ const CreditCard& credit_card, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!AutofillTable::FromWebDatabase(db)->AddCreditCard(credit_card)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::UpdateCreditCardImpl(
+ const CreditCard& credit_card, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // It is currently valid to try to update a missing profile. We simply drop
+ // the write and the caller will detect this on the next refresh.
+ CreditCard* original_credit_card = NULL;
+ if (!AutofillTable::FromWebDatabase(db)->GetCreditCard(credit_card.guid(),
+ &original_credit_card)) {
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ scoped_ptr<CreditCard> scoped_credit_card(original_credit_card);
+
+ if (!AutofillTable::FromWebDatabase(db)->UpdateCreditCard(credit_card)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataService::RemoveCreditCardImpl(
+ const std::string& guid, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!AutofillTable::FromWebDatabase(db)->RemoveCreditCard(guid)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult> AutofillWebDataService::GetCreditCardsImpl(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<CreditCard*> credit_cards;
+ AutofillTable::FromWebDatabase(db)->GetCreditCards(&credit_cards);
+ return scoped_ptr<WDTypedResult>(
+ new WDDestroyableResult<std::vector<CreditCard*> >(
+ AUTOFILL_CREDITCARDS_RESULT,
+ credit_cards,
+ base::Bind(&AutofillWebDataService::DestroyAutofillCreditCardResult,
+ base::Unretained(this))));
+}
+
+WebDatabase::State
+ AutofillWebDataService::RemoveAutofillDataModifiedBetweenImpl(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<std::string> profile_guids;
+ std::vector<std::string> credit_card_guids;
+ if (AutofillTable::FromWebDatabase(db)->RemoveAutofillDataModifiedBetween(
+ delete_begin,
+ delete_end,
+ &profile_guids,
+ &credit_card_guids)) {
+ for (std::vector<std::string>::iterator iter = profile_guids.begin();
+ iter != profile_guids.end(); ++iter) {
+ AutofillProfileChange change(AutofillProfileChange::REMOVE, *iter, NULL);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+ }
+ // Note: It is the caller's responsibility to post notifications for any
+ // changes, e.g. by calling the Refresh() method of PersonalDataManager.
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+void AutofillWebDataService::DestroyAutofillProfileResult(
+ const WDTypedResult* result) {
+ DCHECK(result->GetType() == AUTOFILL_PROFILES_RESULT);
+ const WDResult<std::vector<AutofillProfile*> >* r =
+ static_cast<const WDResult<std::vector<AutofillProfile*> >*>(result);
+ std::vector<AutofillProfile*> profiles = r->GetValue();
+ STLDeleteElements(&profiles);
+}
+
+void AutofillWebDataService::DestroyAutofillCreditCardResult(
+ const WDTypedResult* result) {
+ DCHECK(result->GetType() == AUTOFILL_CREDITCARDS_RESULT);
+ const WDResult<std::vector<CreditCard*> >* r =
+ static_cast<const WDResult<std::vector<CreditCard*> >*>(result);
+
+ std::vector<CreditCard*> credit_cards = r->GetValue();
+ STLDeleteElements(&credit_cards);
+}
+
+void AutofillWebDataService::NotifyAutofillMultipleChangedOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnUIThread,
+ ui_observer_list_,
+ AutofillMultipleChanged());
+}
diff --git a/components/autofill/browser/webdata/autofill_webdata_service.h b/components/autofill/browser/webdata/autofill_webdata_service.h
new file mode 100644
index 0000000..f1573fd
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_webdata_service.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 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 COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "components/autofill/browser/webdata/autofill_webdata.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+#include "components/webdata/common/web_database.h"
+
+class AutofillChange;
+class AutofillProfile;
+class AutofillWebDataServiceObserverOnDBThread;
+class AutofillWebDataServiceObserverOnUIThread;
+class CreditCard;
+class WebDatabaseService;
+
+namespace content {
+class BrowserContext;
+}
+
+// API for Autofill web data.
+class AutofillWebDataService : public AutofillWebData,
+ public WebDataServiceBase {
+ public:
+ AutofillWebDataService();
+
+ AutofillWebDataService(scoped_refptr<WebDatabaseService> wdbs,
+ const ProfileErrorCallback& callback);
+
+ // Retrieve an AutofillWebDataService for the given context.
+ // Can return NULL in some contexts.
+ static scoped_refptr<AutofillWebDataService> FromBrowserContext(
+ content::BrowserContext* context);
+
+ // Notifies listeners on the UI thread that multiple changes have been made to
+ // to Autofill records of the database.
+ // NOTE: This method is intended to be called from the DB thread. It
+ // it asynchronously notifies listeners on the UI thread.
+ // |web_data_service| may be NULL for testing purposes.
+ static void NotifyOfMultipleAutofillChanges(
+ AutofillWebDataService* web_data_service);
+
+ // AutofillWebData implementation.
+ virtual void AddFormFields(
+ const std::vector<FormFieldData>& fields) OVERRIDE;
+ virtual WebDataServiceBase::Handle GetFormValuesForElementName(
+ const string16& name,
+ const string16& prefix,
+ int limit,
+ WebDataServiceConsumer* consumer) OVERRIDE;
+ virtual void RemoveFormElementsAddedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) OVERRIDE;
+ virtual void RemoveExpiredFormElements() OVERRIDE;
+ virtual void RemoveFormValueForElementName(const string16& name,
+ const string16& value) OVERRIDE;
+ virtual void AddAutofillProfile(const AutofillProfile& profile) OVERRIDE;
+ virtual void UpdateAutofillProfile(const AutofillProfile& profile) OVERRIDE;
+ virtual void RemoveAutofillProfile(const std::string& guid) OVERRIDE;
+ virtual WebDataServiceBase::Handle GetAutofillProfiles(
+ WebDataServiceConsumer* consumer) OVERRIDE;
+ virtual void AddCreditCard(const CreditCard& credit_card) OVERRIDE;
+ virtual void UpdateCreditCard(const CreditCard& credit_card) OVERRIDE;
+ virtual void RemoveCreditCard(const std::string& guid) OVERRIDE;
+ virtual WebDataServiceBase::Handle GetCreditCards(
+ WebDataServiceConsumer* consumer) OVERRIDE;
+ virtual void RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) OVERRIDE;
+
+ void AddObserver(AutofillWebDataServiceObserverOnDBThread* observer);
+ void RemoveObserver(AutofillWebDataServiceObserverOnDBThread* observer);
+
+ void AddObserver(AutofillWebDataServiceObserverOnUIThread* observer);
+ void RemoveObserver(AutofillWebDataServiceObserverOnUIThread* observer);
+
+ protected:
+ virtual ~AutofillWebDataService();
+
+ // WebDataServiceBase overrides:
+ virtual void NotifyDatabaseLoadedOnUIThread() OVERRIDE;
+
+ private:
+ WebDatabase::State AddFormElementsImpl(
+ const std::vector<FormFieldData>& fields, WebDatabase* db);
+ scoped_ptr<WDTypedResult> GetFormValuesForElementNameImpl(
+ const string16& name, const string16& prefix, int limit, WebDatabase* db);
+ WebDatabase::State RemoveFormElementsAddedBetweenImpl(
+ const base::Time& delete_begin, const base::Time& delete_end,
+ WebDatabase* db);
+ WebDatabase::State RemoveExpiredFormElementsImpl(WebDatabase* db);
+ WebDatabase::State RemoveFormValueForElementNameImpl(
+ const string16& name, const string16& value, WebDatabase* db);
+ WebDatabase::State AddAutofillProfileImpl(
+ const AutofillProfile& profile, WebDatabase* db);
+ WebDatabase::State UpdateAutofillProfileImpl(
+ const AutofillProfile& profile, WebDatabase* db);
+ WebDatabase::State RemoveAutofillProfileImpl(
+ const std::string& guid, WebDatabase* db);
+ scoped_ptr<WDTypedResult> GetAutofillProfilesImpl(WebDatabase* db);
+ WebDatabase::State AddCreditCardImpl(
+ const CreditCard& credit_card, WebDatabase* db);
+ WebDatabase::State UpdateCreditCardImpl(
+ const CreditCard& credit_card, WebDatabase* db);
+ WebDatabase::State RemoveCreditCardImpl(
+ const std::string& guid, WebDatabase* db);
+ scoped_ptr<WDTypedResult> GetCreditCardsImpl(WebDatabase* db);
+ WebDatabase::State RemoveAutofillDataModifiedBetweenImpl(
+ const base::Time& delete_begin, const base::Time& delete_end,
+ WebDatabase* db);
+
+ // Callbacks to ensure that sensitive info is destroyed if request is
+ // cancelled.
+ void DestroyAutofillProfileResult(const WDTypedResult* result);
+ void DestroyAutofillCreditCardResult(const WDTypedResult* result);
+
+ void NotifyAutofillMultipleChangedOnUIThread();
+
+ ObserverList<AutofillWebDataServiceObserverOnDBThread> db_observer_list_;
+ ObserverList<AutofillWebDataServiceObserverOnUIThread> ui_observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillWebDataService);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_H_
diff --git a/components/autofill/browser/webdata/autofill_webdata_service_observer.h b/components/autofill/browser/webdata/autofill_webdata_service_observer.h
new file mode 100644
index 0000000..f6a6e43
--- /dev/null
+++ b/components/autofill/browser/webdata/autofill_webdata_service_observer.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 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 COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_OBSERVER_H_
+
+#include "components/autofill/browser/webdata/autofill_change.h"
+
+class AutofillWebDataServiceObserverOnDBThread {
+ public:
+ // Called on DB thread whenever Autofill entries are changed.
+ virtual void AutofillEntriesChanged(const AutofillChangeList& changes) {}
+
+ // Called on DB thread when an AutofillProfile has been added/removed/updated
+ // in the WebDatabase.
+ virtual void AutofillProfileChanged(const AutofillProfileChange& change) {}
+
+ protected:
+ virtual ~AutofillWebDataServiceObserverOnDBThread() {}
+};
+
+class AutofillWebDataServiceObserverOnUIThread {
+ public:
+ // Called on UI thread whenever the web database service has finished loading
+ // the web database.
+ virtual void WebDatabaseLoaded() {}
+
+ // Called on UI thread when multiple Autofill entries have been modified by
+ // Sync.
+ virtual void AutofillMultipleChanged() {}
+
+ protected:
+ virtual ~AutofillWebDataServiceObserverOnUIThread() {}
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_OBSERVER_H_
diff --git a/components/autofill/browser/webdata/web_data_service_unittest.cc b/components/autofill/browser/webdata/web_data_service_unittest.cc
new file mode 100644
index 0000000..a933925
--- /dev/null
+++ b/components/autofill/browser/webdata/web_data_service_unittest.cc
@@ -0,0 +1,532 @@
+// Copyright (c) 2012 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 <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop.h"
+#include "base/stl_util.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/webdata/autofill_change.h"
+#include "components/autofill/browser/webdata/autofill_entry.h"
+#include "components/autofill/browser/webdata/autofill_table.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/webdata/common/web_data_service_test_util.h"
+#include "components/webdata/common/web_database_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+using base::WaitableEvent;
+using content::BrowserThread;
+using testing::_;
+using testing::DoDefault;
+using testing::ElementsAreArray;
+using testing::Pointee;
+using testing::Property;
+
+static const int kWebDataServiceTimeoutSeconds = 8;
+
+ACTION_P(SignalEvent, event) {
+ event->Signal();
+}
+
+class MockAutofillWebDataServiceObserver
+ : public AutofillWebDataServiceObserverOnDBThread {
+ public:
+ MOCK_METHOD1(AutofillEntriesChanged,
+ void(const AutofillChangeList& changes));
+ MOCK_METHOD1(AutofillProfileChanged,
+ void(const AutofillProfileChange& change));
+};
+
+class WebDataServiceTest : public testing::Test {
+ public:
+ WebDataServiceTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB) {}
+
+ protected:
+ virtual void SetUp() {
+ db_thread_.Start();
+
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ base::FilePath path = temp_dir_.path().AppendASCII("TestWebDB");
+
+ wdbs_ = new WebDatabaseService(path);
+ wdbs_->AddTable(scoped_ptr<WebDatabaseTable>(new AutofillTable("en-US")));
+ wdbs_->LoadDatabase(WebDatabaseService::InitCallback());
+
+ wds_ = new AutofillWebDataService(
+ wdbs_, WebDataServiceBase::ProfileErrorCallback());
+ wds_->Init();
+ }
+
+ virtual void TearDown() {
+ wds_->ShutdownOnUIThread();
+ wdbs_->ShutdownDatabase();
+ wds_ = NULL;
+ wdbs_ = NULL;
+ WaitForDatabaseThread();
+
+ MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ MessageLoop::current()->Run();
+ db_thread_.Stop();
+ }
+
+ void WaitForDatabaseThread() {
+ base::WaitableEvent done(false, false);
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
+ done.Wait();
+ }
+
+ MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ base::FilePath profile_dir_;
+ scoped_refptr<AutofillWebDataService> wds_;
+ scoped_refptr<WebDatabaseService> wdbs_;
+ base::ScopedTempDir temp_dir_;
+};
+
+class WebDataServiceAutofillTest : public WebDataServiceTest {
+ public:
+ WebDataServiceAutofillTest()
+ : WebDataServiceTest(),
+ unique_id1_(1),
+ unique_id2_(2),
+ test_timeout_(TimeDelta::FromSeconds(kWebDataServiceTimeoutSeconds)),
+ done_event_(false, false) {}
+
+ protected:
+ virtual void SetUp() {
+ WebDataServiceTest::SetUp();
+ name1_ = ASCIIToUTF16("name1");
+ name2_ = ASCIIToUTF16("name2");
+ value1_ = ASCIIToUTF16("value1");
+ value2_ = ASCIIToUTF16("value2");
+
+ void(AutofillWebDataService::*add_observer_func)(
+ AutofillWebDataServiceObserverOnDBThread*) =
+ &AutofillWebDataService::AddObserver;
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(add_observer_func, wds_, &observer_));
+ WaitForDatabaseThread();
+ }
+
+ virtual void TearDown() {
+ void(AutofillWebDataService::*remove_observer_func)(
+ AutofillWebDataServiceObserverOnDBThread*) =
+ &AutofillWebDataService::RemoveObserver;
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(remove_observer_func, wds_, &observer_));
+ WaitForDatabaseThread();
+
+ WebDataServiceTest::TearDown();
+ }
+
+ void AppendFormField(const string16& name,
+ const string16& value,
+ std::vector<FormFieldData>* form_fields) {
+ FormFieldData field;
+ field.name = name;
+ field.value = value;
+ form_fields->push_back(field);
+ }
+
+ string16 name1_;
+ string16 name2_;
+ string16 value1_;
+ string16 value2_;
+ int unique_id1_, unique_id2_;
+ const TimeDelta test_timeout_;
+ testing::NiceMock<MockAutofillWebDataServiceObserver> observer_;
+ WaitableEvent done_event_;
+};
+
+// Simple consumer for Keywords data. Stores the result data and quits UI
+// message loop when callback is invoked.
+class KeywordsConsumer : public WebDataServiceConsumer {
+ public:
+ KeywordsConsumer() : load_succeeded(false) {}
+
+ virtual void OnWebDataServiceRequestDone(
+ WebDataService::Handle h,
+ const WDTypedResult* result) OVERRIDE {
+ if (result) {
+ load_succeeded = true;
+ DCHECK(result->GetType() == KEYWORDS_RESULT);
+ keywords_result =
+ reinterpret_cast<const WDResult<WDKeywordsResult>*>(
+ result)->GetValue();
+ }
+
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ MessageLoop::current()->Quit();
+ }
+
+ // True if keywords data was loaded successfully.
+ bool load_succeeded;
+ // Result data from completion callback.
+ WDKeywordsResult keywords_result;
+};
+
+TEST_F(WebDataServiceAutofillTest, FormFillAdd) {
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::ADD, AutofillKey(name1_, value1_)),
+ AutofillChange(AutofillChange::ADD, AutofillKey(name2_, value2_))
+ };
+
+ // This will verify that the correct notification is triggered,
+ // passing the correct list of autofill keys in the details.
+ EXPECT_CALL(observer_,
+ AutofillEntriesChanged(ElementsAreArray(expected_changes)))
+ .WillOnce(SignalEvent(&done_event_));
+
+ std::vector<FormFieldData> form_fields;
+ AppendFormField(name1_, value1_, &form_fields);
+ AppendFormField(name2_, value2_, &form_fields);
+ wds_->AddFormFields(form_fields);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+
+ AutofillWebDataServiceConsumer<std::vector<string16> > consumer;
+ WebDataService::Handle handle;
+ static const int limit = 10;
+ handle = wds_->GetFormValuesForElementName(
+ name1_, string16(), limit, &consumer);
+
+ // The message loop will exit when the consumer is called.
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(value1_, consumer.result()[0]);
+}
+
+TEST_F(WebDataServiceAutofillTest, FormFillRemoveOne) {
+ // First add some values to autofill.
+ EXPECT_CALL(observer_, AutofillEntriesChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+ std::vector<FormFieldData> form_fields;
+ AppendFormField(name1_, value1_, &form_fields);
+ wds_->AddFormFields(form_fields);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+
+ // This will verify that the correct notification is triggered,
+ // passing the correct list of autofill keys in the details.
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_))
+ };
+ EXPECT_CALL(observer_,
+ AutofillEntriesChanged(ElementsAreArray(expected_changes)))
+ .WillOnce(SignalEvent(&done_event_));
+ wds_->RemoveFormValueForElementName(name1_, value1_);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+}
+
+TEST_F(WebDataServiceAutofillTest, FormFillRemoveMany) {
+ TimeDelta one_day(TimeDelta::FromDays(1));
+ Time t = Time::Now();
+
+ EXPECT_CALL(observer_, AutofillEntriesChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+
+ std::vector<FormFieldData> form_fields;
+ AppendFormField(name1_, value1_, &form_fields);
+ AppendFormField(name2_, value2_, &form_fields);
+ wds_->AddFormFields(form_fields);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+
+ // This will verify that the correct notification is triggered,
+ // passing the correct list of autofill keys in the details.
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)),
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name2_, value2_))
+ };
+ EXPECT_CALL(observer_,
+ AutofillEntriesChanged(ElementsAreArray(expected_changes)))
+ .WillOnce(SignalEvent(&done_event_));
+ wds_->RemoveFormElementsAddedBetween(t, t + one_day);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+}
+
+TEST_F(WebDataServiceAutofillTest, ProfileAdd) {
+ AutofillProfile profile;
+
+ // Check that GUID-based notification was sent.
+ const AutofillProfileChange expected_change(
+ AutofillProfileChange::ADD, profile.guid(), &profile);
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ wds_->AddAutofillProfile(profile);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(profile, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, ProfileRemove) {
+ AutofillProfile profile;
+
+ // Add a profile.
+ EXPECT_CALL(observer_, AutofillProfileChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+ wds_->AddAutofillProfile(profile);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(profile, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+
+ // Check that GUID-based notification was sent.
+ const AutofillProfileChange expected_change(
+ AutofillProfileChange::REMOVE, profile.guid(), NULL);
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ // Remove the profile.
+ wds_->RemoveAutofillProfile(profile.guid());
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was removed.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetAutofillProfiles(&consumer2);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(0U, consumer2.result().size());
+}
+
+TEST_F(WebDataServiceAutofillTest, ProfileUpdate) {
+ AutofillProfile profile1;
+ profile1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Abe"));
+ AutofillProfile profile2;
+ profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Alice"));
+
+ EXPECT_CALL(observer_, AutofillProfileChanged(_))
+ .WillOnce(DoDefault())
+ .WillOnce(SignalEvent(&done_event_));
+
+ wds_->AddAutofillProfile(profile1);
+ wds_->AddAutofillProfile(profile2);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that they were added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(2U, consumer.result().size());
+ EXPECT_EQ(profile1, *consumer.result()[0]);
+ EXPECT_EQ(profile2, *consumer.result()[1]);
+ STLDeleteElements(&consumer.result());
+
+ AutofillProfile profile1_changed(profile1);
+ profile1_changed.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Bill"));
+ const AutofillProfileChange expected_change(
+ AutofillProfileChange::UPDATE, profile1.guid(), &profile1_changed);
+
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ // Update the profile.
+ wds_->UpdateAutofillProfile(profile1_changed);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that the updates were made.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetAutofillProfiles(&consumer2);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(2U, consumer2.result().size());
+ EXPECT_NE(profile1, *consumer2.result()[0]);
+ EXPECT_EQ(profile1_changed, *consumer2.result()[0]);
+ EXPECT_EQ(profile2, *consumer2.result()[1]);
+ STLDeleteElements(&consumer2.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, CreditAdd) {
+ CreditCard card;
+ wds_->AddCreditCard(card);
+ WaitForDatabaseThread();
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer;
+ WebDataService::Handle handle = wds_->GetCreditCards(&consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(card, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, CreditCardRemove) {
+ CreditCard credit_card;
+
+ // Add a credit card.
+ wds_->AddCreditCard(credit_card);
+ WaitForDatabaseThread();
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer;
+ WebDataService::Handle handle = wds_->GetCreditCards(&consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(credit_card, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+
+ // Remove the credit card.
+ wds_->RemoveCreditCard(credit_card.guid());
+ WaitForDatabaseThread();
+
+ // Check that it was removed.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(0U, consumer2.result().size());
+}
+
+TEST_F(WebDataServiceAutofillTest, CreditUpdate) {
+ CreditCard card1;
+ card1.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Abe"));
+ CreditCard card2;
+ card2.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Alice"));
+
+ wds_->AddCreditCard(card1);
+ wds_->AddCreditCard(card2);
+ WaitForDatabaseThread();
+
+ // Check that they got added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer;
+ WebDataService::Handle handle = wds_->GetCreditCards(&consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(2U, consumer.result().size());
+ EXPECT_EQ(card1, *consumer.result()[0]);
+ EXPECT_EQ(card2, *consumer.result()[1]);
+ STLDeleteElements(&consumer.result());
+
+ CreditCard card1_changed(card1);
+ card1_changed.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Bill"));
+
+ wds_->UpdateCreditCard(card1_changed);
+ WaitForDatabaseThread();
+
+ // Check that the updates were made.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(2U, consumer2.result().size());
+ EXPECT_NE(card1, *consumer2.result()[0]);
+ EXPECT_EQ(card1_changed, *consumer2.result()[0]);
+ EXPECT_EQ(card2, *consumer2.result()[1]);
+ STLDeleteElements(&consumer2.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, AutofillRemoveModifiedBetween) {
+ // Add a profile.
+ EXPECT_CALL(observer_, AutofillProfileChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+ AutofillProfile profile;
+ wds_->AddAutofillProfile(profile);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> >
+ profile_consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&profile_consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, profile_consumer.handle());
+ ASSERT_EQ(1U, profile_consumer.result().size());
+ EXPECT_EQ(profile, *profile_consumer.result()[0]);
+ STLDeleteElements(&profile_consumer.result());
+
+ // Add a credit card.
+ CreditCard credit_card;
+ wds_->AddCreditCard(credit_card);
+ WaitForDatabaseThread();
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > card_consumer;
+ handle = wds_->GetCreditCards(&card_consumer);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle, card_consumer.handle());
+ ASSERT_EQ(1U, card_consumer.result().size());
+ EXPECT_EQ(credit_card, *card_consumer.result()[0]);
+ STLDeleteElements(&card_consumer.result());
+
+ // Check that GUID-based notification was sent for the profile.
+ const AutofillProfileChange expected_profile_change(
+ AutofillProfileChange::REMOVE, profile.guid(), NULL);
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_profile_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ // Remove the profile using time range of "all time".
+ wds_->RemoveAutofillDataModifiedBetween(Time(), Time());
+ done_event_.TimedWait(test_timeout_);
+ WaitForDatabaseThread();
+
+ // Check that the profile was removed.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> >
+ profile_consumer2;
+ WebDataService::Handle handle2 =
+ wds_->GetAutofillProfiles(&profile_consumer2);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, profile_consumer2.handle());
+ ASSERT_EQ(0U, profile_consumer2.result().size());
+
+ // Check that the credit card was removed.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > card_consumer2;
+ handle2 = wds_->GetCreditCards(&card_consumer2);
+ MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, card_consumer2.handle());
+ ASSERT_EQ(0U, card_consumer2.result().size());
+}