summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-17 22:04:00 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-11-17 22:04:00 +0000
commit5f9b16e96cc1bd69df3aa064233f49573da74274 (patch)
treea0151ba7058b487df02f965d1aff4ea8e7c96007 /webkit
parent019d50c3d479f5cf48934dc454c7f9f824bb4a1b (diff)
downloadchromium_src-5f9b16e96cc1bd69df3aa064233f49573da74274.zip
chromium_src-5f9b16e96cc1bd69df3aa064233f49573da74274.tar.gz
chromium_src-5f9b16e96cc1bd69df3aa064233f49573da74274.tar.bz2
The password manager and the form autofill feature were listening for events on text input elements by registering a listener on each element. This is slowing down the page load.
This CL changes that to only 1 listener on the body of the page, that forwards the events to element specific listeners. BUG=None TEST=Run the test shell unit tests Review URL: http://codereview.chromium.org/10844 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5578 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r--webkit/glue/autocomplete_input_listener.cc147
-rw-r--r--webkit/glue/autocomplete_input_listener.h168
-rw-r--r--webkit/glue/autocomplete_input_listener_unittest.cc194
-rw-r--r--webkit/glue/dom_operations.cc15
-rw-r--r--webkit/glue/form_autocomplete_listener.cc16
-rw-r--r--webkit/glue/form_autocomplete_listener.h16
-rw-r--r--webkit/glue/password_autocomplete_listener.cc16
-rw-r--r--webkit/glue/password_autocomplete_listener.h17
-rw-r--r--webkit/glue/password_autocomplete_listener_unittest.cc101
-rw-r--r--webkit/glue/webframe_impl.cc15
-rw-r--r--webkit/glue/webframe_impl.h13
-rw-r--r--webkit/glue/webframeloaderclient_impl.cc15
12 files changed, 407 insertions, 326 deletions
diff --git a/webkit/glue/autocomplete_input_listener.cc b/webkit/glue/autocomplete_input_listener.cc
index 25af5e9..0aceb02 100644
--- a/webkit/glue/autocomplete_input_listener.cc
+++ b/webkit/glue/autocomplete_input_listener.cc
@@ -6,6 +6,7 @@
// infrastructure defined in autocomplete_input_listener.h.
#include "webkit/glue/autocomplete_input_listener.h"
+#include <set>
MSVC_PUSH_WARNING_LEVEL(0);
#include "HTMLInputElement.h"
@@ -15,6 +16,7 @@ MSVC_PUSH_WARNING_LEVEL(0);
#include "Editor.h"
#include "EventNames.h"
#include "Event.h"
+#include "HTMLNames.h"
MSVC_POP_WARNING();
#undef LOG
@@ -50,36 +52,10 @@ HTMLInputDelegate::~HTMLInputDelegate() {
element_->deref();
}
-bool HTMLInputDelegate::IsCaretAtEndOfText(size_t input_length,
- size_t previous_length) const {
- // Hack 2 of 2 for http://bugs.webkit.org/show_bug.cgi?id=16976.
- // TODO(timsteele): This check should only return early if
- // !(selectionEnd == selectionStart == user_input.length()).
- // However, because of webkit bug #16976 the caret is not properly moved
- // until after the handlers have executed, so for now we do the following
- // several checks. The first check handles the case webkit sets the End
- // selection but not the Start selection correctly, and the second is for
- // when webcore sets neither. This won't be perfect if the user moves the
- // selection around during inline autocomplete, but for now its the
- // friendliest behavior we can offer. Once the bug is fixed this method
- // should no longer need the previous_length parameter.
- if (((element_->selectionEnd() != element_->selectionStart() + 1) ||
- (element_->selectionEnd() != static_cast<int>(input_length))) &&
- ((element_->selectionEnd() != element_->selectionStart()) ||
- (element_->selectionEnd() != static_cast<int>(previous_length)))) {
- return false;
- }
- return true;
-}
-
void HTMLInputDelegate::SetValue(const std::wstring& value) {
element_->setValue(StdWStringToString(value));
}
-std::wstring HTMLInputDelegate::GetValue() const {
- return StringToStdWString(element_->value());
-}
-
void HTMLInputDelegate::SetSelectionRange(size_t start, size_t end) {
element_->setSelectionRange(start, end);
// Hack, see comments for PreserveSelection().
@@ -94,11 +70,34 @@ void HTMLInputDelegate::OnFinishedAutocompleting() {
element_->onChange();
}
-AutocompleteInputListener::AutocompleteInputListener(
- AutocompleteEditDelegate* edit_delegate)
- : edit_delegate_(edit_delegate) {
- previous_text_ = edit_delegate->GetValue();
+AutocompleteBodyListener::AutocompleteBodyListener(WebCore::Frame* frame) {
+ WebCore::HTMLElement* body = frame->document()->body();
+ body->addEventListener(WebCore::eventNames().DOMFocusOutEvent, this, false);
+ body->addEventListener(WebCore::eventNames().inputEvent, this, false);
+ // Attaching to the WebCore body element effectively transfers ownership of
+ // the listener object. When WebCore is tearing down the document, any
+ // attached listeners are destroyed.
+ // See Document::removeAllEventListenersFromAllNodes which is called by
+ // FrameLoader::stopLoading. Also, there is no need for matching calls to
+ // removeEventListener because the simplest and most convienient thing to do
+ // for autocompletion is to stop listening once the element is destroyed.
+}
+
+AutocompleteBodyListener::~AutocompleteBodyListener() {
+ // Delete the listener. Pay special attention since we may have the same
+ // listener registered for several elements.
+ std::set<AutocompleteInputListener*> to_be_deleted_;
+ for (InputElementInfoMap::iterator iter = elements_info_.begin();
+ iter != elements_info_.end(); ++iter) {
+ to_be_deleted_.insert(iter->second.listener);
+ }
+
+ std::set<AutocompleteInputListener*>::iterator iter;
+ for (iter = to_be_deleted_.begin(); iter != to_be_deleted_.end(); ++iter)
+ delete *iter;
+ elements_info_.clear();
}
+
// The following method is based on Firefox2 code in
// toolkit/components/autocomplete/src/nsAutoCompleteController.cpp
// Its license block is
@@ -142,57 +141,91 @@ AutocompleteInputListener::AutocompleteInputListener(
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
-bool AutocompleteInputListener::ShouldInlineAutocomplete(
- const std::wstring& user_input) {
- size_t prev_length = previous_text_.length();
+bool AutocompleteBodyListener::ShouldInlineAutocomplete(
+ WebCore::HTMLInputElement* input,
+ const std::wstring& old_text,
+ const std::wstring& new_text) {
+ size_t prev_length = old_text.length();
// The following are a bunch of early returns in cases we don't want to
// go through with inline autocomplete.
// Don't bother doing AC if nothing changed.
- if (user_input.length() > 0 && (user_input == previous_text_))
+ if (new_text.length() > 0 && (new_text == old_text))
return false;
// Did user backspace?
- if ((user_input.length() < previous_text_.length()) &&
- previous_text_.substr(0, user_input.length()) == user_input) {
- previous_text_ = user_input;
+ if ((new_text.length() < old_text.length()) &&
+ old_text.substr(0, new_text.length()) == new_text) {
return false;
}
- // Remember the current input.
- previous_text_ = user_input;
-
// Is search string empty?
- if (user_input.empty())
+ if (new_text.empty())
return false;
- return edit_delegate_->IsCaretAtEndOfText(user_input.length(), prev_length);
+ return IsCaretAtEndOfText(input, new_text.length(), prev_length);
}
-void AutocompleteInputListener::handleEvent(WebCore::Event* event,
- bool /*is_window_event*/) {
+void AutocompleteBodyListener::handleEvent(WebCore::Event* event,
+ bool /*is_window_event*/) {
const WebCore::AtomicString& webcore_type = event->type();
- const std::wstring& user_input = edit_delegate_->GetValue();
+ DCHECK(event->target()->toNode());
+ if (!event->target()->toNode()->hasTagName(WebCore::HTMLNames::inputTag))
+ return; // Not a node of interest to us.
+
+ WebCore::HTMLInputElement* input =
+ static_cast<WebCore::HTMLInputElement*>(event->target()->toNode());
+ InputElementInfoMap::const_iterator iter = elements_info_.find(input);
+ if (iter == elements_info_.end())
+ return; // Not an input node we are listening to.
+
+ InputElementInfo input_info = iter->second;
+ const std::wstring& user_input = StringToStdWString(input->value());
if (webcore_type == WebCore::eventNames().DOMFocusOutEvent) {
- OnBlur(user_input);
+ input_info.listener->OnBlur(input, user_input);
} else if (webcore_type == WebCore::eventNames().inputEvent) {
// Perform inline autocomplete if it is safe to do so.
- if (ShouldInlineAutocomplete(user_input))
- OnInlineAutocompleteNeeded(user_input);
+ if (ShouldInlineAutocomplete(input,
+ input_info.previous_text, user_input)) {
+ input_info.listener->OnInlineAutocompleteNeeded(input, user_input);
+ }
+ // Update the info.
+ input_info.previous_text = user_input;
+ elements_info_[input] = input_info;
} else {
NOTREACHED() << "unexpected EventName for autocomplete listener";
}
}
-void AttachForInlineAutocomplete(
- WebCore::HTMLInputElement* target,
+void AutocompleteBodyListener::AddInputListener(
+ WebCore::HTMLInputElement* element,
AutocompleteInputListener* listener) {
- target->addEventListener(WebCore::eventNames().DOMFocusOutEvent,
- listener,
- false);
- target->addEventListener(WebCore::eventNames().inputEvent,
- listener,
- false);
+ DCHECK(elements_info_.find(element) == elements_info_.end());
+ InputElementInfo elem_info;
+ elem_info.listener = listener;
+ elements_info_[element] = elem_info;
}
+bool AutocompleteBodyListener::IsCaretAtEndOfText(
+ WebCore::HTMLInputElement* element,
+ size_t input_length,
+ size_t previous_length) const {
+ // Hack 2 of 2 for http://bugs.webkit.org/show_bug.cgi?id=16976.
+ // TODO(timsteele): This check should only return early if
+ // !(selectionEnd == selectionStart == user_input.length()).
+ // However, because of webkit bug #16976 the caret is not properly moved
+ // until after the handlers have executed, so for now we do the following
+ // several checks. The first check handles the case webkit sets the End
+ // selection but not the Start selection correctly, and the second is for
+ // when webcore sets neither. This won't be perfect if the user moves the
+ // selection around during inline autocomplete, but for now its the
+ // friendliest behavior we can offer. Once the bug is fixed this method
+ // should no longer need the previous_length parameter.
+ if (((element->selectionEnd() != element->selectionStart() + 1) ||
+ (element->selectionEnd() != static_cast<int>(input_length))) &&
+ ((element->selectionEnd() != element->selectionStart()) ||
+ (element->selectionEnd() != static_cast<int>(previous_length)))) {
+ return false;
+ }
+ return true;
+}
} // webkit_glue
-
diff --git a/webkit/glue/autocomplete_input_listener.h b/webkit/glue/autocomplete_input_listener.h
index fe412f1..b89b698 100644
--- a/webkit/glue/autocomplete_input_listener.h
+++ b/webkit/glue/autocomplete_input_listener.h
@@ -9,12 +9,15 @@
#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"
@@ -23,65 +26,58 @@ MSVC_POP_WARNING();
namespace WebCore {
class AtomicString;
class Event;
-class HTMLInputElement;
}
namespace webkit_glue {
-// This interface exposes all required functionality to perform inline
-// autocomplete on an edit field.
-class AutocompleteEditDelegate {
- public:
- // Virtual destructor so it is safe to delete through AutocompleteEditDelegate
- // pointers.
- virtual ~AutocompleteEditDelegate() {
- }
-
- // Whether or not the caret/selection is at the end of input.
- // input_length gives the length of the user-typed input.
- // previous_length is the length of the previously typed input.
- virtual bool IsCaretAtEndOfText(size_t input_length,
- size_t previous_length) const = 0;
-
- // Set the selected range of text that should be displayed to the user
- // to [start,end] (inclusive).
- virtual void SetSelectionRange(size_t start, size_t end) = 0;
-
- // Accessor/Mutator for the text value.
- virtual void SetValue(const std::wstring& value) = 0;
- virtual std::wstring GetValue() const = 0;
-
- // Called when processing is finished.
- virtual void OnFinishedAutocompleting() = 0;
-};
-
// 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 AutocompleteEditDelegate {
+class HTMLInputDelegate {
public:
explicit HTMLInputDelegate(WebCore::HTMLInputElement* element);
virtual ~HTMLInputDelegate();
- // AutocompleteEditDelegate implementation.
- virtual bool IsCaretAtEndOfText(size_t input_length,
- size_t previous_length) const;
virtual void SetValue(const std::wstring& value);
- virtual std::wstring GetValue() const;
virtual void SetSelectionRange(size_t start, size_t end);
virtual void OnFinishedAutocompleting();
private:
- // The underlying DOM element we're wrapping. We reference the
+ // 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_EVIL_CONSTRUCTORS(HTMLInputDelegate);
+ 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
@@ -89,48 +85,35 @@ class HTMLInputDelegate : public AutocompleteEditDelegate {
// upon destruction).
// 2. It allows fine-grained control when the popup/down is implemented
// in handling key events / selecting elements.
-//
-// Note: The element an AutocompleteInputListener is attached to is kept
-// separate from the element it explicitly interacts with (via the
-// AutocompleteEditDelegate API) so that the listeners are effectively
-// decoupled from HTMLInputElement; which is great when it comes time
-// for testing. The listeners can be constructed using only the delegates,
-// and events can be manually fired to test specific behaviour.
-//
-class AutocompleteInputListener : public WebCore::EventListener {
+class AutocompleteBodyListener : public WebCore::EventListener {
public:
- // Construct a listener with access to an edit field (i.e an HTMLInputElement)
- // through a delegate, so that it can obtain and set values needed for
- // autocomplete. See the above Note which explains why the edit_delegate it
- // is handed here is not necessarily the node it is attached to as an
- // EventListener. This object takes ownership of its edit_delegate.
- explicit AutocompleteInputListener(AutocompleteEditDelegate* edit_delegate);
-
- virtual ~AutocompleteInputListener() {
- }
+ // 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);
- // Subclasses need only implement the following two methods. They could
- // be declared protected but are left public to be invoked by testing.
-
- // 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(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(const std::wstring& user_input) = 0;
-
protected:
- // Access and modify the edit field only via the AutocompleteEditDelegate API.
- AutocompleteEditDelegate* edit_delegate() { return edit_delegate_.get(); }
+ // 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,
@@ -186,32 +169,31 @@ class AutocompleteInputListener : public WebCore::EventListener {
// 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(const std::wstring& user_input);
-
- // For testability, the AutocompleteEditDelegate API is used to decouple
- // AutocompleteInputListeners from underlying HTMLInputElements. This
- // allows testcases to mock delegates and test the autocomplete code without
- // a real underlying DOM.
- scoped_ptr<AutocompleteEditDelegate> edit_delegate_;
-
- // Stores the text across input events during inline autocomplete.
- std::wstring previous_text_;
-
- DISALLOW_EVIL_CONSTRUCTORS(AutocompleteInputListener);
+ 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);
};
-// Attach the listener as an EventListener to basic events required
-// to handle inline autocomplete (and blur events for tab/click-out).
-// Attaching to the WebCore element effectively transfers ownership of
-// the listener objects. When WebCore is tearing down the document,
-// any attached listeners are destroyed.
-// See Document::removeAllEventListenersFromAllNodes which is called by
-// FrameLoader::stopLoading. Also, there is no need for matching calls to
-// removeEventListener because the simplest and most convienient thing to do
-// for autocompletion is to stop listening once the element is destroyed.
-void AttachForInlineAutocomplete(WebCore::HTMLInputElement* target,
- AutocompleteInputListener* listener);
-
} // webkit_glue
#endif // WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__
diff --git a/webkit/glue/autocomplete_input_listener_unittest.cc b/webkit/glue/autocomplete_input_listener_unittest.cc
index 18c70ef..a4bc906 100644
--- a/webkit/glue/autocomplete_input_listener_unittest.cc
+++ b/webkit/glue/autocomplete_input_listener_unittest.cc
@@ -29,56 +29,57 @@ MSVC_POP_WARNING();
#undef LOG
#include "webkit/glue/autocomplete_input_listener.h"
+#include "webkit/glue/webframe.h"
+#include "webkit/glue/webframe_impl.h"
+#include "webkit/glue/webview.h"
+#include "webkit/tools/test_shell/test_shell_test.h"
#include "testing/gtest/include/gtest/gtest.h"
-using webkit_glue::AutocompleteInputListener;
-using webkit_glue::AutocompleteEditDelegate;
-
using WebCore::Event;
-class TestAutocompleteEditDelegate : public AutocompleteEditDelegate {
- public:
- TestAutocompleteEditDelegate() : caret_at_end_(false) {
- }
-
- virtual bool IsCaretAtEndOfText(size_t input_length,
- size_t prev_length) const {
- return caret_at_end_;
- }
-
- void SetCaretAtEnd(bool caret_at_end) {
- caret_at_end_ = caret_at_end;
- }
-
- virtual void SetSelectionRange(size_t start, size_t end) {
- }
+namespace webkit_glue {
- virtual void SetValue(const std::wstring& value) {
- value_ = value;
+class TestAutocompleteBodyListener : public AutocompleteBodyListener {
+ public:
+ TestAutocompleteBodyListener() {
}
- virtual std::wstring GetValue() const {
- return value_;
+ void SetCaretAtEnd(WebCore::HTMLInputElement* element, bool value) {
+ std::vector<WebCore::HTMLInputElement*>::iterator iter =
+ std::find(caret_at_end_elements_.begin(), caret_at_end_elements_.end(),
+ element);
+ if (value) {
+ if (iter == caret_at_end_elements_.end())
+ caret_at_end_elements_.push_back(element);
+ } else {
+ if (iter != caret_at_end_elements_.end())
+ caret_at_end_elements_.erase(iter);
+ }
}
- virtual void OnFinishedAutocompleting() {
+ void ResetTestState() {
+ caret_at_end_elements_.clear();
}
- void ResetTestState() {
- caret_at_end_ = false;
- value_.clear();
+ protected:
+ // AutocompleteBodyListener override.
+ virtual bool IsCaretAtEndOfText(WebCore::HTMLInputElement* element,
+ size_t input_length,
+ size_t previous_length) const {
+ return std::find(caret_at_end_elements_.begin(),
+ caret_at_end_elements_.end(),
+ element) != caret_at_end_elements_.end();
}
private:
- bool caret_at_end_;
- std::wstring value_;
+ // List of elements for which the caret is at the end of the text.
+ std::vector<WebCore::HTMLInputElement*> caret_at_end_elements_;
};
class TestAutocompleteInputListener : public AutocompleteInputListener {
public:
- TestAutocompleteInputListener(AutocompleteEditDelegate* d)
- : AutocompleteInputListener(d),
- blurred_(false),
+ TestAutocompleteInputListener()
+ : blurred_(false),
did_request_inline_autocomplete_(false) {
}
@@ -92,10 +93,12 @@ class TestAutocompleteInputListener : public AutocompleteInputListener {
return did_request_inline_autocomplete_;
}
- virtual void OnBlur(const std::wstring& user_input) {
+ virtual void OnBlur(WebCore::HTMLInputElement* element,
+ const std::wstring& user_input) {
blurred_ = true;
}
- virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input) {
+ virtual void OnInlineAutocompleteNeeded(WebCore::HTMLInputElement* element,
+ const std::wstring& user_input) {
did_request_inline_autocomplete_ = true;
}
@@ -105,81 +108,120 @@ class TestAutocompleteInputListener : public AutocompleteInputListener {
};
namespace {
-class DomAutocompleteTests : public testing::Test {
+
+class DomAutocompleteTests : public TestShellTest {
public:
- void SetUp() {
- WTF::initializeThreading();
- WebCore::EventNames::init();
+ virtual void SetUp() {
+ TestShellTest::SetUp();
+ // We need a document in order to create HTMLInputElements.
+ WebView* view = test_shell_->webView();
+ WebFrameImpl* frame = static_cast<WebFrameImpl*>(view->GetMainFrame());
+ document_ = frame->frame()->document();
}
- void FireAndHandleInputEvent(AutocompleteInputListener* listener) {
+ void FireAndHandleInputEvent(AutocompleteBodyListener* listener,
+ WebCore::HTMLInputElement* element) {
RefPtr<Event> event(Event::create(WebCore::eventNames().inputEvent,
false, false));
+ event->setTarget(element);
listener->handleEvent(event.get(), false);
}
- void SimulateTypedInput(TestAutocompleteEditDelegate* delegate,
- AutocompleteInputListener* listener,
- const std::wstring& new_input) {
- delegate->SetValue(new_input);
- delegate->SetCaretAtEnd(true);
- FireAndHandleInputEvent(listener);
+ void SimulateTypedInput(TestAutocompleteBodyListener* listener,
+ WebCore::HTMLInputElement* element,
+ const std::wstring& new_input,
+ bool caret_at_end) {
+ element->setValue(StdWStringToString(new_input));
+ listener->SetCaretAtEnd(element, caret_at_end);
+ FireAndHandleInputEvent(listener, element);
}
+
+ WebCore::Document* document_;
};
} // namespace
TEST_F(DomAutocompleteTests, OnBlur) {
- // Simulate a blur event and ensure it is properly dispatched.
- // Listener takes ownership of its delegate.
- TestAutocompleteInputListener listener(new TestAutocompleteEditDelegate());
+ RefPtr<WebCore::HTMLInputElement> ignored_element =
+ new WebCore::HTMLInputElement(document_);
+ RefPtr<WebCore::HTMLInputElement> listened_element =
+ new WebCore::HTMLInputElement(document_);
+ RefPtr<TestAutocompleteBodyListener> body_listener =
+ new TestAutocompleteBodyListener;
+ TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
+ // body_listener takes ownership of the listener.
+ body_listener->AddInputListener(listened_element.get(), listener);
+
+ // Simulate a blur event to the element we are not listening to.
+ // Our listener should not be notified.
RefPtr<Event> event(Event::create(WebCore::eventNames().DOMFocusOutEvent,
false, false));
- listener.handleEvent(event.get(), false);
- EXPECT_TRUE(listener.blurred());
+ event->setTarget(ignored_element.get());
+ body_listener->handleEvent(event.get(), false);
+ EXPECT_FALSE(listener->blurred());
+
+ // Now simulate the event on the input element we are listening to.
+ event->setTarget(listened_element.get());
+ body_listener->handleEvent(event.get(), false);
+ EXPECT_TRUE(listener->blurred());
}
TEST_F(DomAutocompleteTests, InlineAutocompleteTriggeredByInputEvent) {
- // Set up the edit delegate, assuming the field was initially empty.
- TestAutocompleteEditDelegate* delegate = new TestAutocompleteEditDelegate();
- TestAutocompleteInputListener listener(delegate);
+ RefPtr<WebCore::HTMLInputElement> ignored_element =
+ new WebCore::HTMLInputElement(document_);
+ RefPtr<WebCore::HTMLInputElement> listened_element =
+ new WebCore::HTMLInputElement(document_);
+ RefPtr<TestAutocompleteBodyListener> body_listener =
+ new TestAutocompleteBodyListener;
+
+ TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
+ body_listener->AddInputListener(listened_element.get(), listener);
// Simulate an inputEvent by setting the value and artificially firing evt.
// The user typed 'g'.
- SimulateTypedInput(delegate, &listener, L"g");
- EXPECT_TRUE(listener.did_request_inline_autocomplete());
+ SimulateTypedInput(body_listener.get(), ignored_element.get(), L"g", true);
+ EXPECT_FALSE(listener->did_request_inline_autocomplete());
+ SimulateTypedInput(body_listener.get(), listened_element.get(), L"g", true);
+ EXPECT_TRUE(listener->did_request_inline_autocomplete());
}
TEST_F(DomAutocompleteTests, InlineAutocompleteHeuristics) {
- TestAutocompleteEditDelegate* delegate = new TestAutocompleteEditDelegate();
- TestAutocompleteInputListener listener(delegate);
+ RefPtr<WebCore::HTMLInputElement> input_element =
+ new WebCore::HTMLInputElement(document_);
+ RefPtr<TestAutocompleteBodyListener> body_listener =
+ new TestAutocompleteBodyListener();
+
+ TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
+ body_listener->AddInputListener(input_element.get(), listener);
// Simulate a user entering some text, and then backspacing to remove
// a character.
- SimulateTypedInput(delegate, &listener, L"g");
- EXPECT_TRUE(listener.did_request_inline_autocomplete());
- listener.ResetTestState();
+ SimulateTypedInput(body_listener.get(), input_element.get(), L"g", true);
+ EXPECT_TRUE(listener->did_request_inline_autocomplete());
+ listener->ResetTestState();
+ body_listener->ResetTestState();
- SimulateTypedInput(delegate, &listener, L"go");
- EXPECT_TRUE(listener.did_request_inline_autocomplete());
- listener.ResetTestState();
+ SimulateTypedInput(body_listener.get(), input_element.get(), L"go", true);
+ EXPECT_TRUE(listener->did_request_inline_autocomplete());
+ listener->ResetTestState();
+ body_listener->ResetTestState();
- SimulateTypedInput(delegate, &listener, L"g");
- EXPECT_FALSE(listener.did_request_inline_autocomplete());
- listener.ResetTestState();
+ SimulateTypedInput(body_listener.get(), input_element.get(), L"g", true);
+ EXPECT_FALSE(listener->did_request_inline_autocomplete());
+ listener->ResetTestState();
+ body_listener->ResetTestState();
// Now simulate the user moving the cursor to a position other than the end,
// and adding text.
- delegate->SetCaretAtEnd(false);
- delegate->SetValue(L"og");
- FireAndHandleInputEvent(&listener);
- EXPECT_FALSE(listener.did_request_inline_autocomplete());
- listener.ResetTestState();
+ SimulateTypedInput(body_listener.get(), input_element.get(), L"og", false);
+ EXPECT_FALSE(listener->did_request_inline_autocomplete());
+ listener->ResetTestState();
+ body_listener->ResetTestState();
// Test that same input doesn't trigger autocomplete.
- delegate->SetCaretAtEnd(true);
- delegate->SetValue(L"og");
- FireAndHandleInputEvent(&listener);
- EXPECT_FALSE(listener.did_request_inline_autocomplete());
- listener.ResetTestState();
+ SimulateTypedInput(body_listener.get(), input_element.get(), L"og", true);
+ EXPECT_FALSE(listener->did_request_inline_autocomplete());
+ listener->ResetTestState();
+ body_listener->ResetTestState();
}
+} // webkit_glue
diff --git a/webkit/glue/dom_operations.cc b/webkit/glue/dom_operations.cc
index 9e15d8a..6b560c8 100644
--- a/webkit/glue/dom_operations.cc
+++ b/webkit/glue/dom_operations.cc
@@ -445,11 +445,16 @@ void FillPasswordForm(WebView* view,
WebCore::HTMLInputElement* password_element =
form_elements->input_elements[data.basic_data.elements[1]].get();
- AttachForInlineAutocomplete(username_element,
- new PasswordAutocompleteListener(
- new HTMLInputDelegate(username_element),
- new HTMLInputDelegate(password_element),
- data));
+ WebFrameLoaderClient* frame_loader_client =
+ static_cast<WebFrameLoaderClient*>(username_element->document()->
+ frame()->loader()->client());
+ WebFrameImpl* webframe_impl = frame_loader_client->webframe();
+ webframe_impl->GetAutocompleteListener()->AddInputListener(
+ username_element,
+ new PasswordAutocompleteListener(
+ new HTMLInputDelegate(username_element),
+ new HTMLInputDelegate(password_element),
+ data));
}
}
diff --git a/webkit/glue/form_autocomplete_listener.cc b/webkit/glue/form_autocomplete_listener.cc
index aaaf0f70..e5751ca 100644
--- a/webkit/glue/form_autocomplete_listener.cc
+++ b/webkit/glue/form_autocomplete_listener.cc
@@ -19,19 +19,17 @@ MSVC_POP_WARNING();
namespace webkit_glue {
FormAutocompleteListener::FormAutocompleteListener(
- WebViewDelegate* webview_delegate,
- WebCore::HTMLInputElement* input_element)
- : AutocompleteInputListener(new HTMLInputDelegate(input_element)),
- webview_delegate_(webview_delegate),
- name_(webkit_glue::StringToStdWString(input_element->name().string())),
- node_id_(reinterpret_cast<int64>(input_element)) {
- DCHECK(input_element->isTextField() && !input_element->isPasswordField() &&
- input_element->autoComplete());
+ WebViewDelegate* webview_delegate)
+ : webview_delegate_(webview_delegate) {
}
void FormAutocompleteListener::OnInlineAutocompleteNeeded(
+ WebCore::HTMLInputElement* input_element,
const std::wstring& user_input) {
- webview_delegate_->QueryFormFieldAutofill(name_, user_input, node_id_);
+ std::wstring name = webkit_glue::StringToStdWString(input_element->
+ name().string());
+ webview_delegate_->QueryFormFieldAutofill(name, user_input,
+ reinterpret_cast<int64>(input_element));
}
} // namespace
diff --git a/webkit/glue/form_autocomplete_listener.h b/webkit/glue/form_autocomplete_listener.h
index e55522e..1c86c02 100644
--- a/webkit/glue/form_autocomplete_listener.h
+++ b/webkit/glue/form_autocomplete_listener.h
@@ -18,26 +18,20 @@ namespace webkit_glue {
class FormAutocompleteListener : public AutocompleteInputListener {
public:
- FormAutocompleteListener(WebViewDelegate* webview_delegate,
- WebCore::HTMLInputElement* input_element);
+ explicit FormAutocompleteListener(WebViewDelegate* webview_delegate);
virtual ~FormAutocompleteListener() { }
// AutocompleteInputListener implementation.
- virtual void OnBlur(const std::wstring& user_input) { }
- virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input);
+ virtual void OnBlur(WebCore::HTMLInputElement* input_element,
+ const std::wstring& user_input) { }
+ virtual void OnInlineAutocompleteNeeded(WebCore::HTMLInputElement* element,
+ const std::wstring& user_input);
private:
// The delegate associated with the WebView that contains thhe input we are
// listening to.
WebViewDelegate* webview_delegate_;
- // The name of the input node we are listening to.
- std::wstring name_;
-
- // An id to represent the input element. That ID is passed to the call that
- // queries for suggestions.
- int64 node_id_;
-
DISALLOW_COPY_AND_ASSIGN(FormAutocompleteListener);
};
diff --git a/webkit/glue/password_autocomplete_listener.cc b/webkit/glue/password_autocomplete_listener.cc
index 0a88ab3..fff24f4 100644
--- a/webkit/glue/password_autocomplete_listener.cc
+++ b/webkit/glue/password_autocomplete_listener.cc
@@ -12,15 +12,16 @@
namespace webkit_glue {
PasswordAutocompleteListener::PasswordAutocompleteListener(
- AutocompleteEditDelegate* username_delegate,
+ HTMLInputDelegate* username_delegate,
HTMLInputDelegate* password_delegate,
const PasswordFormDomManager::FillData& data)
- : AutocompleteInputListener(username_delegate),
- password_delegate_(password_delegate),
+ : password_delegate_(password_delegate),
+ username_delegate_(username_delegate),
data_(data) {
}
-void PasswordAutocompleteListener::OnBlur(const std::wstring& user_input) {
+void PasswordAutocompleteListener::OnBlur(WebCore::HTMLInputElement* element,
+ const std::wstring& 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.
@@ -39,6 +40,7 @@ void PasswordAutocompleteListener::OnBlur(const std::wstring& user_input) {
}
void PasswordAutocompleteListener::OnInlineAutocompleteNeeded(
+ WebCore::HTMLInputElement* element,
const std::wstring& user_input) {
// If wait_for_username is true, we only autofill the password when
// the username field is blurred (i.e not inline) with a matching
@@ -75,9 +77,9 @@ bool PasswordAutocompleteListener::TryToMatch(const std::wstring& input,
return false;
// Input matches the username, fill in required values.
- edit_delegate()->SetValue(username);
- edit_delegate()->SetSelectionRange(input.length(), username.length());
- edit_delegate()->OnFinishedAutocompleting();
+ username_delegate_->SetValue(username);
+ username_delegate_->SetSelectionRange(input.length(), username.length());
+ username_delegate_->OnFinishedAutocompleting();
password_delegate_->SetValue(password);
password_delegate_->OnFinishedAutocompleting();
return true;
diff --git a/webkit/glue/password_autocomplete_listener.h b/webkit/glue/password_autocomplete_listener.h
index 736303e..a2c7a7a 100644
--- a/webkit/glue/password_autocomplete_listener.h
+++ b/webkit/glue/password_autocomplete_listener.h
@@ -5,8 +5,8 @@
// A concrete definition of the DOM autocomplete framework defined by
// autocomplete_input_listener.h, for the password manager.
-#ifndef WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H__
-#define WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H__
+#ifndef WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H_
+#define WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H_
#include "base/basictypes.h"
#include "webkit/glue/autocomplete_input_listener.h"
@@ -16,15 +16,17 @@ namespace webkit_glue {
class PasswordAutocompleteListener : public AutocompleteInputListener {
public:
- PasswordAutocompleteListener(AutocompleteEditDelegate* username_delegate,
+ PasswordAutocompleteListener(HTMLInputDelegate* username_delegate,
HTMLInputDelegate* password_delegate,
const PasswordFormDomManager::FillData& data);
virtual ~PasswordAutocompleteListener() {
}
// AutocompleteInputListener implementation.
- virtual void OnBlur(const std::wstring& user_input);
- virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input);
+ virtual void OnBlur(WebCore::HTMLInputElement* element,
+ const std::wstring& user_input);
+ virtual void OnInlineAutocompleteNeeded(WebCore::HTMLInputElement* element,
+ const std::wstring& user_input);
private:
// Check if the input string resembles a potential matching login
@@ -36,14 +38,15 @@ class PasswordAutocompleteListener : public AutocompleteInputListener {
// Access to password field to autocomplete on blur/username updates.
scoped_ptr<HTMLInputDelegate> password_delegate_;
+ scoped_ptr<HTMLInputDelegate> username_delegate_;
// Contains the extra logins for matching on delta/blur.
PasswordFormDomManager::FillData data_;
- DISALLOW_EVIL_CONSTRUCTORS(PasswordAutocompleteListener);
+ DISALLOW_COPY_AND_ASSIGN(PasswordAutocompleteListener);
};
} // webkit_glue
-#endif // WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H__
+#endif // WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H_
diff --git a/webkit/glue/password_autocomplete_listener_unittest.cc b/webkit/glue/password_autocomplete_listener_unittest.cc
index 53623d1..85cfa9f 100644
--- a/webkit/glue/password_autocomplete_listener_unittest.cc
+++ b/webkit/glue/password_autocomplete_listener_unittest.cc
@@ -30,7 +30,6 @@ MSVC_POP_WARNING();
using webkit_glue::AutocompleteInputListener;
using webkit_glue::PasswordAutocompleteListener;
-using webkit_glue::AutocompleteEditDelegate;
using webkit_glue::HTMLInputDelegate;
class TestHTMLInputDelegate : public HTMLInputDelegate {
@@ -44,10 +43,6 @@ class TestHTMLInputDelegate : public HTMLInputDelegate {
}
// Override those methods we implicitly invoke in the tests.
- virtual std::wstring GetValue() const {
- return value_;
- }
-
virtual void SetValue(const std::wstring& value) {
value_ = value;
did_set_value_ = true;
@@ -70,6 +65,10 @@ class TestHTMLInputDelegate : public HTMLInputDelegate {
did_set_selection_ = false;
}
+ std::wstring value() const {
+ return value_;
+ }
+
bool did_call_on_finish() const {
return did_call_on_finish_;
}
@@ -126,42 +125,41 @@ TEST_F(PasswordManagerAutocompleteTests, OnBlur) {
TestHTMLInputDelegate* username_delegate = new TestHTMLInputDelegate();
TestHTMLInputDelegate* password_delegate = new TestHTMLInputDelegate();
- PasswordAutocompleteListener* listener = new PasswordAutocompleteListener(
- username_delegate, password_delegate, data_);
+ scoped_ptr<PasswordAutocompleteListener> listener(
+ new PasswordAutocompleteListener(username_delegate, password_delegate,
+ data_));
// Clear the password field.
password_delegate->SetValue(std::wstring());
// Simulate a blur event on the username field and expect a password autofill.
- listener->OnBlur(username1_);
- EXPECT_EQ(password1_, password_delegate->GetValue());
+ listener->OnBlur(NULL, username1_);
+ EXPECT_EQ(password1_, password_delegate->value());
// Now the user goes back and changes the username to something we don't
// have saved. The password should remain unchanged.
- listener->OnBlur(L"blahblahblah");
- EXPECT_EQ(password1_, password_delegate->GetValue());
+ listener->OnBlur(NULL, L"blahblahblah");
+ EXPECT_EQ(password1_, password_delegate->value());
// Now they type in the additional login username.
- listener->OnBlur(username2_);
- EXPECT_EQ(password2_, password_delegate->GetValue());
-
- // Now they submit and the listener is destroyed.
- delete listener;
+ listener->OnBlur(NULL, username2_);
+ EXPECT_EQ(password2_, password_delegate->value());
}
TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
TestHTMLInputDelegate* username_delegate = new TestHTMLInputDelegate();
TestHTMLInputDelegate* password_delegate = new TestHTMLInputDelegate();
- PasswordAutocompleteListener* listener = new PasswordAutocompleteListener(
- username_delegate, password_delegate, data_);
+ scoped_ptr<PasswordAutocompleteListener> listener(
+ new PasswordAutocompleteListener(username_delegate, password_delegate,
+ data_));
password_delegate->SetValue(std::wstring());
// Simulate the user typing in the first letter of 'alice', a stored username.
- listener->OnInlineAutocompleteNeeded(L"a");
+ listener->OnInlineAutocompleteNeeded(NULL, L"a");
// Both the username and password delegates should reflect selection
// of the stored login.
- EXPECT_EQ(username1_, username_delegate->GetValue());
- EXPECT_EQ(password1_, password_delegate->GetValue());
+ EXPECT_EQ(username1_, username_delegate->value());
+ EXPECT_EQ(password1_, password_delegate->value());
// And the selection should have been set to 'lice', the last 4 letters.
EXPECT_EQ(1U, username_delegate->selection_start());
EXPECT_EQ(username1_.length(), username_delegate->selection_end());
@@ -170,11 +168,11 @@ 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(L"al");
+ listener->OnInlineAutocompleteNeeded(NULL, L"al");
// Now the fields should have the same value, but the selection should have a
// different start value.
- EXPECT_EQ(username1_, username_delegate->GetValue());
- EXPECT_EQ(password1_, password_delegate->GetValue());
+ EXPECT_EQ(username1_, username_delegate->value());
+ EXPECT_EQ(password1_, password_delegate->value());
EXPECT_EQ(2U, username_delegate->selection_start());
EXPECT_EQ(username1_.length(), username_delegate->selection_end());
@@ -190,7 +188,7 @@ TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) {
// was invoked during OnInlineAutocompleteNeeded.
username_delegate->ResetTestState();
password_delegate->ResetTestState();
- listener->OnInlineAutocompleteNeeded(L"alf");
+ listener->OnInlineAutocompleteNeeded(NULL, L"alf");
EXPECT_FALSE(username_delegate->did_set_selection());
EXPECT_FALSE(username_delegate->did_set_value());
EXPECT_FALSE(username_delegate->did_call_on_finish());
@@ -198,15 +196,12 @@ 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(L"b");
+ listener->OnInlineAutocompleteNeeded(NULL, L"b");
// The username and password fields should match the 'bob' entry.
- EXPECT_EQ(username2_, username_delegate->GetValue());
- EXPECT_EQ(password2_, password_delegate->GetValue());
+ EXPECT_EQ(username2_, username_delegate->value());
+ EXPECT_EQ(password2_, password_delegate->value());
EXPECT_EQ(1U, username_delegate->selection_start());
EXPECT_EQ(username2_.length(), username_delegate->selection_end());
-
- // The End.
- delete listener;
}
TEST_F(PasswordManagerAutocompleteTests, TestWaitUsername) {
@@ -218,36 +213,34 @@ TEST_F(PasswordManagerAutocompleteTests, TestWaitUsername) {
// We require an explicit blur on the username field, and that a valid
// matching username is in the field, before we autofill passwords.
data_.wait_for_username = true;
- PasswordAutocompleteListener* listener = new PasswordAutocompleteListener(
- username_delegate, password_delegate, data_);
+ scoped_ptr<PasswordAutocompleteListener> listener(
+ new PasswordAutocompleteListener(username_delegate, password_delegate,
+ data_));
std::wstring empty;
// In all cases, username_delegate should remain empty because we should
// 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(L"a");
- EXPECT_EQ(empty, username_delegate->GetValue());
- EXPECT_EQ(empty, password_delegate->GetValue());
- listener->OnInlineAutocompleteNeeded(L"al");
- EXPECT_EQ(empty, username_delegate->GetValue());
- EXPECT_EQ(empty, password_delegate->GetValue());
- listener->OnInlineAutocompleteNeeded(L"alice");
- EXPECT_EQ(empty, username_delegate->GetValue());
- EXPECT_EQ(empty, password_delegate->GetValue());
+ listener->OnInlineAutocompleteNeeded(NULL, L"a");
+ EXPECT_EQ(empty, username_delegate->value());
+ EXPECT_EQ(empty, password_delegate->value());
+ listener->OnInlineAutocompleteNeeded(NULL, L"al");
+ EXPECT_EQ(empty, username_delegate->value());
+ EXPECT_EQ(empty, password_delegate->value());
+ listener->OnInlineAutocompleteNeeded(NULL, L"alice");
+ EXPECT_EQ(empty, username_delegate->value());
+ EXPECT_EQ(empty, password_delegate->value());
- listener->OnBlur(L"a");
- EXPECT_EQ(empty, username_delegate->GetValue());
- EXPECT_EQ(empty, password_delegate->GetValue());
- listener->OnBlur(L"ali");
- EXPECT_EQ(empty, username_delegate->GetValue());
- EXPECT_EQ(empty, password_delegate->GetValue());
+ listener->OnBlur(NULL, L"a");
+ EXPECT_EQ(empty, username_delegate->value());
+ EXPECT_EQ(empty, password_delegate->value());
+ listener->OnBlur(NULL, L"ali");
+ EXPECT_EQ(empty, username_delegate->value());
+ EXPECT_EQ(empty, password_delegate->value());
// Blur with 'alice' should allow password autofill.
- listener->OnBlur(L"alice");
- EXPECT_EQ(empty, username_delegate->GetValue());
- EXPECT_EQ(password1_, password_delegate->GetValue());
-
- delete listener;
+ listener->OnBlur(NULL, L"alice");
+ EXPECT_EQ(empty, username_delegate->value());
+ EXPECT_EQ(password1_, password_delegate->value());
}
-
diff --git a/webkit/glue/webframe_impl.cc b/webkit/glue/webframe_impl.cc
index 511b5d0..fa9960c 100644
--- a/webkit/glue/webframe_impl.cc
+++ b/webkit/glue/webframe_impl.cc
@@ -286,7 +286,8 @@ MSVC_POP_WARNING()
frames_scoping_count_(-1),
scoping_complete_(false),
next_invalidate_after_(0),
- printing_(false) {
+ printing_(false),
+ form_autocomplete_listener_(NULL) {
StatsCounter(kWebFrameActiveCount).Increment();
live_object_count_++;
}
@@ -1871,3 +1872,15 @@ bool WebFrameImpl::IsReloadAllowingStaleData() const {
int WebFrameImpl::PendingFrameUnloadEventCount() const {
return frame()->eventHandler()->pendingFrameUnloadEventCount();
}
+
+webkit_glue::AutocompleteBodyListener* WebFrameImpl::GetAutocompleteListener() {
+ if (!form_autocomplete_listener_) {
+ form_autocomplete_listener_ =
+ adoptRef(new webkit_glue::AutocompleteBodyListener(frame()));
+ }
+ return form_autocomplete_listener_.get();
+}
+
+void WebFrameImpl::ClearAutocompleteListener() {
+ form_autocomplete_listener_ = NULL;
+}
diff --git a/webkit/glue/webframe_impl.h b/webkit/glue/webframe_impl.h
index ce2521a..b3b241a 100644
--- a/webkit/glue/webframe_impl.h
+++ b/webkit/glue/webframe_impl.h
@@ -33,6 +33,7 @@
#include "base/gfx/platform_canvas.h"
#include "base/scoped_ptr.h"
#include "base/task.h"
+#include "webkit/glue/form_autocomplete_listener.h"
#include "webkit/glue/webdatasource_impl.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/webframeloaderclient_impl.h"
@@ -270,6 +271,15 @@ class WebFrameImpl : public WebFrame {
virtual bool IsReloadAllowingStaleData() const;
+ // Returns the listener used for autocomplete. Creates it and registers it on
+ // the frame body node on the first invocation.
+ webkit_glue::AutocompleteBodyListener* GetAutocompleteListener();
+
+ // Nulls the autocomplete listener for this frame. Useful as a frame might
+ // be reused (on reload for example), in which case a new body element is
+ // created and the existing autocomplete listener becomes useless.
+ void ClearAutocompleteListener();
+
protected:
friend class WebFrameLoaderClient;
@@ -436,6 +446,9 @@ class WebFrameImpl : public WebFrame {
// For each printed page, the view of the document in pixels.
Vector<WebCore::IntRect> pages_;
+ // The listener responsible for showing form autocomplete suggestions.
+ RefPtr<webkit_glue::AutocompleteBodyListener> form_autocomplete_listener_;
+
DISALLOW_COPY_AND_ASSIGN(WebFrameImpl);
};
diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc
index 7d7bcb2..f311ce1 100644
--- a/webkit/glue/webframeloaderclient_impl.cc
+++ b/webkit/glue/webframeloaderclient_impl.cc
@@ -328,6 +328,10 @@ void WebFrameLoaderClient::dispatchDidFailLoading(DocumentLoader* loader,
void WebFrameLoaderClient::dispatchDidFinishDocumentLoad() {
WebViewImpl* webview = webframe_->webview_impl();
WebViewDelegate* d = webview->delegate();
+ // A frame may be reused. This call ensures a new AutoCompleteListener will
+ // be created for the newly created frame.
+ webframe_->ClearAutocompleteListener();
+
// The document has now been fully loaded.
// Scan for password forms to be sent to the browser
PassRefPtr<WebCore::HTMLCollection> forms =
@@ -720,17 +724,16 @@ void WebFrameLoaderClient::RegisterAutofillListeners(
}
std::wstring name = webkit_glue::StringToStdWString(input_element->name());
- if (excluded_fields.find(name) != excluded_fields.end())
+ if (name.empty() || excluded_fields.find(name) != excluded_fields.end())
continue;
-/* Disabling this temporarily to investigate perf regressions.
#if !defined(OS_MACOSX)
// FIXME on Mac
webkit_glue::FormAutocompleteListener* listener =
- new webkit_glue::FormAutocompleteListener(webview_delegate,
- input_element);
- webkit_glue::AttachForInlineAutocomplete(input_element, listener);
-#endif*/
+ new webkit_glue::FormAutocompleteListener(webview_delegate);
+ webframe_->GetAutocompleteListener()->AddInputListener(input_element,
+ listener);
+#endif
}
}