blob: fc15af090a519a44d31c3e19009467baa1f0e776 [file] [log] [blame]
// Copyright (c) 2006-2008 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.
//
// This file provides the implementaiton of the password manager's autocomplete
// component.
#include "webkit/glue/webpasswordautocompletelistener_impl.h"
#include <vector>
#include "base/string_util.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
using WebKit::WebFrame;
using WebKit::WebView;
namespace webkit_glue {
WebInputElementDelegate::WebInputElementDelegate() {
}
WebInputElementDelegate::WebInputElementDelegate(const WebInputElement& element)
: element_(element) {
}
WebInputElementDelegate::~WebInputElementDelegate() {
}
bool WebInputElementDelegate::IsEditable() const {
return element_.isEnabledFormControl() && !element_.hasAttribute("readonly");
}
void WebInputElementDelegate::SetValue(const string16& value) {
element_.setValue(value);
}
bool WebInputElementDelegate::IsAutofilled() const {
return element_.isAutofilled();
}
void WebInputElementDelegate::SetAutofilled(bool autofilled) {
if (element_.isAutofilled() == autofilled)
return;
element_.setAutofilled(autofilled);
// Notify any changeEvent listeners.
element_.dispatchFormControlChangeEvent();
}
void WebInputElementDelegate::SetSelectionRange(size_t start, size_t end) {
element_.setSelectionRange(start, end);
}
void WebInputElementDelegate::RefreshAutofillPopup(
const std::vector<string16>& suggestions) {
WebView* webview = element_.document().frame()->view();
if (webview) {
std::vector<string16> names;
std::vector<string16> labels;
std::vector<string16> icons;
std::vector<int> unique_ids;
for (size_t i = 0; i < suggestions.size(); ++i) {
names.push_back(suggestions[i]);
labels.push_back(string16());
icons.push_back(string16());
unique_ids.push_back(0);
}
webview->applyAutoFillSuggestions(
element_, names, labels, icons, unique_ids, -1);
}
}
WebPasswordAutocompleteListenerImpl::WebPasswordAutocompleteListenerImpl(
WebInputElementDelegate* username_delegate,
WebInputElementDelegate* password_delegate,
const PasswordFormFillData& data)
: password_delegate_(password_delegate),
username_delegate_(username_delegate),
data_(data) {
}
WebPasswordAutocompleteListenerImpl::~WebPasswordAutocompleteListenerImpl() {
}
void WebPasswordAutocompleteListenerImpl::didBlurInputElement(
const WebString& user_input) {
// If this listener exists, its because the password manager had more than
// one match for the password form, which implies it had at least one
// [preferred] username/password pair.
// DCHECK(data_.basic_data.values.size() == 2);
if (!password_delegate_->IsEditable())
return;
string16 user_input16 = user_input;
// If enabled, set the password field to match the current username.
if (data_.basic_data.fields[0].value() == user_input16) {
// Preferred username/login is selected.
password_delegate_->SetValue(data_.basic_data.fields[1].value());
password_delegate_->SetAutofilled(true);
} else if (data_.additional_logins.find(user_input16) !=
data_.additional_logins.end()) {
// One of the extra username/logins is selected.
password_delegate_->SetValue(data_.additional_logins[user_input16]);
password_delegate_->SetAutofilled(true);
}
}
void WebPasswordAutocompleteListenerImpl::performInlineAutocomplete(
const WebString& user_input,
bool backspace_or_delete_pressed,
bool show_suggestions) {
// If wait_for_username is true, we only autofill the password when the
// username field is blurred (i.e not inline) with a matching username string
// entered.
if (data_.wait_for_username)
return;
string16 user_input16 = user_input;
// The input text is being changed, so any autofilled password is now
// outdated.
username_delegate_->SetAutofilled(false);
if (password_delegate_->IsAutofilled()) {
password_delegate_->SetValue(string16());
password_delegate_->SetAutofilled(false);
}
if (show_suggestions)
showSuggestionPopup(user_input16);
if (backspace_or_delete_pressed)
return; // Don't inline autocomplete when the user deleted something.
// Look for any suitable matches to current field text.
// TODO(timsteele): The preferred login (in basic_data.values) and additional
// logins could be bundled into the same data structure (possibly even as
// WebCore strings) upon construction of the PasswordAutocompleteListenerImpl
// to simplify lookup and save string conversions (see SetValue) on each
// successful call to OnInlineAutocompleteNeeded.
if (TryToMatch(user_input16,
data_.basic_data.fields[0].value(),
data_.basic_data.fields[1].value())) {
return;
}
// Scan additional logins for a match.
for (PasswordFormFillData::LoginCollection::iterator it =
data_.additional_logins.begin();
it != data_.additional_logins.end();
++it) {
if (TryToMatch(user_input16, it->first, it->second))
return;
}
}
bool WebPasswordAutocompleteListenerImpl::showSuggestionPopup(
const WebString& value) {
std::vector<string16> suggestions;
GetSuggestions(value, &suggestions);
if (suggestions.empty())
return false;
username_delegate_->RefreshAutofillPopup(suggestions);
return true;
}
bool WebPasswordAutocompleteListenerImpl::TryToMatch(const string16& input,
const string16& username,
const string16& password) {
if (!StartsWith(username, input, false))
return false;
// Input matches the username, fill in required values.
username_delegate_->SetValue(username);
username_delegate_->SetSelectionRange(input.length(), username.length());
username_delegate_->SetAutofilled(true);
if (password_delegate_->IsEditable())
password_delegate_->SetValue(password);
password_delegate_->SetAutofilled(true);
return true;
}
void WebPasswordAutocompleteListenerImpl::GetSuggestions(
const string16& input, std::vector<string16>* suggestions) {
if (StartsWith(data_.basic_data.fields[0].value(), input, false))
suggestions->push_back(data_.basic_data.fields[0].value());
for (PasswordFormFillData::LoginCollection::iterator it =
data_.additional_logins.begin();
it != data_.additional_logins.end();
++it) {
if (StartsWith(it->first, input, false))
suggestions->push_back(it->first);
}
}
} // namespace webkit_glue