diff options
Diffstat (limited to 'components/autofill/browser')
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, ×tamps_); +} + +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"), + ×tamps)); + 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()); +} |