diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/autofill/personal_data_manager.cc | 14 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service_autofill_unittest.cc | 21 | ||||
-rw-r--r-- | chrome/browser/webdata/autocomplete_syncable_service.cc | 157 | ||||
-rw-r--r-- | chrome/browser/webdata/autocomplete_syncable_service.h | 12 | ||||
-rw-r--r-- | chrome/browser/webdata/autofill_entry.cc | 43 | ||||
-rw-r--r-- | chrome/browser/webdata/autofill_entry.h | 19 | ||||
-rw-r--r-- | chrome/browser/webdata/autofill_entry_unittest.cc | 52 | ||||
-rw-r--r-- | chrome/browser/webdata/autofill_table.cc | 137 | ||||
-rw-r--r-- | chrome/browser/webdata/autofill_table.h | 24 | ||||
-rw-r--r-- | chrome/browser/webdata/autofill_table_unittest.cc | 11 | ||||
-rw-r--r-- | chrome/browser/webdata/web_data_service.cc | 32 | ||||
-rw-r--r-- | chrome/browser/webdata/web_data_service.h | 2 |
12 files changed, 424 insertions, 100 deletions
diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc index f291cf9..7ab1285 100644 --- a/chrome/browser/autofill/personal_data_manager.cc +++ b/chrome/browser/autofill/personal_data_manager.cc @@ -23,6 +23,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/webdata/autofill_entry.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" @@ -161,6 +162,19 @@ void PersonalDataManager::OnWebDataServiceRequestDone( AutofillProfile::AdjustInferredLabels(&profile_pointers); FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_, OnPersonalDataChanged()); + + // As all Autofill data is ready, the Autocomplete data is ready as well. + // If sync is not set, cull older entries of the autocomplete. Otherwise, + // the entries will be culled when sync is connected. + ProfileSyncService* sync_service = + ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_); + if (sync_service && (!sync_service->HasSyncSetupCompleted() || + !profile_->GetPrefs()->GetBoolean(prefs::kSyncAutofill))) { + WebDataService* wds = + profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); + if (wds) + wds->RemoveExpiredFormElements(); + } } } diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc index 7974c58..f680214 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -58,6 +58,7 @@ #include "testing/gmock/include/gmock/gmock.h" using base::Time; +using base::TimeDelta; using base::WaitableEvent; using browser_sync::AutofillDataTypeController; using browser_sync::AutofillProfileDataTypeController; @@ -542,21 +543,25 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { static AutofillEntry MakeAutofillEntry(const char* name, const char* value, - time_t timestamp0, - time_t timestamp1) { + int time_shift0, + int time_shift1) { + // Time deep in the past would cause Autocomplete sync to discard the + // entries. + static Time base_time = Time::Now().LocalMidnight(); + std::vector<Time> timestamps; - if (timestamp0 > 0) - timestamps.push_back(Time::FromTimeT(timestamp0)); - if (timestamp1 > 0) - timestamps.push_back(Time::FromTimeT(timestamp1)); + if (time_shift0 > 0) + timestamps.push_back(base_time + TimeDelta::FromSeconds(time_shift0)); + if (time_shift1 > 0) + timestamps.push_back(base_time + TimeDelta::FromSeconds(time_shift1)); return AutofillEntry( AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)), timestamps); } static AutofillEntry MakeAutofillEntry(const char* name, const char* value, - time_t timestamp) { - return MakeAutofillEntry(name, value, timestamp, -1); + int time_shift) { + return MakeAutofillEntry(name, value, time_shift, -1); } friend class AddAutofillHelper<AutofillEntry>; diff --git a/chrome/browser/webdata/autocomplete_syncable_service.cc b/chrome/browser/webdata/autocomplete_syncable_service.cc index 3b3ecb1..6ec7048 100644 --- a/chrome/browser/webdata/autocomplete_syncable_service.cc +++ b/chrome/browser/webdata/autocomplete_syncable_service.cc @@ -29,30 +29,54 @@ const char kAutofillEntryNamespaceTag[] = "autofill_entry|"; // Merges timestamps from the |autofill| entry and |timestamps|. Returns // true if they were different, false if they were the same. // All of the timestamp vectors are assummed to be sorted, resulting vector is -// sorted as well. +// sorted as well. Only two timestamps - the earliest and the latest are stored. bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill, const std::vector<base::Time>& timestamps, std::vector<base::Time>* new_timestamps) { DCHECK(new_timestamps); - std::set<base::Time> timestamp_union(timestamps.begin(), - timestamps.end()); + new_timestamps->clear(); size_t timestamps_count = autofill.usage_timestamp_size(); - - bool different = timestamps.size() != timestamps_count; - for (size_t i = 0; i < timestamps_count; ++i) { - if (timestamp_union.insert(base::Time::FromInternalValue( - autofill.usage_timestamp(i))).second) { - different = true; - } - } - - if (different) { + if (timestamps_count == 0 && timestamps.empty()) { + return false; + } else if (timestamps_count == 0) { new_timestamps->insert(new_timestamps->begin(), - timestamp_union.begin(), - timestamp_union.end()); + timestamps.begin(), + timestamps.end()); + return true; + } else if (timestamps.empty()) { + new_timestamps->reserve(2); + new_timestamps->push_back(base::Time::FromInternalValue( + autofill.usage_timestamp(0))); + if (timestamps_count > 1) { + new_timestamps->push_back(base::Time::FromInternalValue( + autofill.usage_timestamp(timestamps_count - 1))); + } + return true; + } else { + base::Time sync_time_begin = base::Time::FromInternalValue( + autofill.usage_timestamp(0)); + base::Time sync_time_end = base::Time::FromInternalValue( + autofill.usage_timestamp(timestamps_count - 1)); + if (timestamps.front() != sync_time_begin || + timestamps.back() != sync_time_end) { + new_timestamps->push_back( + timestamps.front() < sync_time_begin ? timestamps.front() : + sync_time_begin); + if (new_timestamps->back() != timestamps.back() || + new_timestamps->back() != sync_time_end) { + new_timestamps->push_back( + timestamps.back() > sync_time_end ? timestamps.back() : + sync_time_end); + } + return true; + } else { + new_timestamps->insert(new_timestamps->begin(), + timestamps.begin(), + timestamps.end()); + return false; + } } - return different; } } // namespace @@ -101,24 +125,58 @@ SyncError AutocompleteSyncableService::MergeDataAndStartSyncing( sync_processor_ = sync_processor.Pass(); std::vector<AutofillEntry> new_synced_entries; + std::vector<AutofillEntry> synced_expired_entries; // Go through and check for all the entries that sync already knows about. // CreateOrUpdateEntry() will remove entries that are same with the synced // ones from |new_db_entries|. for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); sync_iter != initial_sync_data.end(); ++sync_iter) { - CreateOrUpdateEntry(*sync_iter, &new_db_entries, &new_synced_entries); + CreateOrUpdateEntry(*sync_iter, &new_db_entries, + &new_synced_entries, &synced_expired_entries); } + // Check if newly received items need culling. + bool need_to_cull_data = !synced_expired_entries.empty(); + if (!SaveChangesToWebData(new_synced_entries)) return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); + keys_to_ignore_.clear(); SyncChangeList new_changes; for (AutocompleteEntryMap::iterator i = new_db_entries.begin(); i != new_db_entries.end(); ++i) { - new_changes.push_back( - SyncChange(i->second.first, CreateSyncData(*(i->second.second)))); + // Sync back only the data that appeared after + // |AutofillEntry::ExpirationTime()|. + if (!i->second.second->IsExpired()) { + new_changes.push_back( + SyncChange(i->second.first, CreateSyncData(*(i->second.second)))); + } else { + need_to_cull_data = true; + // Key is not on the server and is too old, it will not ever be synced - + // delete it locally. + if (i->second.first == SyncChange::ACTION_ADD) + keys_to_ignore_.insert(i->first); + } + } + + // Delete only the changes never synced to the db as they are too old. + for (size_t i = 0; i < synced_expired_entries.size(); ++i) { + // Key is on the server and not local and is too old, we need to notify + // sync that it has expired. + if (keys_to_ignore_.find(synced_expired_entries[i].key()) == + keys_to_ignore_.end()) { + new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, + CreateSyncData(synced_expired_entries[i]))); + } + } + + if (need_to_cull_data) { + // This will schedule deletion operation later on DB thread and we will + // be notified on the results of the deletion and deletes will be synced to + // the sync. + web_data_service_->RemoveExpiredFormElements(); } SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); @@ -169,6 +227,7 @@ SyncError AutocompleteSyncableService::ProcessSyncChanges( std::vector<AutofillEntry> entries; scoped_ptr<AutocompleteEntryMap> db_entries; std::vector<AutofillEntry> new_entries; + std::vector<AutofillEntry> ignored_entries; SyncError list_processing_error; @@ -192,7 +251,8 @@ SyncError AutocompleteSyncableService::ProcessSyncChanges( std::make_pair(SyncChange::ACTION_ADD, it); } } - CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries); + CreateOrUpdateEntry(i->sync_data(), db_entries.get(), + &new_entries, &ignored_entries); break; case SyncChange::ACTION_DELETE: { DCHECK(i->sync_data().GetSpecifics().has_autofill()) @@ -217,6 +277,17 @@ SyncError AutocompleteSyncableService::ProcessSyncChanges( if (!SaveChangesToWebData(new_entries)) return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); + // Remove already expired data. + for (size_t i = 0; i < ignored_entries.size(); ++i) { + if (db_entries.get() && + db_entries->find(ignored_entries[i].key()) != db_entries->end()) { + bool success = web_data_service_->GetDatabase()->GetAutofillTable()-> + RemoveFormElement(ignored_entries[i].key().name(), + ignored_entries[i].key().value()); + DCHECK(success); + } + } + WebDataService::NotifyOfMultipleAutofillChanges(web_data_service_); return list_processing_error; @@ -264,7 +335,8 @@ bool AutocompleteSyncableService::SaveChangesToWebData( void AutocompleteSyncableService::CreateOrUpdateEntry( const SyncData& data, AutocompleteEntryMap* loaded_data, - std::vector<AutofillEntry>* new_entries) { + std::vector<AutofillEntry>* new_entries, + std::vector<AutofillEntry>* ignored_entries) { const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); const sync_pb::AutofillSpecifics& autofill_specifics( specifics.autofill()); @@ -282,12 +354,20 @@ void AutocompleteSyncableService::CreateOrUpdateEntry( // New entry. std::vector<base::Time> timestamps; size_t timestamps_count = autofill_specifics.usage_timestamp_size(); - timestamps.resize(timestamps_count); - for (size_t ts = 0; ts < timestamps_count; ++ts) { - timestamps[ts] = base::Time::FromInternalValue( - autofill_specifics.usage_timestamp(ts)); + timestamps.reserve(2); + if (timestamps_count) { + timestamps.push_back(base::Time::FromInternalValue( + autofill_specifics.usage_timestamp(0))); } - new_entries->push_back(AutofillEntry(key, timestamps)); + if (timestamps_count > 1) { + timestamps.push_back(base::Time::FromInternalValue( + autofill_specifics.usage_timestamp(timestamps_count - 1))); + } + AutofillEntry new_entry(key, timestamps); + if (new_entry.IsExpired()) + ignored_entries->push_back(new_entry); + else + new_entries->push_back(new_entry); } else { // Entry already present - merge if necessary. std::vector<base::Time> timestamps; @@ -295,11 +375,14 @@ void AutocompleteSyncableService::CreateOrUpdateEntry( autofill_specifics, it->second.second->timestamps(), ×tamps); if (different) { AutofillEntry new_entry(it->second.second->key(), timestamps); - new_entries->push_back(new_entry); - - // Update the sync db if the list of timestamps have changed. - *(it->second.second) = new_entry; - it->second.first = SyncChange::ACTION_UPDATE; + if (new_entry.IsExpired()) { + ignored_entries->push_back(new_entry); + } else { + new_entries->push_back(new_entry); + // Update the sync db if the list of timestamps have changed. + *(it->second.second) = new_entry; + it->second.first = SyncChange::ACTION_UPDATE; + } } else { loaded_data->erase(it); } @@ -358,10 +441,12 @@ void AutocompleteSyncableService::ActOnChanges( break; } case AutofillChange::REMOVE: { - std::vector<base::Time> timestamps; - AutofillEntry entry(change->key(), timestamps); - new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, - CreateSyncData(entry))); + if (keys_to_ignore_.find(change->key()) == keys_to_ignore_.end()) { + std::vector<base::Time> timestamps; + AutofillEntry entry(change->key(), timestamps); + new_changes.push_back(SyncChange(SyncChange::ACTION_DELETE, + CreateSyncData(entry))); + } break; } default: @@ -375,6 +460,8 @@ void AutocompleteSyncableService::ActOnChanges( << " Failed processing change:" << " Error:" << error.message(); } + // |keys_to_ignore_| are only needed for the very first notification. + keys_to_ignore_.clear(); } SyncData AutocompleteSyncableService::CreateSyncData( diff --git a/chrome/browser/webdata/autocomplete_syncable_service.h b/chrome/browser/webdata/autocomplete_syncable_service.h index 0c38849..f13873d 100644 --- a/chrome/browser/webdata/autocomplete_syncable_service.h +++ b/chrome/browser/webdata/autocomplete_syncable_service.h @@ -6,6 +6,7 @@ #pragma once #include <map> +#include <set> #include <string> #include <utility> #include <vector> @@ -93,9 +94,15 @@ class AutocompleteSyncableService AutocompleteEntryMap; // Creates or updates an autocomplete entry based on |data|. + // |data| - an entry for sync. + // |loaded_data| - entries that were loaded from local storage. + // |new_entries| - entries that came from the sync. + // |ignored_entries| - entries that came from the sync, but too old to be + // stored and immediately discarded. void CreateOrUpdateEntry(const SyncData& data, AutocompleteEntryMap* loaded_data, - std::vector<AutofillEntry>* bundle); + std::vector<AutofillEntry>* new_entries, + std::vector<AutofillEntry>* ignored_entries); // Writes |entry| data into supplied |autofill_specifics|. static void WriteAutofillEntry(const AutofillEntry& entry, @@ -118,6 +125,9 @@ class AutocompleteSyncableService sync_processor_.reset(sync_processor); } + // Ignore deletions of the following keys as they were never synced. + std::set<AutofillKey> keys_to_ignore_; + // Lifetime of AutocompleteSyncableService object is shorter than // |web_data_service_| passed to it. WebDataService* web_data_service_; diff --git a/chrome/browser/webdata/autofill_entry.cc b/chrome/browser/webdata/autofill_entry.cc index fe874a5..27dcbf3 100644 --- a/chrome/browser/webdata/autofill_entry.cc +++ b/chrome/browser/webdata/autofill_entry.cc @@ -1,14 +1,22 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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 "chrome/browser/webdata/autofill_entry.h" + #include <algorithm> #include <set> #include "base/logging.h" -#include "chrome/browser/webdata/autofill_entry.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) @@ -72,7 +80,19 @@ bool AutofillEntry::operator<(const AutofillEntry& entry) const { return key_ < entry.key(); } -// Culls the list of timestamps to the most recent |kMaxAutofillTimeStamps|. +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 @@ -83,26 +103,23 @@ bool AutofillEntry::operator<(const AutofillEntry& entry) const { // 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. -// Also if sync is not enabled this culling would never take place. bool AutofillEntry::CullTimeStamps(const std::vector<base::Time>& source, - std::vector<base::Time>* result) { + std::vector<base::Time>* result) { DCHECK(result); DCHECK(&source != result); // First copy the source to result. result->clear(); - result->insert(result->begin(), source.begin(), source.end()); - if (source.size() <= kMaxAutofillTimeStamps) + if (source.size() <= 2) { + result->insert(result->begin(), source.begin(), source.end()); return false; + } - VLOG(1) << "Culling timestamps. Current count is : " << source.size(); - - // Regular sort does ascending order. So we pass in a comparator function. - partial_sort(result->begin(), result->begin() + kMaxAutofillTimeStamps, - result->end(), std::greater<base::Time>()); + result->push_back(source.front()); + result->push_back(source.back()); - result->erase(result->begin() + kMaxAutofillTimeStamps, result->end()); + DVLOG(1) << "Culling timestamps. Current count is : " << source.size(); return true; } diff --git a/chrome/browser/webdata/autofill_entry.h b/chrome/browser/webdata/autofill_entry.h index 8aae993..c168f13 100644 --- a/chrome/browser/webdata/autofill_entry.h +++ b/chrome/browser/webdata/autofill_entry.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -46,14 +46,21 @@ class AutofillEntry { 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 |kMaxAutofillTimeStamps| latest timestamps. - // Result is stored in |result|. If the original vtor's size is less - // than kMaxAutofillTimeStamps then false is returned. Otherwise true is - // returned. Note: source and result should be DIFFERENT vectors. + // 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); @@ -62,6 +69,4 @@ class AutofillEntry { bool timestamps_culled_; }; -// TODO(lipalani): Move this inside the class defintion. -const unsigned int kMaxAutofillTimeStamps = 50; #endif // CHROME_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__ diff --git a/chrome/browser/webdata/autofill_entry_unittest.cc b/chrome/browser/webdata/autofill_entry_unittest.cc index 36c158a..66a6178 100644 --- a/chrome/browser/webdata/autofill_entry_unittest.cc +++ b/chrome/browser/webdata/autofill_entry_unittest.cc @@ -5,19 +5,20 @@ #include <algorithm> #include "base/time.h" +#include "base/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "chrome/browser/webdata/autofill_entry.h" -extern const unsigned int kMaxAutofillTimeStamps; +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 -1 ; ++i) { + for (size_t i = 0; i < kMaxAutofillTimeStamps; ++i) source.push_back(current); - } EXPECT_FALSE(AutofillEntry::CullTimeStamps(source, &result)); - EXPECT_EQ(result.size(), kMaxAutofillTimeStamps -1); + EXPECT_EQ(result.size(), kMaxAutofillTimeStamps); for (std::vector<base::Time>::const_iterator it = result.begin(); it != result.end(); ++it) { EXPECT_EQ(*it, current); @@ -38,11 +39,40 @@ TEST(AutofillEntryTest, Culling) { EXPECT_TRUE(AutofillEntry::CullTimeStamps(source, &result)); EXPECT_EQ(result.size(), kMaxAutofillTimeStamps); - int count = kMaxAutofillTimeStamps * 2 - 1; - for (std::vector<base::Time>::const_iterator it = result.begin(); - it != result.end(); ++it) { - EXPECT_EQ(*it, base::Time::FromInternalValue( - count*offset + internal_value)); - --count; - } + 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/chrome/browser/webdata/autofill_table.cc b/chrome/browser/webdata/autofill_table.cc index bba0b83..4cc6f2f 100644 --- a/chrome/browser/webdata/autofill_table.cc +++ b/chrome/browser/webdata/autofill_table.cc @@ -23,6 +23,7 @@ #include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/password_manager/encryptor.h" #include "chrome/browser/webdata/autofill_change.h" +#include "chrome/browser/webdata/autofill_entry.h" #include "chrome/common/guid.h" #include "sql/statement.h" #include "ui/base/l10n/l10n_util.h" @@ -418,17 +419,25 @@ bool AutofillTable::RemoveFormElementsAddedBetween( return false; for (AutofillElementList::iterator itr = elements.begin(); - itr != elements.end(); itr++) { + itr != elements.end(); ++itr) { int how_many = 0; if (!RemoveFormElementForTimeRange(itr->a, delete_begin, delete_end, &how_many)) { return false; } - bool was_removed = false; - if (!AddToCountOfFormElement(itr->a, -how_many, &was_removed)) - 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 = - was_removed ? AutofillChange::REMOVE : AutofillChange::UPDATE; + should_remove ? AutofillChange::REMOVE : AutofillChange::UPDATE; changes->push_back(AutofillChange(change_type, AutofillKey(itr->b, itr->c))); } @@ -436,6 +445,77 @@ bool AutofillTable::RemoveFormElementsAddedBetween( 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(); + } + + 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, @@ -455,20 +535,29 @@ bool AutofillTable::RemoveFormElementForTimeRange(int64 pair_id, 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, - bool* was_removed) { - DCHECK(was_removed); + int delta) { int count = 0; - *was_removed = false; if (!GetCountOfFormElement(pair_id, &count)) return false; if (count + delta == 0) { - if (!RemoveFormElementForID(pair_id)) - return false; - *was_removed = true; + // Should remove the element earlier in the code. + NOTREACHED(); + return false; } else { if (!SetCountOfFormElement(pair_id, count + delta)) return false; @@ -552,6 +641,19 @@ bool AutofillTable::InsertPairIDAndDate(int64 pair_id, 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<FormField>& elements, std::vector<AutofillChange>* changes, @@ -561,10 +663,8 @@ bool AutofillTable::AddFormFieldValuesTime( const size_t kMaximumUniqueNames = 256; std::set<string16> seen_names; bool result = true; - for (std::vector<FormField>::const_iterator - itr = elements.begin(); - itr != elements.end(); - itr++) { + for (std::vector<FormField>::const_iterator itr = elements.begin(); + itr != elements.end(); ++itr) { if (seen_names.size() >= kMaximumUniqueNames) break; if (seen_names.find(itr->name) != seen_names.end()) @@ -733,6 +833,11 @@ bool AutofillTable::AddFormFieldValueTime(const FormField& element, 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; diff --git a/chrome/browser/webdata/autofill_table.h b/chrome/browser/webdata/autofill_table.h index 4430f17..344154c 100644 --- a/chrome/browser/webdata/autofill_table.h +++ b/chrome/browser/webdata/autofill_table.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -154,17 +154,26 @@ class AutofillTable : public WebDatabaseTable { 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 delte_begin and delte_end. + // 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|. Removes the row from the table and sets the - // |was_removed| out parameter to true if the count becomes 0. - bool AddToCountOfFormElement(int64 pair_id, int delta, bool* was_removed); + // 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 @@ -187,6 +196,9 @@ class AutofillTable : public WebDatabaseTable { // 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); diff --git a/chrome/browser/webdata/autofill_table_unittest.cc b/chrome/browser/webdata/autofill_table_unittest.cc index 2eca221..337da0b 100644 --- a/chrome/browser/webdata/autofill_table_unittest.cc +++ b/chrome/browser/webdata/autofill_table_unittest.cc @@ -141,20 +141,25 @@ TEST_F(AutofillTableTest, Autofill) { FormField 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(db.GetAutofillTable()->AddFormFieldValue(field, &changes)); std::vector<string16> v; for (int i = 0; i < 5; i++) { field.value = ASCIIToUTF16("Clark Kent"); - EXPECT_TRUE(db.GetAutofillTable()->AddFormFieldValue(field, &changes)); + EXPECT_TRUE(db.GetAutofillTable()->AddFormFieldValueTime(field, &changes, + now + i * two_seconds)); } for (int i = 0; i < 3; i++) { field.value = ASCIIToUTF16("Clark Sutter"); - EXPECT_TRUE(db.GetAutofillTable()->AddFormFieldValue(field, &changes)); + EXPECT_TRUE(db.GetAutofillTable()->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(db.GetAutofillTable()->AddFormFieldValue(field, &changes)); + EXPECT_TRUE(db.GetAutofillTable()->AddFormFieldValueTime(field, &changes, + now + i * two_seconds)); } int count = 0; diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc index e5f76d8..e52e167 100644 --- a/chrome/browser/webdata/web_data_service.cc +++ b/chrome/browser/webdata/web_data_service.cc @@ -515,6 +515,15 @@ void WebDataService::RemoveFormElementsAddedBetween(const Time& delete_begin, this, request)); } +void WebDataService::RemoveExpiredFormElements() { + WebDataRequest* request = + new WebDataRequest(this, GetNextRequestHandle(), NULL); + RegisterRequest(request); + ScheduleTask(FROM_HERE, + Bind(&WebDataService::RemoveExpiredFormElementsImpl, + this, request)); +} + void WebDataService::RemoveFormValueForElementName( const string16& name, const string16& value) { GenericRequest2<string16, string16>* request = @@ -1258,6 +1267,29 @@ void WebDataService::RemoveFormElementsAddedBetweenImpl( request->RequestComplete(); } +void WebDataService::RemoveExpiredFormElementsImpl(WebDataRequest* request) { + InitializeDatabaseIfNecessary(); + if (db_ && !request->IsCancelled(NULL)) { + AutofillChangeList changes; + if (db_->GetAutofillTable()->RemoveExpiredFormElements(&changes)) { + if (!changes.empty()) { + request->SetResult( + new WDResult<AutofillChangeList>(AUTOFILL_CHANGES, changes)); + + // Post the notifications including the list of affected keys. + // This is sent here so that work resulting from this notification + // will be done on the DB thread, and not the UI thread. + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_AUTOFILL_ENTRIES_CHANGED, + content::Source<WebDataService>(this), + content::Details<AutofillChangeList>(&changes)); + } + ScheduleCommit(); + } + } + request->RequestComplete(); +} + void WebDataService::RemoveFormValueForElementNameImpl( GenericRequest2<string16, string16>* request) { InitializeDatabaseIfNecessary(); diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h index 9f0ac30..54f22fb 100644 --- a/chrome/browser/webdata/web_data_service.h +++ b/chrome/browser/webdata/web_data_service.h @@ -512,6 +512,7 @@ class WebDataService // Removes form elements recorded for Autocomplete from the database. void RemoveFormElementsAddedBetween(const base::Time& delete_begin, const base::Time& delete_end); + void RemoveExpiredFormElements(); void RemoveFormValueForElementName(const string16& name, const string16& value); @@ -715,6 +716,7 @@ class WebDataService const string16& name, const string16& prefix, int limit); void RemoveFormElementsAddedBetweenImpl( GenericRequest2<base::Time, base::Time>* request); + void RemoveExpiredFormElementsImpl(WebDataRequest* request); void RemoveFormValueForElementNameImpl( GenericRequest2<string16, string16>* request); void AddAutofillProfileImpl(GenericRequest<AutofillProfile>* request); |