summaryrefslogtreecommitdiffstats
path: root/webkit/glue
diff options
context:
space:
mode:
authortim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-14 00:19:48 +0000
committertim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-14 00:19:48 +0000
commit5c2ac85b5f50766b3fe31af931f1b2500e463bfd (patch)
tree147f50bef6f7fd32850d3df06280dc974ae72065 /webkit/glue
parente3a3f854c2d09b87cf1373eeaaf26cdb6decb08b (diff)
downloadchromium_src-5c2ac85b5f50766b3fe31af931f1b2500e463bfd.zip
chromium_src-5c2ac85b5f50766b3fe31af931f1b2500e463bfd.tar.gz
chromium_src-5c2ac85b5f50766b3fe31af931f1b2500e463bfd.tar.bz2
Add autofill dropdown support for password forms.
BUG=5406 Review URL: http://codereview.chromium.org/155399 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20585 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue')
-rw-r--r--webkit/glue/autocomplete_input_listener.h198
-rw-r--r--webkit/glue/editor_client_impl.cc27
-rw-r--r--webkit/glue/editor_client_impl.h9
-rw-r--r--webkit/glue/password_autocomplete_listener.cc46
-rw-r--r--webkit/glue/password_autocomplete_listener.h11
-rw-r--r--webkit/glue/password_autocomplete_listener_unittest.cc14
-rw-r--r--webkit/glue/webview_impl.cc5
7 files changed, 93 insertions, 217 deletions
diff --git a/webkit/glue/autocomplete_input_listener.h b/webkit/glue/autocomplete_input_listener.h
deleted file mode 100644
index 5f90e98..0000000
--- a/webkit/glue/autocomplete_input_listener.h
+++ /dev/null
@@ -1,198 +0,0 @@
-// 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 defines some infrastructure to handle inline autocomplete of DOM
-// input elements.
-
-#ifndef WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__
-#define WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__
-
-#include "config.h"
-#include <map>
-#include <string>
-
-#include "base/compiler_specific.h"
-
-MSVC_PUSH_WARNING_LEVEL(0);
-#include "HTMLBodyElement.h"
-#include "EventListener.h"
-#include "HTMLInputElement.h"
-MSVC_POP_WARNING();
-
-#include "base/basictypes.h"
-
-namespace WebCore {
-class AtomicString;
-class Event;
-}
-
-namespace webkit_glue {
-
-// A proxy interface to a WebCore::HTMLInputElement for inline autocomplete.
-// This class is NOT used directly by the AutocompleteInputListener but
-// is included here since it is likely most listener implementations will
-// want to interface with an HTMLInputElement (see PasswordACListener).
-// The delegate does not own the WebCore element; it only interfaces it.
-class HTMLInputDelegate {
- public:
- explicit HTMLInputDelegate(WebCore::HTMLInputElement* element);
- virtual ~HTMLInputDelegate();
-
- virtual void SetValue(const std::wstring& value);
- virtual void SetSelectionRange(size_t start, size_t end);
- virtual void OnFinishedAutocompleting();
-
- private:
- // The underlying DOM element we're wrapping. We reference the
- // underlying HTMLInputElement for its lifetime to ensure it does not get
- // freed by WebCore while in use by the delegate instance.
- WebCore::HTMLInputElement* element_;
-
- DISALLOW_COPY_AND_ASSIGN(HTMLInputDelegate);
-};
-
-
-class AutocompleteInputListener {
- public:
- virtual ~AutocompleteInputListener() { }
-
- // OnBlur: DOMFocusOutEvent occured, means one of two things.
- // 1. The user removed focus from the text field
- // either by tabbing out or clicking;
- // 2. The page is being destroyed (e.g user closed the tab)
- virtual void OnBlur(WebCore::HTMLInputElement* input_element,
- const std::wstring& user_input) = 0;
-
- // This method is called when there was a user-initiated text delta in
- // the edit field that now needs some inline autocompletion.
- // ShouldInlineAutocomplete gives the precondition for invoking this method.
- virtual void OnInlineAutocompleteNeeded(
- WebCore::HTMLInputElement* input_element,
- const std::wstring& user_input) = 0;
-};
-
-// The AutocompleteBodyListener class is a listener on the body element of a
-// page that is responsible for reporting blur (for tab/click-out) and input
-// events for form elements (registered by calling AddInputListener()).
-// This allows to have one global listener for a page, as opposed to registering
-// a listener for each form element, which impacts performances.
-// Reasons for attaching to DOM directly rather than using EditorClient API:
-// 1. Since we don't need to stop listening until the DOM node is unloaded,
-// it makes sense to use an object owned by the DOM node itself. Attaching
-// as a listener gives you this for free (nodes cleanup their listeners
-// upon destruction).
-// 2. It allows fine-grained control when the popup/down is implemented
-// in handling key events / selecting elements.
-class AutocompleteBodyListener : public WebCore::EventListener {
- public:
- // Constructs a listener for the specified frame. It listens for blur and
- // input events for elements that are registered through the AddListener
- // method.
- // The listener is ref counted (because it inherits from
- // WebCore::EventListener).
- explicit AutocompleteBodyListener(WebCore::Frame* frame);
- virtual ~AutocompleteBodyListener();
-
- // Used by unit-tests.
- AutocompleteBodyListener() { }
-
- // Adds a listener for the specified |element|.
- // This call takes ownership of the |listener|. Note that it is still OK to
- // add the same listener for different elements.
- void AddInputListener(WebCore::HTMLInputElement* element,
- AutocompleteInputListener* listener);
-
- // EventListener implementation. Code that is common to inline autocomplete,
- // such as deciding whether or not it is safe to perform it, is refactored
- // into this method and the appropriate delegate method is invoked.
- virtual void handleEvent(WebCore::Event* event, bool is_window_event);
-
- protected:
- // Protected for mocking purposes.
- virtual bool IsCaretAtEndOfText(WebCore::HTMLInputElement* element,
- size_t input_length,
- size_t previous_length) const;
-
- private:
- // Determines, based on current state (previous_text_) and user input,
- // whether or not it is a good idea to attempt inline autocomplete.
- //
- // This method is based on firefox2 code in
- // toolkit/components/autocomplete/src/nsAutoCompleteController.cpp
- // Its license block is:
- /* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Joe Hewitt <hewitt@netscape.com> (Original Author)
- * Dean Tessman <dean_tessman@hotmail.com>
- * Johnny Stenback <jst@mozilla.jstenback.com>
- * Masayuki Nakano <masayuki@d-toybox.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- // The semantics of deciding whether or not the field is in a inline-
- // autocomplete-healthy state are summarized:
- // 1. The text is not identical to the text on the previous input event.
- // 2. This is not the result of a backspace.
- // 3. The text is not empty.
- // 4. The caret is at the end of the textbox.
- // TODO(timsteele): Examine autocomplete_edit.cc in the browser/ code and
- // make sure to capture all common exclusion cases here.
- bool ShouldInlineAutocomplete(WebCore::HTMLInputElement* input,
- const std::wstring& old_text,
- const std::wstring& new_text);
-
- // The data we store for each registered listener.
- struct InputElementInfo {
- InputElementInfo() : listener(NULL) { }
- AutocompleteInputListener* listener;
- std::wstring previous_text;
- };
-
- struct CmpRefPtrs {
- bool operator()(const RefPtr<WebCore::HTMLInputElement>& a,
- const RefPtr<WebCore::HTMLInputElement>& b) const {
- return a.get() < b.get();
- }
- };
-
- typedef std::map<RefPtr<WebCore::HTMLInputElement>, InputElementInfo,
- CmpRefPtrs> InputElementInfoMap;
- InputElementInfoMap elements_info_;
-
- DISALLOW_COPY_AND_ASSIGN(AutocompleteBodyListener);
-};
-
-} // webkit_glue
-
-#endif // WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__
diff --git a/webkit/glue/editor_client_impl.cc b/webkit/glue/editor_client_impl.cc
index 07d1b5c..5094545 100644
--- a/webkit/glue/editor_client_impl.cc
+++ b/webkit/glue/editor_client_impl.cc
@@ -73,7 +73,7 @@ EditorClientImpl::EditorClientImpl(WebView* web_view)
: web_view_(static_cast<WebViewImpl*>(web_view)),
use_editor_delegate_(false),
in_redo_(false),
- backspace_pressed_(false),
+ backspace_or_delete_pressed_(false),
spell_check_this_field_status_(SPELLCHECK_AUTOMATIC),
// Don't complain about using "this" in initializer list.
MSVC_PUSH_DISABLE_WARNING(4355)
@@ -749,7 +749,7 @@ bool EditorClientImpl::Autofill(WebCore::HTMLInputElement* input_element,
if (!requires_caret_at_end) {
DoAutofill(input_element, form_autofill_only, autofill_on_empty_value,
- false, backspace_pressed_);
+ false, backspace_or_delete_pressed_);
} else {
// We post a task for doing the autofill as the caret position is not set
// properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and
@@ -763,7 +763,7 @@ bool EditorClientImpl::Autofill(WebCore::HTMLInputElement* input_element,
form_autofill_only,
autofill_on_empty_value,
true,
- backspace_pressed_));
+ backspace_or_delete_pressed_));
}
return true;
}
@@ -797,10 +797,7 @@ void EditorClientImpl::DoAutofill(WebCore::HTMLInputElement* input_element,
if (form_autofill_only)
return;
- if (backspace) // No autocomplete for password on backspace.
- return;
-
- listener->OnInlineAutocompleteNeeded(input_element, value);
+ listener->OnInlineAutocompleteNeeded(input_element, value, backspace, true);
return;
}
@@ -811,6 +808,19 @@ void EditorClientImpl::DoAutofill(WebCore::HTMLInputElement* input_element,
reinterpret_cast<int64>(input_element));
}
+void EditorClientImpl::OnAutofillSuggestionAccepted(
+ WebCore::HTMLInputElement* text_field) {
+ WebFrameImpl* webframe =
+ WebFrameImpl::FromFrame(text_field->document()->frame());
+ webkit_glue::PasswordAutocompleteListener* listener =
+ webframe->GetPasswordListener(text_field);
+ std::wstring value = webkit_glue::StringToStdWString(text_field->value());
+ // Password listeners need to autocomplete other fields that depend on the
+ // input element with autofill suggestions.
+ if (listener)
+ listener->OnInlineAutocompleteNeeded(text_field, value, false, false);
+}
+
bool EditorClientImpl::doTextFieldCommandFromEvent(
WebCore::Element* element,
WebCore::KeyboardEvent* event) {
@@ -818,7 +828,8 @@ bool EditorClientImpl::doTextFieldCommandFromEvent(
// find if backspace was pressed from textFieldDidBeginEditing and
// textDidChangeInTextField as when these methods are called the value of the
// input element already contains the type character.
- backspace_pressed_ = (event->keyCode() == WebCore::VKEY_BACK);
+ backspace_or_delete_pressed_ = (event->keyCode() == WebCore::VKEY_BACK) ||
+ (event->keyCode() == WebCore::VKEY_DELETE);
// The Mac code appears to use this method as a hook to implement special
// keyboard commands specific to Safari's auto-fill implementation. We
diff --git a/webkit/glue/editor_client_impl.h b/webkit/glue/editor_client_impl.h
index 4bacad2a..ad133a7 100644
--- a/webkit/glue/editor_client_impl.h
+++ b/webkit/glue/editor_client_impl.h
@@ -124,6 +124,13 @@ class EditorClientImpl : public WebCore::EditorClient {
// otherwise.
virtual bool ShowFormAutofillForNode(WebCore::Node* node);
+ // Notification that the text changed in |text_field| due to acceptance of
+ // a suggestion provided by an autofill popup. Having a separate callback
+ // in this case is a simple way to break the cycle that would otherwise occur
+ // if textDidChangeInTextField was called.
+ virtual void OnAutofillSuggestionAccepted(
+ WebCore::HTMLInputElement* text_field);
+
private:
void ModifySelection(WebCore::Frame* frame,
WebCore::KeyboardEvent* event);
@@ -173,7 +180,7 @@ class EditorClientImpl : public WebCore::EditorClient {
bool ShouldSpellcheckByDefault();
// Whether the last entered key was a backspace.
- bool backspace_pressed_;
+ bool backspace_or_delete_pressed_;
// This flag is set to false if spell check for this editor is manually
// turned off. The default setting is SPELLCHECK_AUTOMATIC.
diff --git a/webkit/glue/password_autocomplete_listener.cc b/webkit/glue/password_autocomplete_listener.cc
index 1b4260c..80e51a4 100644
--- a/webkit/glue/password_autocomplete_listener.cc
+++ b/webkit/glue/password_autocomplete_listener.cc
@@ -8,7 +8,10 @@
#include "webkit/glue/password_autocomplete_listener.h"
#undef LOG
#include "base/logging.h"
+#include "base/string_util.h"
#include "webkit/glue/glue_util.h"
+#include "webkit/glue/webframe_impl.h"
+#include "webkit/glue/webview_impl.h"
namespace webkit_glue {
@@ -41,6 +44,19 @@ void HTMLInputDelegate::OnFinishedAutocompleting() {
element_->dispatchFormControlChangeEvent();
}
+void HTMLInputDelegate::RefreshAutofillPopup(
+ const std::vector<std::wstring>& suggestions,
+ int default_suggestion_index) {
+ WebFrameImpl* webframe =
+ WebFrameImpl::FromFrame(element_->document()->frame());
+ WebViewImpl* webview = webframe->GetWebViewImpl();
+ if (!webview)
+ return;
+
+ int64 node_id = reinterpret_cast<int64>(element_);
+ webview->AutofillSuggestionsForNode(node_id, suggestions, 0);
+}
+
PasswordAutocompleteListener::PasswordAutocompleteListener(
HTMLInputDelegate* username_delegate,
HTMLInputDelegate* password_delegate,
@@ -71,12 +87,24 @@ void PasswordAutocompleteListener::OnBlur(WebCore::HTMLInputElement* element,
void PasswordAutocompleteListener::OnInlineAutocompleteNeeded(
WebCore::HTMLInputElement* element,
- const std::wstring& user_input) {
+ const std::wstring& user_input,
+ bool backspace_or_delete,
+ bool with_suggestion_popup) {
// 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;
+
+ if (with_suggestion_popup) {
+ std::vector<std::wstring> suggestions;
+ GetSuggestions(user_input, &suggestions);
+ username_delegate_->RefreshAutofillPopup(suggestions, 0);
+ }
+
+ if (backspace_or_delete)
+ 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
@@ -103,7 +131,7 @@ void PasswordAutocompleteListener::OnInlineAutocompleteNeeded(
bool PasswordAutocompleteListener::TryToMatch(const std::wstring& input,
const std::wstring& username,
const std::wstring& password) {
- if (input.compare(0, input.length(), username, 0, input.length()) != 0)
+ if (!StartsWith(username, input, false))
return false;
// Input matches the username, fill in required values.
@@ -115,4 +143,18 @@ bool PasswordAutocompleteListener::TryToMatch(const std::wstring& input,
return true;
}
+void PasswordAutocompleteListener::GetSuggestions(
+ const std::wstring& input, std::vector<std::wstring>* suggestions) {
+ if (StartsWith(data_.basic_data.values[0], input, false))
+ suggestions->push_back(data_.basic_data.values[0]);
+
+ for (PasswordFormDomManager::LoginCollection::iterator it =
+ data_.additional_logins.begin();
+ it != data_.additional_logins.end();
+ ++it) {
+ if (StartsWith(it->first, input, false))
+ suggestions->push_back(it->first);
+ }
+}
+
} // webkit_glue
diff --git a/webkit/glue/password_autocomplete_listener.h b/webkit/glue/password_autocomplete_listener.h
index 9666397..c56c0189 100644
--- a/webkit/glue/password_autocomplete_listener.h
+++ b/webkit/glue/password_autocomplete_listener.h
@@ -32,6 +32,9 @@ class HTMLInputDelegate {
virtual void SetValue(const std::wstring& value);
virtual void SetSelectionRange(size_t start, size_t end);
virtual void OnFinishedAutocompleting();
+ virtual void RefreshAutofillPopup(
+ const std::vector<std::wstring>& suggestions,
+ int default_suggestion_index);
private:
// The underlying DOM element we're wrapping. We reference the underlying
@@ -54,7 +57,9 @@ class PasswordAutocompleteListener {
virtual void OnBlur(WebCore::HTMLInputElement* element,
const std::wstring& user_input);
virtual void OnInlineAutocompleteNeeded(WebCore::HTMLInputElement* element,
- const std::wstring& user_input);
+ const std::wstring& user_input,
+ bool backspace_or_delete,
+ bool with_suggestion_popup);
private:
// Check if the input string resembles a potential matching login
@@ -64,6 +69,10 @@ class PasswordAutocompleteListener {
const std::wstring& username,
const std::wstring& password);
+ // Scan |data_| for prefix matches of |input| and add each to |suggestions|.
+ void GetSuggestions(const std::wstring& input,
+ std::vector<std::wstring>* suggestions);
+
// Access to password field to autocomplete on blur/username updates.
scoped_ptr<HTMLInputDelegate> password_delegate_;
scoped_ptr<HTMLInputDelegate> username_delegate_;
diff --git a/webkit/glue/password_autocomplete_listener_unittest.cc b/webkit/glue/password_autocomplete_listener_unittest.cc
index 05337d9..3a12047 100644
--- a/webkit/glue/password_autocomplete_listener_unittest.cc
+++ b/webkit/glue/password_autocomplete_listener_unittest.cc
@@ -153,7 +153,7 @@ TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
password_delegate->SetValue(std::wstring());
// Simulate the user typing in the first letter of 'alice', a stored username.
- listener->OnInlineAutocompleteNeeded(NULL, L"a");
+ listener->OnInlineAutocompleteNeeded(NULL, L"a", false, false);
// Both the username and password delegates should reflect selection
// of the stored login.
EXPECT_EQ(username1_, username_delegate->value());
@@ -166,7 +166,7 @@ TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
EXPECT_TRUE(password_delegate->did_call_on_finish());
// Now the user types the next letter of the same username, 'l'.
- listener->OnInlineAutocompleteNeeded(NULL, L"al");
+ listener->OnInlineAutocompleteNeeded(NULL, L"al", false, false);
// Now the fields should have the same value, but the selection should have a
// different start value.
EXPECT_EQ(username1_, username_delegate->value());
@@ -186,7 +186,7 @@ TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
// was invoked during OnInlineAutocompleteNeeded.
username_delegate->ResetTestState();
password_delegate->ResetTestState();
- listener->OnInlineAutocompleteNeeded(NULL, L"alf");
+ listener->OnInlineAutocompleteNeeded(NULL, L"alf", false, false);
EXPECT_FALSE(username_delegate->did_set_selection());
EXPECT_FALSE(username_delegate->did_set_value());
EXPECT_FALSE(username_delegate->did_call_on_finish());
@@ -194,7 +194,7 @@ TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
EXPECT_FALSE(password_delegate->did_call_on_finish());
// Ok, so now the user removes all the text and enters the letter 'b'.
- listener->OnInlineAutocompleteNeeded(NULL, L"b");
+ listener->OnInlineAutocompleteNeeded(NULL, L"b", false, false);
// The username and password fields should match the 'bob' entry.
EXPECT_EQ(username2_, username_delegate->value());
EXPECT_EQ(password2_, password_delegate->value());
@@ -220,13 +220,13 @@ TEST_F(PasswordManagerAutocompleteTests, TestWaitUsername) {
// never modify it when wait_for_username is true; only the user can by
// typing into (in real life) the HTMLInputElement.
password_delegate->SetValue(std::wstring());
- listener->OnInlineAutocompleteNeeded(NULL, L"a");
+ listener->OnInlineAutocompleteNeeded(NULL, L"a", false, false);
EXPECT_EQ(empty, username_delegate->value());
EXPECT_EQ(empty, password_delegate->value());
- listener->OnInlineAutocompleteNeeded(NULL, L"al");
+ listener->OnInlineAutocompleteNeeded(NULL, L"al", false, false);
EXPECT_EQ(empty, username_delegate->value());
EXPECT_EQ(empty, password_delegate->value());
- listener->OnInlineAutocompleteNeeded(NULL, L"alice");
+ listener->OnInlineAutocompleteNeeded(NULL, L"alice", false, false);
EXPECT_EQ(empty, username_delegate->value());
EXPECT_EQ(empty, password_delegate->value());
diff --git a/webkit/glue/webview_impl.cc b/webkit/glue/webview_impl.cc
index 250896e..fe9348d 100644
--- a/webkit/glue/webview_impl.cc
+++ b/webkit/glue/webview_impl.cc
@@ -182,6 +182,11 @@ class AutocompletePopupMenuClient : public WebCore::PopupMenuClient {
// WebCore::PopupMenuClient implementation.
virtual void valueChanged(unsigned listIndex, bool fireEvents = true) {
text_field_->setValue(suggestions_[listIndex]);
+ EditorClientImpl* editor =
+ static_cast<EditorClientImpl*>(webview_->page()->editorClient());
+ DCHECK(editor);
+ editor->OnAutofillSuggestionAccepted(
+ static_cast<WebCore::HTMLInputElement*>(text_field_.get()));
}
virtual WebCore::String itemText(unsigned list_index) const {