Index: webkit/glue/webview_impl.cc |
=================================================================== |
--- webkit/glue/webview_impl.cc (revision 4621) |
+++ webkit/glue/webview_impl.cc (working copy) |
@@ -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 @@ |
#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 @@ |
#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 @@ |
#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 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 @@ |
// 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 @@ |
// 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::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 @@ |
// updated below. |
ReleaseFocusReferences(); |
- // Clear focus on the currently focused frame if any. |
- |
if (!main_frame_) |
return; |
@@ -1020,7 +1163,8 @@ |
// 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 @@ |
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 @@ |
// deletion. |
MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher); |
} |
+ |
+void WebViewImpl::HideAutoCompletePopup() { |
+ if (autocomplete_popup_) { |
+ autocomplete_popup_->hidePopup(); |
+ autocomplete_popup_.clear(); |
+ autocomplete_popup_client_.clear(); |
+ } |
+} |