blob: b2c4d3c5f7239d69c3628235fe42ef8865383276 [file] [log] [blame]
// Copyright (c) 2010 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/autocomplete_history_manager.h"
#include <vector>
#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/pref_names.h"
#include "webkit/glue/form_data.h"
using webkit_glue::FormData;
namespace {
// Limit on the number of suggestions to appear in the pop-up menu under an
// text input element in a form.
const int kMaxAutocompleteMenuItems = 6;
// The separator characters for SSNs.
const string16 kSSNSeparators = ASCIIToUTF16(" -");
bool IsSSN(const string16& text) {
string16 number_string;
RemoveChars(text, kSSNSeparators.c_str(), &number_string);
if (number_string.length() != 9 || !IsStringASCII(number_string))
return false;
// A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S =
// serial number). The validation we do here is simply checking if the area,
// group, and serial numbers are valid. It is possible to check if the group
// number is valid for the given area, but that data changes all the time.
//
// See: http://www.socialsecurity.gov/history/ssn/geocard.html
// http://www.socialsecurity.gov/employer/stateweb.htm
// http://www.socialsecurity.gov/employer/ssnvhighgroup.htm
string16 area_string = number_string.substr(0, 3);
string16 group_string = number_string.substr(3, 2);
string16 serial_string = number_string.substr(5, 4);
int area;
if (!base::StringToInt(area_string, &area))
return false;
if (area < 1 ||
area == 666 ||
(area > 733 && area < 750) ||
area > 772)
return false;
int group;
if (!base::StringToInt(group_string, &group) || group == 0)
return false;
int serial;
if (!base::StringToInt(serial_string, &serial) || serial == 0)
return false;
return true;
}
} // namespace
AutocompleteHistoryManager::AutocompleteHistoryManager(
TabContents* tab_contents) : tab_contents_(tab_contents),
pending_query_handle_(0),
query_id_(0) {
DCHECK(tab_contents);
profile_ = tab_contents_->profile();
DCHECK(profile_);
web_data_service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);
DCHECK(web_data_service_);
autofill_enabled_.Init(prefs::kAutoFillEnabled, profile_->GetPrefs(), NULL);
}
AutocompleteHistoryManager::~AutocompleteHistoryManager() {
CancelPendingQuery();
}
void AutocompleteHistoryManager::FormSubmitted(const FormData& form) {
StoreFormEntriesInWebDatabase(form);
}
bool AutocompleteHistoryManager::GetAutocompleteSuggestions(
int query_id, const string16& name, const string16& prefix) {
if (!*autofill_enabled_)
return false;
CancelPendingQuery();
query_id_ = query_id;
pending_query_handle_ = web_data_service_->GetFormValuesForElementName(
name, prefix, kMaxAutocompleteMenuItems, this);
return true;
}
void AutocompleteHistoryManager::RemoveAutocompleteEntry(
const string16& name, const string16& value) {
web_data_service_->RemoveFormValueForElementName(name, value);
}
void AutocompleteHistoryManager::OnWebDataServiceRequestDone(
WebDataService::Handle h,
const WDTypedResult* result) {
DCHECK(pending_query_handle_);
pending_query_handle_ = 0;
if (*autofill_enabled_) {
DCHECK(result);
SendSuggestions(result);
} else {
SendSuggestions(NULL);
}
}
AutocompleteHistoryManager::AutocompleteHistoryManager(
Profile* profile, WebDataService* wds) : tab_contents_(NULL),
profile_(profile),
web_data_service_(wds),
pending_query_handle_(0),
query_id_(0) {
autofill_enabled_.Init(
prefs::kAutoFillEnabled, profile_->GetPrefs(), NULL);
}
void AutocompleteHistoryManager::CancelPendingQuery() {
if (pending_query_handle_) {
SendSuggestions(NULL);
web_data_service_->CancelRequest(pending_query_handle_);
}
pending_query_handle_ = 0;
}
void AutocompleteHistoryManager::StoreFormEntriesInWebDatabase(
const FormData& form) {
if (!*autofill_enabled_)
return;
if (profile_->IsOffTheRecord())
return;
// Don't save data that was submitted through JavaScript.
if (!form.user_submitted)
return;
// We put the following restriction on stored FormFields:
// - non-empty name
// - non-empty value
// - text field
// - value is not a credit card number
// - value is not a SSN
std::vector<webkit_glue::FormField> values;
for (std::vector<webkit_glue::FormField>::const_iterator iter =
form.fields.begin();
iter != form.fields.end(); ++iter) {
if (!iter->value().empty() &&
!iter->name().empty() &&
iter->form_control_type() == ASCIIToUTF16("text") &&
!CreditCard::IsCreditCardNumber(iter->value()) &&
!IsSSN(iter->value()))
values.push_back(*iter);
}
if (!values.empty())
web_data_service_->AddFormFields(values);
}
void AutocompleteHistoryManager::SendSuggestions(const WDTypedResult* result) {
RenderViewHost* host = tab_contents_->render_view_host();
if (!host)
return;
if (result) {
DCHECK(result->GetType() == AUTOFILL_VALUE_RESULT);
const WDResult<std::vector<string16> >* autofill_result =
static_cast<const WDResult<std::vector<string16> >*>(result);
host->AutocompleteSuggestionsReturned(
query_id_, autofill_result->GetValue());
} else {
host->AutocompleteSuggestionsReturned(
query_id_, std::vector<string16>());
}
}