diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-05 19:28:08 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-05 19:28:08 +0000 |
commit | 89ac46c97e6a3b2f2726bd11d52824d27ee26a24 (patch) | |
tree | c976f9689ff7fabd3c3e54e06ed051d1bd4544c7 /webkit | |
parent | 9ac105a1023bc696e619e5714c524e1a3ffdc2ef (diff) | |
download | chromium_src-89ac46c97e6a3b2f2726bd11d52824d27ee26a24.zip chromium_src-89ac46c97e6a3b2f2726bd11d52824d27ee26a24.tar.gz chromium_src-89ac46c97e6a3b2f2726bd11d52824d27ee26a24.tar.bz2 |
This CL adds the autofill UI in forms.
When the user types text in a text field in a form, the renderer queries the browser for suggestion based on the entered text and displays the suggestions in a popup.
Listeners are set on the form text field in a similar fashion than for password save.
The popup showing the suggestion uses the same mechanism as the select popup. Note that a difference between the select and the autofill popup is that the autofill should not take focus, so the page still has focus and the user can still type in while it shows.
The creation of the render widget was modified for that purpose so we can specify the popup should not be focused when shown.
Review URL: http://codereview.chromium.org/8885
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4804 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/build/glue/glue.vcproj | 8 | ||||
-rw-r--r-- | webkit/build/port/port.vcproj | 4 | ||||
-rw-r--r-- | webkit/glue/SConscript | 1 | ||||
-rw-r--r-- | webkit/glue/autocomplete_input_listener.h | 1 | ||||
-rw-r--r-- | webkit/glue/chrome_client_impl.cc | 8 | ||||
-rw-r--r-- | webkit/glue/chrome_client_impl.h | 3 | ||||
-rw-r--r-- | webkit/glue/form_autocomplete_listener.cc | 35 | ||||
-rw-r--r-- | webkit/glue/form_autocomplete_listener.h | 46 | ||||
-rw-r--r-- | webkit/glue/password_autocomplete_listener.cc | 1 | ||||
-rw-r--r-- | webkit/glue/webframeloaderclient_impl.cc | 60 | ||||
-rw-r--r-- | webkit/glue/webframeloaderclient_impl.h | 8 | ||||
-rw-r--r-- | webkit/glue/webplugin_impl_mac.mm | 3 | ||||
-rw-r--r-- | webkit/glue/webview.h | 7 | ||||
-rw-r--r-- | webkit/glue/webview_delegate.h | 11 | ||||
-rw-r--r-- | webkit/glue/webview_impl.cc | 213 | ||||
-rw-r--r-- | webkit/glue/webview_impl.h | 16 | ||||
-rw-r--r-- | webkit/port/page/chromium/ChromeClientChromium.h | 4 | ||||
-rw-r--r-- | webkit/port/platform/chromium/PopupMenuChromium.cpp | 133 | ||||
-rw-r--r-- | webkit/port/platform/chromium/PopupMenuChromium.h | 82 |
19 files changed, 556 insertions, 88 deletions
diff --git a/webkit/build/glue/glue.vcproj b/webkit/build/glue/glue.vcproj index 28fe820..3ce56f3 100644 --- a/webkit/build/glue/glue.vcproj +++ b/webkit/build/glue/glue.vcproj @@ -381,6 +381,14 @@ > </File> <File + RelativePath="..\..\glue\form_autocomplete_listener.cc" + > + </File> + <File + RelativePath="..\..\glue\form_autocomplete_listener.h" + > + </File> + <File RelativePath="..\..\glue\glue_accessibility.cc" > </File> diff --git a/webkit/build/port/port.vcproj b/webkit/build/port/port.vcproj index c91d3ba..7464c87 100644 --- a/webkit/build/port/port.vcproj +++ b/webkit/build/port/port.vcproj @@ -807,6 +807,10 @@ > </File> <File + RelativePath="..\..\port\platform\chromium\PopupMenuChromium.h" + > + </File> + <File RelativePath="..\..\port\platform\chromium\SearchPopupMenuChromium.cpp" > </File> diff --git a/webkit/glue/SConscript b/webkit/glue/SConscript index 42dfec6..61d3aa0 100644 --- a/webkit/glue/SConscript +++ b/webkit/glue/SConscript @@ -35,6 +35,7 @@ input_files = [ 'editor_client_impl.cc', 'entity_map.cc', 'event_conversion.cc', + 'form_autocomplete_listener.cc', 'feed_preview.cc', 'glue_util.cc', 'glue_serialize.cc', diff --git a/webkit/glue/autocomplete_input_listener.h b/webkit/glue/autocomplete_input_listener.h index 56233a3..45ad993 100644 --- a/webkit/glue/autocomplete_input_listener.h +++ b/webkit/glue/autocomplete_input_listener.h @@ -16,7 +16,6 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "EventListener.h" MSVC_POP_WARNING(); -#undef LOG #include "base/basictypes.h" #include "base/scoped_ptr.h" diff --git a/webkit/glue/chrome_client_impl.cc b/webkit/glue/chrome_client_impl.cc index 618869d..b8cb4dd 100644 --- a/webkit/glue/chrome_client_impl.cc +++ b/webkit/glue/chrome_client_impl.cc @@ -449,12 +449,14 @@ void ChromeClientImpl::runFileChooser(const WebCore::String& default_path, delegate->RunFileChooser(suggestion, chooser); } -void ChromeClientImpl::popupOpened( - WebCore::FramelessScrollView* popup_view, const WebCore::IntRect& bounds) { +void ChromeClientImpl::popupOpened(WebCore::FramelessScrollView* popup_view, + const WebCore::IntRect& bounds, + bool focus_on_show) { WebViewDelegate* d = webview_->delegate(); if (d) { WebWidgetImpl* webwidget = - static_cast<WebWidgetImpl*>(d->CreatePopupWidget(webview_)); + static_cast<WebWidgetImpl*>(d->CreatePopupWidget(webview_, + focus_on_show)); webwidget->Init(popup_view, webkit_glue::FromIntRect(bounds)); } } diff --git a/webkit/glue/chrome_client_impl.h b/webkit/glue/chrome_client_impl.h index acecb44..7da89d3 100644 --- a/webkit/glue/chrome_client_impl.h +++ b/webkit/glue/chrome_client_impl.h @@ -111,7 +111,8 @@ public: virtual void runFileChooser(const WebCore::String&, PassRefPtr<WebCore::FileChooser>); virtual void popupOpened(WebCore::FramelessScrollView* popup_view, - const WebCore::IntRect& bounds); + const WebCore::IntRect& bounds, + bool focus_on_show); virtual void setCursor(const WebCore::Cursor&); private: diff --git a/webkit/glue/form_autocomplete_listener.cc b/webkit/glue/form_autocomplete_listener.cc new file mode 100644 index 0000000..4c17527 --- /dev/null +++ b/webkit/glue/form_autocomplete_listener.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/glue/form_autocomplete_listener.h" + +MSVC_PUSH_WARNING_LEVEL(0); +#include "HTMLInputElement.h" +MSVC_POP_WARNING(); + +#undef LOG + +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webview_delegate.h" + +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()); +} + +void FormAutocompleteListener::OnInlineAutocompleteNeeded( + const std::wstring& user_input) { + webview_delegate_->QueryFormFieldAutofill(name_, user_input, node_id_); +} + +} // namespace diff --git a/webkit/glue/form_autocomplete_listener.h b/webkit/glue/form_autocomplete_listener.h new file mode 100644 index 0000000..e55522e --- /dev/null +++ b/webkit/glue/form_autocomplete_listener.h @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_GLUE_FORM_AUTOCOMPLETE_LISTENER_ +#define WEBKIT_GLUE_FORM_AUTOCOMPLETE_LISTENER_ + +#include <string> + +#include "webkit/glue/autocomplete_input_listener.h" + +class WebViewDelegate; + +namespace webkit_glue { + +// This class listens for the user typing in a text input in a form and queries +// the browser for autofill information. + +class FormAutocompleteListener : public AutocompleteInputListener { + public: + FormAutocompleteListener(WebViewDelegate* webview_delegate, + WebCore::HTMLInputElement* input_element); + virtual ~FormAutocompleteListener() { } + + // AutocompleteInputListener implementation. + virtual void OnBlur(const std::wstring& user_input) { } + virtual void OnInlineAutocompleteNeeded(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); +}; + +} // webkit_glue + +#endif // WEBKIT_GLUE_FORM_AUTOCOMPLETE_LISTENER_ diff --git a/webkit/glue/password_autocomplete_listener.cc b/webkit/glue/password_autocomplete_listener.cc index aaf6125..0a88ab3 100644 --- a/webkit/glue/password_autocomplete_listener.cc +++ b/webkit/glue/password_autocomplete_listener.cc @@ -6,6 +6,7 @@ // component. #include "webkit/glue/password_autocomplete_listener.h" +#undef LOG #include "base/logging.h" namespace webkit_glue { diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc index f178554..2a46af8 100644 --- a/webkit/glue/webframeloaderclient_impl.cc +++ b/webkit/glue/webframeloaderclient_impl.cc @@ -16,6 +16,9 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "Element.h" #include "HistoryItem.h" #include "HTMLFormElement.h" // needed by FormState.h +#include "HTMLFormControlElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" #include "FormState.h" #include "FrameLoader.h" #include "FrameLoadRequest.h" @@ -39,8 +42,9 @@ MSVC_POP_WARNING(); #if defined(OS_WIN) #include "webkit/activex_shim/activex_shared.h" #endif -#include "webkit/glue/webframeloaderclient_impl.h" #include "webkit/glue/alt_404_page_resource_fetcher.h" +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/form_autocomplete_listener.h" #include "webkit/glue/glue_util.h" #include "webkit/glue/password_form_dom_manager.h" #include "webkit/glue/plugins/plugin_list.h" @@ -48,6 +52,7 @@ MSVC_POP_WARNING(); #include "webkit/glue/webdatasource_impl.h" #include "webkit/glue/webdocumentloader_impl.h" #include "webkit/glue/weberror_impl.h" +#include "webkit/glue/webframeloaderclient_impl.h" #include "webkit/glue/webhistoryitem_impl.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webplugin_impl.h" @@ -351,12 +356,28 @@ void WebFrameLoaderClient::dispatchDidFinishDocumentLoad() { if (!form->autoComplete()) continue; + std::set<std::wstring> password_related_fields; scoped_ptr<PasswordForm> data( PasswordFormDomManager::CreatePasswordForm(form)); - if (data.get()) + if (data.get()) { actions.push_back(*data); + // Let's remember the names of password related fields so we do not + // autofill them with the regular form autofill. + DCHECK(!data->username_element.empty()); + DCHECK(!data->password_element.empty()); + password_related_fields.insert(data->username_element); + password_related_fields.insert(data->password_element); + if (!data->old_password_element.empty()) + password_related_fields.insert(data->old_password_element); + } + + // Now let's register for any text input. + // TODO(jcampan): bug #3847 merge password and form autofill so we + // traverse the form elements only once. + RegisterAutofillListeners(form, password_related_fields); } } + if (d && (actions.size() > 0)) d->OnPasswordFormsSeen(webview, actions); if (d) @@ -685,6 +706,40 @@ NavigationGesture WebFrameLoaderClient::NavigationGestureForLastLoad() { NavigationGestureAuto; } +void WebFrameLoaderClient::RegisterAutofillListeners( + WebCore::HTMLFormElement* form, + const std::set<std::wstring>& excluded_fields) { + + WebViewDelegate* webview_delegate = webframe_->webview_impl()->delegate(); + if (!webview_delegate) + return; + + for (size_t i = 0; i < form->formElements.size(); i++) { + WebCore::HTMLFormControlElement* form_element = form->formElements[i]; + if (!form_element->hasLocalName(WebCore::HTMLNames::inputTag)) + continue; + + WebCore::HTMLInputElement* input_element = + static_cast<WebCore::HTMLInputElement*>(form_element); + if (!input_element->isEnabled() || !input_element->isTextField() || + input_element->isPasswordField() || !input_element->autoComplete()) { + continue; + } + + std::wstring name = webkit_glue::StringToStdWString(input_element->name()); + if (excluded_fields.find(name) != excluded_fields.end()) + continue; + +#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 + } +} + void WebFrameLoaderClient::dispatchDidReceiveTitle(const String& title) { WebViewImpl* webview = webframe_->webview_impl(); WebViewDelegate* d = webview->delegate(); @@ -1477,4 +1532,3 @@ bool WebFrameLoaderClient::ActionSpecifiesDisposition( *disposition = shift ? NEW_WINDOW : SAVE_TO_DISK; return true; } - diff --git a/webkit/glue/webframeloaderclient_impl.h b/webkit/glue/webframeloaderclient_impl.h index 20b12ee..6010ba8 100644 --- a/webkit/glue/webframeloaderclient_impl.h +++ b/webkit/glue/webframeloaderclient_impl.h @@ -5,6 +5,8 @@ #ifndef WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ #define WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ +#include <set> + #include "base/compiler_specific.h" MSVC_PUSH_WARNING_LEVEL(0); @@ -18,6 +20,7 @@ MSVC_POP_WARNING(); namespace WebCore { class Frame; +class HTMLFormElement; class Widget; } @@ -209,6 +212,11 @@ class WebFrameLoaderClient : public WebCore::FrameLoaderClient { // otherwise returns NavigationGestureUnknown. NavigationGesture NavigationGestureForLastLoad(); + // Registers the text input fields in the passed form for autofill, with the + // exclusion of any field whose name is contained in |excluded_fields|. + void RegisterAutofillListeners(WebCore::HTMLFormElement* form, + const std::set<std::wstring>& excluded_fields); + // The WebFrame that owns this object and manages its lifetime. Therefore, // the web frame object is guaranteed to exist. WebFrameImpl* webframe_; diff --git a/webkit/glue/webplugin_impl_mac.mm b/webkit/glue/webplugin_impl_mac.mm index 191ebb7..1a420ea 100644 --- a/webkit/glue/webplugin_impl_mac.mm +++ b/webkit/glue/webplugin_impl_mac.mm @@ -4,6 +4,9 @@ #include "config.h" +#include "wtf/ASCIICType.h" + +#undef LOG #include "webkit/glue/webplugin_impl.h" // TODO(pinkerton): all of this needs to be filled in. webplugin_impl.cc has diff --git a/webkit/glue/webview.h b/webkit/glue/webview.h index e47ad0e..1c67c4f4 100644 --- a/webkit/glue/webview.h +++ b/webkit/glue/webview.h @@ -6,6 +6,7 @@ #define WEBKIT_GLUE_WEBVIEW_H__ #include <string> +#include <vector> #include "base/basictypes.h" #include "base/ref_counted.h" @@ -195,6 +196,12 @@ class WebView : public WebWidget { virtual void DragTargetDrop( int client_x, int client_y, int screen_x, int screen_y) = 0; + // Notifies the webview that autofill suggestions are available for a node. + virtual void AutofillSuggestionsForNode( + int64 node_id, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index) = 0; + private: DISALLOW_EVIL_CONSTRUCTORS(WebView); }; diff --git a/webkit/glue/webview_delegate.h b/webkit/glue/webview_delegate.h index b9f2b9e..415510e 100644 --- a/webkit/glue/webview_delegate.h +++ b/webkit/glue/webview_delegate.h @@ -107,7 +107,8 @@ class WebViewDelegate : virtual public WebWidgetDelegate { // This method is called to create a new WebWidget to act as a popup // (like a drop-down menu). - virtual WebWidget* CreatePopupWidget(WebView* webview) { + virtual WebWidget* CreatePopupWidget(WebView* webview, + bool focus_on_show) { return NULL; } @@ -450,6 +451,14 @@ class WebViewDelegate : virtual public WebWidgetDelegate { virtual void OnUnloadListenerChanged(WebView* webview, WebFrame* webframe) { } + // Queries the browser for suggestions to be shown for the form text field + // named |field_name|. |text| is the text entered by the user so far and + // |node_id| is the id of the node of the input field. + virtual void QueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& text, + int64 node_id) { + } + // UIDelegate -------------------------------------------------------------- // Asks the browser to show a modal HTML dialog. The dialog is passed the diff --git a/webkit/glue/webview_impl.cc b/webkit/glue/webview_impl.cc index 814d168..5d3101c 100644 --- a/webkit/glue/webview_impl.cc +++ b/webkit/glue/webview_impl.cc @@ -35,6 +35,7 @@ #include "base/compiler_specific.h" MSVC_PUSH_WARNING_LEVEL(0); +#include "CSSStyleSelector.h" #if defined(OS_WIN) #include "Cursor.h" #endif @@ -50,6 +51,8 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "FrameTree.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLInputElement.h" #include "HitTestResult.h" #include "Image.h" #include "InspectorController.h" @@ -61,9 +64,11 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "PlatformMouseEvent.h" #include "PlatformWheelEvent.h" #include "PluginInfoStore.h" +#include "PopupMenuClient.h" #if defined(OS_WIN) #include "RenderThemeWin.h" #endif +#include "RenderView.h" #include "ResourceHandle.h" #include "SelectionController.h" #include "Settings.h" @@ -94,6 +99,7 @@ MSVC_POP_WARNING(); #include "webkit/glue/webview_delegate.h" #include "webkit/glue/webview_impl.h" #include "webkit/glue/webwidget_impl.h" +#include "webkit/port/platform/chromium/PopupMenuChromium.h" #include "webkit/port/platform/graphics/PlatformContextSkia.h" // Get rid of WTF's pow define so we can use std::pow. @@ -115,6 +121,126 @@ static const double kMaxTextSizeMultiplier = 3.0; static const WebCore::DragOperation kDropTargetOperation = static_cast<WebCore::DragOperation>(DragOperationCopy | DragOperationLink); +// AutocompletePopupMenuClient +class AutocompletePopupMenuClient + : public RefCounted<AutocompletePopupMenuClient>, + public WebCore::PopupMenuClient { + public: + AutocompletePopupMenuClient(WebViewImpl* webview, + WebCore::HTMLInputElement* text_field, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index) + : text_field_(text_field), + selected_index_(default_suggestion_index), + webview_(webview) { + for (std::vector<std::wstring>::const_iterator iter = suggestions.begin(); + iter != suggestions.end(); ++iter) { + suggestions_.push_back(webkit_glue::StdWStringToString(*iter)); + } + } + virtual ~AutocompletePopupMenuClient() { + } + + virtual void valueChanged(unsigned listIndex, bool fireEvents = true) { + text_field_->setValue(suggestions_[listIndex]); + } + + virtual WebCore::String itemText(unsigned list_index) const { + return suggestions_[list_index]; + } + + virtual bool itemIsEnabled(unsigned listIndex) const { + return true; + } + + virtual PopupMenuStyle itemStyle(unsigned listIndex) const { + return menuStyle(); + } + + virtual PopupMenuStyle menuStyle() const { + RenderStyle* style = text_field_->renderStyle() ? + text_field_->renderStyle() : + text_field_->computedStyle(); + return PopupMenuStyle(style->color(), Color::white, style->font(), + style->visibility() == VISIBLE); + } + + virtual int clientInsetLeft() const { + return 0; + } + virtual int clientInsetRight() const { + return 0; + } + virtual int clientPaddingLeft() const { +#if defined(OS_WIN) + return theme()->popupInternalPaddingLeft(text_field_->computedStyle()); +#else + NOTIMPLEMENTED(); + return 0; +#endif + } + virtual int clientPaddingRight() const { +#if defined(OS_WIN) + return theme()->popupInternalPaddingRight(text_field_->computedStyle()); +#else + NOTIMPLEMENTED(); + return 0; +#endif + } + virtual int listSize() const { + return suggestions_.size(); + } + virtual int selectedIndex() const { + return selected_index_; + } + virtual void hidePopup() { + webview_->HideAutoCompletePopup(); + } + virtual bool itemIsSeparator(unsigned listIndex) const { + return false; + } + virtual bool itemIsLabel(unsigned listIndex) const { + return false; + } + virtual bool itemIsSelected(unsigned listIndex) const { + return false; + } + virtual bool shouldPopOver() const { + return false; + } + virtual bool valueShouldChangeOnHotTrack() const { + return false; + } + + virtual FontSelector* fontSelector() const { + return text_field_->document()->styleSelector()->fontSelector(); + } + + virtual void setTextFromItem(unsigned listIndex) { + text_field_->setValue(suggestions_[listIndex]); + } + + virtual HostWindow* hostWindow() const { + return text_field_->document()->view()->hostWindow(); + } + + virtual PassRefPtr<Scrollbar> createScrollbar( + ScrollbarClient* client, + ScrollbarOrientation orientation, + ScrollbarControlSize size) { + RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(client, + orientation, + size); + return widget.release(); + } + + private: + RefPtr<WebCore::HTMLInputElement> text_field_; + std::vector<WebCore::String> suggestions_; + int selected_index_; + WebViewImpl* webview_; +}; + // WebView ---------------------------------------------------------------- /*static*/ @@ -292,6 +418,18 @@ bool WebViewImpl::KeyEvent(const WebKeyboardEvent& event) { // event. suppress_next_keypress_event_ = false; + // Give autocomplete a chance to consume the key events it is interested in. + if (autocomplete_popup_ && + autocomplete_popup_->isInterestedInEventForKey(event.key_code)) { + if (autocomplete_popup_->handleKeyEvent(MakePlatformKeyboardEvent(event))) + return true; + return false; + } + + // A new key being pressed should hide the popup. + if (event.type == WebInputEvent::KEY_DOWN) + HideAutoCompletePopup(); + Frame* frame = GetFocusedWebCoreFrame(); if (!frame) return false; @@ -714,7 +852,6 @@ bool WebViewImpl::HandleInputEvent(const WebInputEvent* input_event) { // we're done. if (doing_drag_and_drop_) return true; - // TODO(eseidel): Remove g_current_input_event. // This only exists to allow ChromeClient::show() to know which mouse button // triggered a window.open event. @@ -782,6 +919,14 @@ void WebViewImpl::SetBackForwardListSize(int size) { void WebViewImpl::SetFocus(bool enable) { if (enable) { + // Hide the popup menu if any. + // TODO(jcampan): bug #3844: we should do that when we lose focus. The + // reason we are not doing it is because when clicking on the autofill + // popup, the page first loses focus before the mouse click is sent to the + // popup. So if we close when the focus is lost, the mouse click does not + // do anything. + HideAutoCompletePopup(); + // Getting the focused frame will have the side-effect of setting the main // frame as the focused frame if it is not already focused. Otherwise, if // there is already a focused frame, then this does nothing. @@ -803,8 +948,6 @@ void WebViewImpl::SetFocus(bool enable) { // updated below. ReleaseFocusReferences(); - // Clear focus on the currently focused frame if any. - if (!main_frame_) return; @@ -1020,7 +1163,8 @@ void WebViewImpl::SetInitialFocus(bool reverse) { // We have to set the key type explicitly to avoid an assert in the // KeyboardEvent constructor. platform_event.SetKeyType(PlatformKeyboardEvent::RawKeyDown); - RefPtr<KeyboardEvent> webkit_event = KeyboardEvent::create(platform_event, NULL); + RefPtr<KeyboardEvent> webkit_event = KeyboardEvent::create(platform_event, + NULL); page()->focusController()->setInitialFocus( reverse ? WebCore::FocusDirectionBackward : WebCore::FocusDirectionForward, @@ -1333,6 +1477,59 @@ SearchableFormData* WebViewImpl::CreateSearchableFormDataForFocusedNode() { return NULL; } +void WebViewImpl::AutofillSuggestionsForNode( + int64 node_id, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index) { + if (!main_frame_ || suggestions.empty()) + return; + + DCHECK(default_suggestion_index < static_cast<int>(suggestions.size())); + + Frame* frame = main_frame_->frame(); + if (!frame) + return; + + if (RefPtr<Frame> focused = + frame->page()->focusController()->focusedFrame()) { + RefPtr<Document> document = focused->document(); + if (!document.get()) + return; + + RefPtr<Node> focused_node = document->focusedNode(); + // If the node for which we queried the autofill suggestions is not the + // focused node, then we have nothing to do. + // TODO(jcampan): also check the carret is at the end and that the text has + // not changed. + if (!focused_node.get() || + reinterpret_cast<int64>(focused_node.get()) != node_id) + return; + + if (!focused_node->hasTagName(WebCore::HTMLNames::inputTag)) { + NOTREACHED(); + return; + } + + WebCore::HTMLInputElement* input_elem = + static_cast<WebCore::HTMLInputElement*>(focused_node.get()); + // Hide any current autocomplete popup. + HideAutoCompletePopup(); + + if (suggestions.size() > 0) { + autocomplete_popup_client_ = + adoptRef(new AutocompletePopupMenuClient(this, input_elem, + suggestions, + default_suggestion_index)); + // Autocomplete popup does not get focused. We need the page to still + // have focus so the user can keep typing when the popup is showing. + autocomplete_popup_ = + WebCore::PopupContainer::create(autocomplete_popup_client_.get(), + false); + autocomplete_popup_->show(focused_node->getRect(), frame->view(), 0); + } + } +} + void WebViewImpl::DidCommitLoad(bool* is_new_navigation) { if (is_new_navigation) *is_new_navigation = observed_new_navigation_; @@ -1481,3 +1678,11 @@ void WebViewImpl::DeleteImageResourceFetcher(ImageResourceFetcher* fetcher) { // deletion. MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher); } + +void WebViewImpl::HideAutoCompletePopup() { + if (autocomplete_popup_) { + autocomplete_popup_->hidePopup(); + autocomplete_popup_.clear(); + autocomplete_popup_client_.clear(); + } +} diff --git a/webkit/glue/webview_impl.h b/webkit/glue/webview_impl.h index 9bc6c4b..5d66def 100644 --- a/webkit/glue/webview_impl.h +++ b/webkit/glue/webview_impl.h @@ -27,10 +27,12 @@ class HistoryItem; class KeyboardEvent; class Page; class PlatformKeyboardEvent; +class PopupContainer; class Range; class Widget; } +class AutocompletePopupMenuClient; class ImageResourceFetcher; class SearchableFormData; struct WebDropData; @@ -96,6 +98,10 @@ class WebViewImpl : public WebView, public WebCore::BackForwardListClient { virtual void DragTargetDragLeave(); virtual void DragTargetDrop( int client_x, int client_y, int screen_x, int screen_y); + virtual void AutofillSuggestionsForNode( + int64 node_id, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index); // WebViewImpl @@ -176,6 +182,9 @@ class WebViewImpl : public WebView, public WebCore::BackForwardListClient { bool errored, const SkBitmap& image); + // Hides the autocomplete popup if it is showing. + void HideAutoCompletePopup(); + protected: friend class WebView; // So WebView::Create can call our constructor @@ -285,6 +294,13 @@ class WebViewImpl : public WebView, public WebCore::BackForwardListClient { // Represents whether or not this object should process incoming IME events. bool ime_accept_events_; + // The currently shown autocomplete popup. + RefPtr<WebCore::PopupContainer> autocomplete_popup_; + + // The popup client of the currently shown autocomplete popup. Necessary for + // managing the life of the client. + RefPtr<AutocompletePopupMenuClient> autocomplete_popup_client_; + // HACK: current_input_event is for ChromeClientImpl::show(), until we can fix // WebKit to pass enough information up into ChromeClient::show() so we can // decide if the window.open event was caused by a middle-mouse click diff --git a/webkit/port/page/chromium/ChromeClientChromium.h b/webkit/port/page/chromium/ChromeClientChromium.h index 66e08b0..31633ae 100644 --- a/webkit/port/page/chromium/ChromeClientChromium.h +++ b/webkit/port/page/chromium/ChromeClientChromium.h @@ -26,7 +26,9 @@ namespace WebCore { // Notifies the client of a new popup widget. The client should place // and size the widget with the given bounds, relative to the screen. - virtual void popupOpened(FramelessScrollView* popupView, const IntRect& bounds) = 0; + virtual void popupOpened(FramelessScrollView* popupView, + const IntRect& bounds, + bool focus_on_show) = 0; // Set the current cursor. virtual void setCursor(const Cursor& cursor) = 0; diff --git a/webkit/port/platform/chromium/PopupMenuChromium.cpp b/webkit/port/platform/chromium/PopupMenuChromium.cpp index d30df68..ae9f164 100644 --- a/webkit/port/platform/chromium/PopupMenuChromium.cpp +++ b/webkit/port/platform/chromium/PopupMenuChromium.cpp @@ -36,9 +36,9 @@ #include "ChromeClientChromium.h" #include "Document.h" #include "Font.h" -#include "Frame.h" #include "FrameView.h" #include "FontSelector.h" +#include "Frame.h" #include "FramelessScrollView.h" #include "FramelessScrollViewClient.h" #include "GraphicsContext.h" @@ -57,6 +57,8 @@ #include "Widget.h" #pragma warning(pop) +#include "webkit/port/platform/chromium/PopupMenuChromium.h" + using namespace WTF; using namespace Unicode; @@ -72,51 +74,6 @@ static const int kMaxHeight = 500; static const int kBorderSize = 1; static const TimeStamp kTypeAheadTimeoutMs = 1000; -class PopupListBox; - -// TODO(darin): Our FramelessScrollView classes need to implement HostWindow! - -// This class holds a PopupListBox. Its sole purpose is to be able to draw -// a border around its child. All its paint/event handling is just forwarded -// to the child listBox (with the appropriate transforms). -class PopupContainer : public FramelessScrollView, public RefCounted<PopupContainer> { -public: - static PassRefPtr<PopupContainer> create(PopupMenuClient* client); - - // FramelessScrollView - virtual void paint(GraphicsContext* gc, const IntRect& rect); - virtual void hide(); - virtual bool handleMouseDownEvent(const PlatformMouseEvent& event); - virtual bool handleMouseMoveEvent(const PlatformMouseEvent& event); - virtual bool handleMouseReleaseEvent(const PlatformMouseEvent& event); - virtual bool handleWheelEvent(const PlatformWheelEvent& event); - virtual bool handleKeyEvent(const PlatformKeyboardEvent& event); - - // PopupContainer methods - - // Show the popup - void showPopup(FrameView* view); - - // Hide the popup. Do not call this directly: use client->hidePopup(). - void hidePopup(); - - // Compute size of widget and children. - void layout(); - - PopupListBox* listBox() const { return m_listBox.get(); } - -private: - friend class RefCounted<PopupContainer>; - - PopupContainer(PopupMenuClient* client); - ~PopupContainer(); - - // Paint the border. - void paintBorder(GraphicsContext* gc, const IntRect& rect); - - RefPtr<PopupListBox> m_listBox; -}; - // This class uses WebCore code to paint and handle events for a drop-down list // box ("combobox" on Windows). class PopupListBox : public FramelessScrollView, public RefCounted<PopupListBox> { @@ -170,6 +127,9 @@ public: // Compute size of widget and children. void layout(); + // Returns whether the popup wants to process events for the passed key. + bool isInterestedInEventForKey(int key_code); + private: friend class PopupContainer; friend class RefCounted<PopupListBox>; @@ -327,13 +287,15 @@ static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& // PopupContainer implementation // static -PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client) +PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, + bool focusOnShow) { - return adoptRef(new PopupContainer(client)); + return adoptRef(new PopupContainer(client, focusOnShow)); } -PopupContainer::PopupContainer(PopupMenuClient* client) - : m_listBox(new PopupListBox(client)) +PopupContainer::PopupContainer(PopupMenuClient* client, bool focusOnShow) + : m_listBox(new PopupListBox(client)), + m_focusOnShow(focusOnShow) { // FrameViews are created with a refcount of 1 so it needs releasing after we // assign it to a RefPtr. @@ -367,7 +329,7 @@ void PopupContainer::showPopup(FrameView* view) if (widgetRect.bottom() > static_cast<int>(screen.bottom())) widgetRect.move(0, -(widgetRect.height() + selectHeight)); - chromeClient->popupOpened(this, widgetRect); + chromeClient->popupOpened(this, widgetRect, m_focusOnShow); } // Must get called after we have a client and containingWindow. @@ -474,6 +436,33 @@ void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect) gc->drawRect(IntRect(tx + width() - kBorderSize, ty, kBorderSize, height())); } +bool PopupContainer::isInterestedInEventForKey(int key_code) { + return m_listBox->isInterestedInEventForKey(key_code); +} + +void PopupContainer::show(const IntRect& r, FrameView* v, int index) { + // The rect is the size of the select box. It's usually larger than we need. + // subtract border size so that usually the container will be displayed + // exactly the same width as the select box. + listBox()->setBaseWidth(max(r.width() - kBorderSize * 2, 0)); + + listBox()->updateFromElement(); + + // We set the selected item in updateFromElement(), and disregard the + // index passed into this function (same as Webkit's PopupMenuWin.cpp) + // TODO(ericroman): make sure this is correct, and add an assertion. + // DCHECK(popupWindow(popup)->listBox()->selectedIndex() == index); + + // Convert point to main window coords. + IntPoint location = v->contentsToWindow(r.location()); + + // Move it below the select widget. + location.move(0, r.height()); + + IntRect popupRect(location, r.size()); + setFrameRect(popupRect); + showPopup(v); +} /////////////////////////////////////////////////////////////////////////////// // PopupListBox implementation @@ -548,6 +537,23 @@ bool PopupListBox::handleWheelEvent(const PlatformWheelEvent& event) return true; } +// Should be kept in sync with handleKeyEvent(). +bool PopupListBox::isInterestedInEventForKey(int key_code) { + switch (key_code) { + case VKEY_ESCAPE: + case VKEY_RETURN: + case VKEY_UP: + case VKEY_DOWN: + case VKEY_PRIOR: + case VKEY_NEXT: + case VKEY_HOME: + case VKEY_END: + return true; + default: + return false; + } +} + bool PopupListBox::handleKeyEvent(const PlatformKeyboardEvent& event) { if (event.type() == PlatformKeyboardEvent::KeyUp) @@ -1036,29 +1042,8 @@ PopupMenu::~PopupMenu() void PopupMenu::show(const IntRect& r, FrameView* v, int index) { - p.m_popup = PopupContainer::create(client()); - - // The rect is the size of the select box. It's usually larger than we need. - // subtract border size so that usually the container will be displayed - // exactly the same width as the select box. - p.m_popup->listBox()->setBaseWidth(max(r.width() - kBorderSize * 2, 0)); - - updateFromElement(); - - // We set the selected item in updateFromElement(), and disregard the - // index passed into this function (same as Webkit's PopupMenuWin.cpp) - // TODO(ericroman): make sure this is correct, and add an assertion. - // DCHECK(popupWindow(m_popup)->listBox()->selectedIndex() == index); - - // Convert point to main window coords. - IntPoint location = v->contentsToWindow(r.location()); - - // Move it below the select widget. - location.move(0, r.height()); - - IntRect popupRect(location, r.size()); - p.m_popup->setFrameRect(popupRect); - p.m_popup->showPopup(v); + p.m_popup = PopupContainer::create(client(), true); + p.m_popup->show(r, v, index); } void PopupMenu::hide() diff --git a/webkit/port/platform/chromium/PopupMenuChromium.h b/webkit/port/platform/chromium/PopupMenuChromium.h new file mode 100644 index 0000000..d419468 --- /dev/null +++ b/webkit/port/platform/chromium/PopupMenuChromium.h @@ -0,0 +1,82 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PopupMenuChromium_h +#define PopupMenuChromium_h + +#pragma warning(push, 0) +#include "PopupMenuClient.h" + +#include "FramelessScrollView.h" +#include "IntRect.h" +#pragma warning(pop) + + +namespace WebCore { + +class PopupListBox; + +// TODO(darin): Our FramelessScrollView classes need to implement HostWindow! + +// This class holds a PopupListBox (see cpp file). Its sole purpose is to be +// able to draw a border around its child. All its paint/event handling is just +// forwarded to the child listBox (with the appropriate transforms). +// NOTE: this class is exposed so it can be instantiated direcly for the +// autofill popup. We cannot use the Popup class directly in that case as the +// autofill popup should not be focused when shown and we want to forward the +// key events to it (through handleKeyEvent). + +class PopupContainer : public FramelessScrollView, public RefCounted<PopupContainer> { +public: + static PassRefPtr<PopupContainer> create(PopupMenuClient* client, + bool focusOnShow); + + // Whether a key event should be sent to this popup. + virtual bool isInterestedInEventForKey(int key_code); + + // FramelessScrollView + virtual void paint(GraphicsContext* gc, const IntRect& rect); + virtual void hide(); + virtual bool handleMouseDownEvent(const PlatformMouseEvent& event); + virtual bool handleMouseMoveEvent(const PlatformMouseEvent& event); + virtual bool handleMouseReleaseEvent(const PlatformMouseEvent& event); + virtual bool handleWheelEvent(const PlatformWheelEvent& event); + virtual bool handleKeyEvent(const PlatformKeyboardEvent& event); + + // PopupContainer methods + + // Show the popup + void showPopup(FrameView* view); + + // Show the popup in the specified rect for the specified frame. + // Note: this code was somehow arbitrarily factored-out of the Popup class + // so WebViewImpl can create a PopupContainer. + void show(const IntRect& r, FrameView* v, int index); + + // Hide the popup. Do not call this directly: use client->hidePopup(). + void hidePopup(); + + // Compute size of widget and children. + void layout(); + + PopupListBox* listBox() const { return m_listBox.get(); } + +private: + friend class WTF::RefCounted<PopupContainer>; + + PopupContainer(PopupMenuClient* client, bool focusOnShow); + ~PopupContainer(); + + // Paint the border. + void paintBorder(GraphicsContext* gc, const IntRect& rect); + + RefPtr<PopupListBox> m_listBox; + + // Whether the window showing this popup should be focused when shown. + bool m_focusOnShow; +}; + +} + +#endif // PopupMenuChromium_h |