| Index: chrome/browser/webdata/web_database.cc
|
| diff --git a/chrome/browser/webdata/web_database.cc b/chrome/browser/webdata/web_database.cc
|
| index adfa93a75c1436875fcda0cfdb2ad9f30b6aeb67..5b600e298bb539c26b14ed52a1ab1c2d0bea90fa 100644
|
| --- a/chrome/browser/webdata/web_database.cc
|
| +++ b/chrome/browser/webdata/web_database.cc
|
| @@ -20,6 +20,7 @@
|
| #include "chrome/browser/autofill/autofill_profile.h"
|
| #include "chrome/browser/autofill/autofill_type.h"
|
| #include "chrome/browser/autofill/credit_card.h"
|
| +#include "chrome/browser/autofill/personal_data_manager.h"
|
| #include "chrome/browser/diagnostics/sqlite_diagnostics.h"
|
| #include "chrome/browser/history/history_database.h"
|
| #include "chrome/browser/password_manager/encryptor.h"
|
| @@ -158,6 +159,13 @@ enum AutoFillPhoneType {
|
| // of the number.
|
| // 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.
|
| @@ -192,8 +200,8 @@ typedef std::vector<Tuple3<int64, string16, string16> > AutofillElementList;
|
| // Current version number. Note: when changing the current version number,
|
| // corresponding changes must happen in the unit tests, and new migration test
|
| // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
|
| -const int kCurrentVersionNumber = 35;
|
| -const int kCompatibleVersionNumber = 35;
|
| +const int kCurrentVersionNumber = 36;
|
| +const int kCompatibleVersionNumber = 36;
|
|
|
| // ID of the url column in keywords.
|
| const int kUrlIdPosition = 16;
|
| @@ -592,6 +600,13 @@ bool RemoveAutofillProfilePieces(const std::string& guid, sql::Connection* db) {
|
| return s3.Run();
|
| }
|
|
|
| +// 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;
|
| +}
|
| +
|
| } // namespace
|
|
|
| WebDatabase::WebDatabase() {
|
| @@ -651,8 +666,8 @@ sql::InitStatus WebDatabase::Init(const FilePath& db_name) {
|
| !InitWebAppsTable() || !InitAutofillTable() ||
|
| !InitAutofillDatesTable() || !InitAutofillProfilesTable() ||
|
| !InitAutofillProfileNamesTable() || !InitAutofillProfileEmailsTable() ||
|
| - !InitAutofillProfilePhonesTable() || !InitCreditCardsTable() ||
|
| - !InitTokenServiceTable()) {
|
| + !InitAutofillProfilePhonesTable() || !InitAutofillProfileTrashTable() ||
|
| + !InitCreditCardsTable() || !InitTokenServiceTable()) {
|
| LOG(WARNING) << "Unable to initialize the web database.";
|
| return sql::INIT_FAILURE;
|
| }
|
| @@ -997,6 +1012,17 @@ bool WebDatabase::InitAutofillProfilePhonesTable() {
|
| return true;
|
| }
|
|
|
| +bool WebDatabase::InitAutofillProfileTrashTable() {
|
| + if (!db_.DoesTableExist("autofill_profiles_trash")) {
|
| + if (!db_.Execute("CREATE TABLE autofill_profiles_trash ( "
|
| + "guid VARCHAR)")) {
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| bool WebDatabase::InitCreditCardsTable() {
|
| if (!db_.DoesTableExist("credit_cards")) {
|
| if (!db_.Execute("CREATE TABLE credit_cards ( "
|
| @@ -1847,6 +1873,9 @@ bool WebDatabase::RemoveFormElement(const string16& name,
|
| }
|
|
|
| bool WebDatabase::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,"
|
| @@ -1986,6 +2015,11 @@ bool WebDatabase::GetAutofillProfiles(
|
| bool WebDatabase::UpdateAutofillProfile(const AutofillProfile& profile) {
|
| DCHECK(guid::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;
|
| @@ -2022,6 +2056,23 @@ bool WebDatabase::UpdateAutofillProfile(const AutofillProfile& profile) {
|
|
|
| bool WebDatabase::RemoveAutofillProfile(const std::string& guid) {
|
| DCHECK(guid::IsValidGUID(guid));
|
| +
|
| + if (IsAutofillGUIDInTrash(guid)) {
|
| + sql::Statement s_trash(db_.GetUniqueStatement(
|
| + "DELETE FROM autofill_profiles_trash WHERE guid = ?"));
|
| + if (!s_trash) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| + s_trash.BindString(0, guid);
|
| + if (!s_trash.Run()) {
|
| + NOTREACHED() << "Expected item in trash.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| sql::Statement s(db_.GetUniqueStatement(
|
| "DELETE FROM autofill_profiles WHERE guid = ?"));
|
| if (!s) {
|
| @@ -2036,6 +2087,50 @@ bool WebDatabase::RemoveAutofillProfile(const std::string& guid) {
|
| return RemoveAutofillProfilePieces(guid, &db_);
|
| }
|
|
|
| +bool WebDatabase::ClearAutofillProfiles() {
|
| + sql::Statement s1(db_.GetUniqueStatement(
|
| + "DELETE FROM autofill_profiles"));
|
| + if (!s1) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + if (!s1.Run())
|
| + return false;
|
| +
|
| + sql::Statement s2(db_.GetUniqueStatement(
|
| + "DELETE FROM autofill_profile_names"));
|
| + if (!s2) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + if (!s2.Run())
|
| + return false;
|
| +
|
| + sql::Statement s3(db_.GetUniqueStatement(
|
| + "DELETE FROM autofill_profile_emails"));
|
| + if (!s3) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + if (!s3.Run())
|
| + return false;
|
| +
|
| + sql::Statement s4(db_.GetUniqueStatement(
|
| + "DELETE FROM autofill_profile_phones"));
|
| + if (!s4) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + if (!s4.Run())
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
| +
|
| bool WebDatabase::AddCreditCard(const CreditCard& credit_card) {
|
| sql::Statement s(db_.GetUniqueStatement(
|
| "INSERT INTO credit_cards"
|
| @@ -2195,6 +2290,36 @@ bool WebDatabase::RemoveAutofillProfilesAndCreditCardsModifiedBetween(
|
| return true;
|
| }
|
|
|
| +bool WebDatabase::GetAutofillProfilesInTrash(std::vector<std::string>* guids) {
|
| + guids->clear();
|
| +
|
| + sql::Statement s(db_.GetUniqueStatement(
|
| + "SELECT guid "
|
| + "FROM autofill_profiles_trash"));
|
| + if (!s) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + while (s.Step()) {
|
| + std::string guid = s.ColumnString(0);
|
| + guids->push_back(guid);
|
| + }
|
| +
|
| + return s.Succeeded();
|
| +}
|
| +
|
| +bool WebDatabase::EmptyAutofillProfilesTrash() {
|
| + sql::Statement s(db_.GetUniqueStatement(
|
| + "DELETE FROM autofill_profiles_trash"));
|
| + if (!s) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + return s.Run();
|
| +}
|
| +
|
| bool WebDatabase::AddToCountOfFormElement(int64 pair_id,
|
| int delta,
|
| bool* was_removed) {
|
| @@ -2231,6 +2356,50 @@ bool WebDatabase::RemoveFormElementForID(int64 pair_id) {
|
| return false;
|
| }
|
|
|
| +bool WebDatabase::AddAutofillGUIDToTrash(const std::string& guid) {
|
| + sql::Statement s(db_.GetUniqueStatement(
|
| + "INSERT INTO autofill_profiles_trash"
|
| + " (guid) "
|
| + "VALUES (?)"));
|
| + if (!s) {
|
| + NOTREACHED();
|
| + return sql::INIT_FAILURE;
|
| + }
|
| +
|
| + s.BindString(0, guid);
|
| + if (!s.Run()) {
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool WebDatabase::IsAutofillProfilesTrashEmpty() {
|
| + sql::Statement s(db_.GetUniqueStatement(
|
| + "SELECT guid "
|
| + "FROM autofill_profiles_trash"));
|
| + if (!s) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + return !s.Step();
|
| +}
|
| +
|
| +bool WebDatabase::IsAutofillGUIDInTrash(const std::string& guid) {
|
| + sql::Statement s(db_.GetUniqueStatement(
|
| + "SELECT guid "
|
| + "FROM autofill_profiles_trash "
|
| + "WHERE guid = ?"));
|
| + if (!s) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return false;
|
| + }
|
| +
|
| + s.BindString(0, guid);
|
| + return s.Step();
|
| +}
|
| +
|
| sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){
|
| // Migrate if necessary.
|
| int current_version = meta_table_.GetVersionNumber();
|
| @@ -3092,6 +3261,101 @@ sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){
|
|
|
| // FALL THROUGH
|
|
|
| + case 35:
|
| + // Merge and cull older profiles where possible.
|
| + {
|
| + sql::Statement s(db_.GetUniqueStatement(
|
| + "SELECT guid, date_modified "
|
| + "FROM autofill_profiles"));
|
| + if (!s) {
|
| + NOTREACHED() << "Statement prepare failed";
|
| + return sql::INIT_FAILURE;
|
| + }
|
| +
|
| + // 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)) {
|
| + NOTREACHED() << "Bad read of profile.";
|
| + return sql::INIT_FAILURE;
|
| + }
|
| + scoped_ptr<AutofillProfile> p(profile);
|
| +
|
| + if (PersonalDataManager::IsValidLearnableProfile(*p)) {
|
| + std::vector<AutofillProfile> merged_profiles;
|
| + bool merged = PersonalDataManager::MergeProfile(
|
| + *p, accumulated_profiles_p, &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());
|
| + }
|
| + }
|
| +
|
| + // Drop the current profiles.
|
| + if (!ClearAutofillProfiles()) {
|
| + LOG(WARNING) << "Unable to update web database to version 36.";
|
| + NOTREACHED();
|
| + return sql::INIT_FAILURE;
|
| + }
|
| +
|
| + // 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)) {
|
| + LOG(WARNING) << "Unable to update web database to version 36.";
|
| + NOTREACHED();
|
| + return sql::INIT_FAILURE;
|
| + }
|
| +
|
| + // 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()) {
|
| + LOG(WARNING) << "Unable to update web database to version 36.";
|
| + NOTREACHED();
|
| + return sql::INIT_FAILURE;
|
| + }
|
| + 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()) {
|
| + LOG(WARNING) << "Unable to update web database to version 36.";
|
| + NOTREACHED();
|
| + return sql::INIT_FAILURE;
|
| + }
|
| + }
|
| + }
|
| +
|
| + meta_table_.SetVersionNumber(36);
|
| + meta_table_.SetCompatibleVersionNumber(
|
| + std::min(36, kCompatibleVersionNumber));
|
| +
|
| + // FALL THROUGH
|
| +
|
| // Add successive versions here. Each should set the version number and
|
| // compatible version number as appropriate, then fall through to the next
|
| // case.
|
|
|