summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/autofill/personal_data_manager.cc14
-rw-r--r--chrome/browser/sync/profile_sync_service_autofill_unittest.cc21
-rw-r--r--chrome/browser/webdata/autocomplete_syncable_service.cc157
-rw-r--r--chrome/browser/webdata/autocomplete_syncable_service.h12
-rw-r--r--chrome/browser/webdata/autofill_entry.cc43
-rw-r--r--chrome/browser/webdata/autofill_entry.h19
-rw-r--r--chrome/browser/webdata/autofill_entry_unittest.cc52
-rw-r--r--chrome/browser/webdata/autofill_table.cc137
-rw-r--r--chrome/browser/webdata/autofill_table.h24
-rw-r--r--chrome/browser/webdata/autofill_table_unittest.cc11
-rw-r--r--chrome/browser/webdata/web_data_service.cc32
-rw-r--r--chrome/browser/webdata/web_data_service.h2
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(), &timestamps);
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);