diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-17 22:04:00 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-17 22:04:00 +0000 |
commit | 5f9b16e96cc1bd69df3aa064233f49573da74274 (patch) | |
tree | a0151ba7058b487df02f965d1aff4ea8e7c96007 /webkit | |
parent | 019d50c3d479f5cf48934dc454c7f9f824bb4a1b (diff) | |
download | chromium_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.cc | 147 | ||||
-rw-r--r-- | webkit/glue/autocomplete_input_listener.h | 168 | ||||
-rw-r--r-- | webkit/glue/autocomplete_input_listener_unittest.cc | 194 | ||||
-rw-r--r-- | webkit/glue/dom_operations.cc | 15 | ||||
-rw-r--r-- | webkit/glue/form_autocomplete_listener.cc | 16 | ||||
-rw-r--r-- | webkit/glue/form_autocomplete_listener.h | 16 | ||||
-rw-r--r-- | webkit/glue/password_autocomplete_listener.cc | 16 | ||||
-rw-r--r-- | webkit/glue/password_autocomplete_listener.h | 17 | ||||
-rw-r--r-- | webkit/glue/password_autocomplete_listener_unittest.cc | 101 | ||||
-rw-r--r-- | webkit/glue/webframe_impl.cc | 15 | ||||
-rw-r--r-- | webkit/glue/webframe_impl.h | 13 | ||||
-rw-r--r-- | webkit/glue/webframeloaderclient_impl.cc | 15 |
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 } } |