Diff
Modified: trunk/Source/WebCore/ChangeLog (174102 => 174103)
--- trunk/Source/WebCore/ChangeLog 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebCore/ChangeLog 2014-09-30 13:28:07 UTC (rev 174103)
@@ -1,3 +1,16 @@
+2014-09-30 Carlos Garcia Campos <[email protected]>
+
+ [GTK] Move GtkPopupMenu implementation to WebPopupMenuProxyGtk
+ https://bugs.webkit.org/show_bug.cgi?id=137193
+
+ Reviewed by Gustavo Noronha Silva.
+
+ Remove GtkPopupMenu from platform.
+
+ * PlatformGTK.cmake:
+ * platform/gtk/GtkPopupMenu.cpp: Removed.
+ * platform/gtk/GtkPopupMenu.h: Removed.
+
2014-09-30 Zan Dobersek <[email protected]>
Avoid copying the iterated-over items in range-based for-loops in RenderGrid
Modified: trunk/Source/WebCore/PlatformGTK.cmake (174102 => 174103)
--- trunk/Source/WebCore/PlatformGTK.cmake 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebCore/PlatformGTK.cmake 2014-09-30 13:28:07 UTC (rev 174103)
@@ -225,7 +225,6 @@
platform/gtk/GtkClickCounter.cpp
platform/gtk/GtkDragAndDropHelper.cpp
platform/gtk/GtkInputMethodFilter.cpp
- platform/gtk/GtkPopupMenu.cpp
platform/gtk/GtkTouchContextHelper.cpp
platform/gtk/GtkUtilities.cpp
platform/gtk/GtkVersioning.c
Deleted: trunk/Source/WebCore/platform/gtk/GtkPopupMenu.cpp (174102 => 174103)
--- trunk/Source/WebCore/platform/gtk/GtkPopupMenu.cpp 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebCore/platform/gtk/GtkPopupMenu.cpp 2014-09-30 13:28:07 UTC (rev 174103)
@@ -1,234 +0,0 @@
-/*
- * This file is part of the popup menu implementation for <select> elements in WebCore.
- *
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
- * Copyright (C) 2006 Michael Emmel [email protected]
- * Copyright (C) 2008 Collabora Ltd.
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * Copyright (C) 2010-2011 Igalia S.L.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
-
-#include "config.h"
-#include "GtkPopupMenu.h"
-
-#include "GtkVersioning.h"
-#include <gtk/gtk.h>
-#include <wtf/gobject/GUniquePtr.h>
-#include <wtf/text/CString.h>
-
-namespace WebCore {
-
-static const uint32_t gSearchTimeoutMs = 1000;
-
-GtkPopupMenu::GtkPopupMenu()
- : m_popup(gtk_menu_new())
- , m_previousKeyEventCharacter(0)
- , m_currentlySelectedMenuItem(0)
-{
- m_keyPressHandlerID = g_signal_connect(m_popup.get(), "key-press-event", G_CALLBACK(GtkPopupMenu::keyPressEventCallback), this);
-}
-
-GtkPopupMenu::~GtkPopupMenu()
-{
- g_signal_handler_disconnect(m_popup.get(), m_keyPressHandlerID);
-}
-
-void GtkPopupMenu::clear()
-{
- gtk_container_foreach(GTK_CONTAINER(m_popup.get()), reinterpret_cast<GtkCallback>(menuRemoveItem), this);
-}
-
-void GtkPopupMenu::appendSeparator()
-{
- GtkWidget* menuItem = gtk_separator_menu_item_new();
- gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), menuItem);
- gtk_widget_show(menuItem);
-}
-
-void GtkPopupMenu::appendItem(GtkAction* action)
-{
- GtkWidget* menuItem = gtk_action_create_menu_item(action);
- gtk_widget_set_tooltip_text(menuItem, gtk_action_get_tooltip(action));
- g_signal_connect(menuItem, "select", G_CALLBACK(GtkPopupMenu::selectItemCallback), this);
- gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), menuItem);
-
- if (gtk_action_is_visible(action))
- gtk_widget_show(menuItem);
-}
-
-void GtkPopupMenu::popUp(const IntSize& menuSize, const IntPoint& menuPosition, int itemCount, int selectedItem, const GdkEvent* event)
-{
- resetTypeAheadFindState();
- m_menuPosition = menuPosition;
- gtk_menu_set_active(GTK_MENU(m_popup.get()), selectedItem);
-
- // This approach follows the one in gtkcombobox.c.
- GtkRequisition requisition;
- gtk_widget_set_size_request(m_popup.get(), -1, -1);
-#ifdef GTK_API_VERSION_2
- gtk_widget_size_request(m_popup.get(), &requisition);
-#else
- gtk_widget_get_preferred_size(m_popup.get(), &requisition, 0);
-#endif
- gtk_widget_set_size_request(m_popup.get(), std::max(menuSize.width(), requisition.width), -1);
-
- if (itemCount) {
- GUniquePtr<GList> children(gtk_container_get_children(GTK_CONTAINER(m_popup.get())));
- int i;
- GList* child;
- for (i = 0, child = children.get(); i < itemCount; i++, child = g_list_next(child)) {
- if (i > selectedItem)
- break;
-
- GtkWidget* item = GTK_WIDGET(child->data);
- GtkRequisition itemRequisition;
-#ifdef GTK_API_VERSION_2
- gtk_widget_get_child_requisition(item, &itemRequisition);
-#else
- gtk_widget_get_preferred_size(item, &itemRequisition, 0);
-#endif
- m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height);
- }
- } else {
- // Center vertically the empty popup in the combo box area.
- m_menuPosition.setY(m_menuPosition.y() - menuSize.height() / 2);
- }
-
- guint button = event && event->type == GDK_BUTTON_PRESS ? event->button.button : 1;
- guint32 activateTime = event ? gdk_event_get_time(event) : GDK_CURRENT_TIME;
-#ifdef GTK_API_VERSION_2
- gtk_menu_popup(GTK_MENU(m_popup.get()), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, button, activateTime);
-#else
- gtk_menu_popup_for_device(GTK_MENU(m_popup.get()), event ? gdk_event_get_device(event) : 0, 0, 0,
- reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, button, activateTime);
-#endif
-
- // Now that the menu has a position, schedule a resize to make sure it's resized to fit vertically in the work area.
- gtk_widget_queue_resize(m_popup.get());
-}
-
-void GtkPopupMenu::popDown()
-{
- gtk_menu_popdown(GTK_MENU(m_popup.get()));
- resetTypeAheadFindState();
-}
-
-void GtkPopupMenu::menuRemoveItem(GtkWidget* widget, GtkPopupMenu* popupMenu)
-{
- ASSERT(popupMenu->m_popup);
- gtk_container_remove(GTK_CONTAINER(popupMenu->m_popup.get()), widget);
-}
-
-void GtkPopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, GtkPopupMenu* popupMenu)
-{
- *x = popupMenu->m_menuPosition.x();
- *y = popupMenu->m_menuPosition.y();
- *pushIn = true;
-}
-
-void GtkPopupMenu::resetTypeAheadFindState()
-{
- m_currentlySelectedMenuItem = 0;
- m_previousKeyEventCharacter = 0;
- m_currentSearchString = "";
-}
-
-bool GtkPopupMenu::typeAheadFind(GdkEventKey* event)
-{
- // If we were given a non-printable character just skip it.
- gunichar unicodeCharacter = gdk_keyval_to_unicode(event->keyval);
- if (!g_unichar_isprint(unicodeCharacter)) {
- resetTypeAheadFindState();
- return false;
- }
-
- glong charactersWritten;
- GUniquePtr<gunichar2> utf16String(g_ucs4_to_utf16(&unicodeCharacter, 1, 0, &charactersWritten, 0));
- if (!utf16String) {
- resetTypeAheadFindState();
- return false;
- }
-
- // If the character is the same as the last character, the user is probably trying to
- // cycle through the menulist entries. This matches the WebCore behavior for collapsed
- // menulists.
- bool repeatingCharacter = unicodeCharacter != m_previousKeyEventCharacter;
- if (event->time - m_previousKeyEventTimestamp > gSearchTimeoutMs)
- m_currentSearchString = String(reinterpret_cast<UChar*>(utf16String.get()), charactersWritten);
- else if (repeatingCharacter)
- m_currentSearchString.append(String(reinterpret_cast<UChar*>(utf16String.get()), charactersWritten));
-
- m_previousKeyEventTimestamp = event->time;
- m_previousKeyEventCharacter = unicodeCharacter;
-
- // Like the Chromium port, we case fold before searching, because
- // strncmp does not handle non-ASCII characters.
- GUniquePtr<gchar> searchStringWithCaseFolded(g_utf8_casefold(m_currentSearchString.utf8().data(), -1));
- size_t prefixLength = strlen(searchStringWithCaseFolded.get());
-
- GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get()));
- if (!children)
- return true;
-
- // If a menu item has already been selected, start searching from the current
- // item down the list. This will make multiple key presses of the same character
- // advance the selection.
- GList* currentChild = children;
- if (m_currentlySelectedMenuItem) {
- currentChild = g_list_find(children, m_currentlySelectedMenuItem);
- if (!currentChild) {
- m_currentlySelectedMenuItem = 0;
- currentChild = children;
- }
-
- // Repeating characters should iterate.
- if (repeatingCharacter) {
- if (GList* nextChild = g_list_next(currentChild))
- currentChild = nextChild;
- }
- }
-
- GList* firstChild = currentChild;
- do {
- currentChild = g_list_next(currentChild);
- if (!currentChild)
- currentChild = children;
-
- GUniquePtr<gchar> itemText(g_utf8_casefold(gtk_menu_item_get_label(GTK_MENU_ITEM(currentChild->data)), -1));
- if (!strncmp(searchStringWithCaseFolded.get(), itemText.get(), prefixLength)) {
- gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup.get()), GTK_WIDGET(currentChild->data));
- break;
- }
- } while (currentChild != firstChild);
-
- g_list_free(children);
- return true;
-}
-
-void GtkPopupMenu::selectItemCallback(GtkMenuItem* item, GtkPopupMenu* popupMenu)
-{
- popupMenu->m_currentlySelectedMenuItem = GTK_WIDGET(item);
-}
-
-gboolean GtkPopupMenu::keyPressEventCallback(GtkWidget*, GdkEventKey* event, GtkPopupMenu* popupMenu)
-{
- return popupMenu->typeAheadFind(event);
-}
-
-} // namespace WebCore
Deleted: trunk/Source/WebCore/platform/gtk/GtkPopupMenu.h (174102 => 174103)
--- trunk/Source/WebCore/platform/gtk/GtkPopupMenu.h 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebCore/platform/gtk/GtkPopupMenu.h 2014-09-30 13:28:07 UTC (rev 174103)
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * Copyright (C) 2011 Igalia S.L.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef GtkPopupMenu_h
-#define GtkPopupMenu_h
-
-#include "GRefPtrGtk.h"
-#include "IntPoint.h"
-#include "IntSize.h"
-#include <wtf/FastMalloc.h>
-#include <wtf/Noncopyable.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/text/WTFString.h>
-
-typedef struct _GdkEventKey GdkEventKey;
-
-namespace WebCore {
-
-class GtkPopupMenu {
- WTF_MAKE_NONCOPYABLE(GtkPopupMenu);
- WTF_MAKE_FAST_ALLOCATED;
-
-public:
- static PassOwnPtr<GtkPopupMenu> create()
- {
- return adoptPtr(new GtkPopupMenu());
- }
-
- ~GtkPopupMenu();
-
- GtkWidget* platformMenu() const { return m_popup.get(); }
- void clear();
- void appendSeparator();
- void appendItem(GtkAction*);
- void popUp(const IntSize&, const IntPoint&, int itemsCount, int selectedItem, const GdkEvent*);
- void popDown();
-
-private:
- GtkPopupMenu();
-
- void resetTypeAheadFindState();
- bool typeAheadFind(GdkEventKey*);
-
- static void menuItemActivated(GtkMenuItem*, GtkPopupMenu*);
- static void menuPositionFunction(GtkMenu*, gint*, gint*, gboolean*, GtkPopupMenu*);
- static void menuRemoveItem(GtkWidget*, GtkPopupMenu*);
- static void selectItemCallback(GtkMenuItem*, GtkPopupMenu*);
- static gboolean keyPressEventCallback(GtkWidget*, GdkEventKey*, GtkPopupMenu*);
-
- GRefPtr<GtkWidget> m_popup;
- IntPoint m_menuPosition;
- String m_currentSearchString;
- uint32_t m_previousKeyEventTimestamp;
- unsigned int m_previousKeyEventCharacter;
- GtkWidget* m_currentlySelectedMenuItem;
- unsigned int m_keyPressHandlerID;
-};
-
-}
-
-#endif // GtkPopupMenu_h
Modified: trunk/Source/WebKit2/ChangeLog (174102 => 174103)
--- trunk/Source/WebKit2/ChangeLog 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebKit2/ChangeLog 2014-09-30 13:28:07 UTC (rev 174103)
@@ -1,3 +1,28 @@
+2014-09-30 Carlos Garcia Campos <[email protected]>
+
+ [GTK] Move GtkPopupMenu implementation to WebPopupMenuProxyGtk
+ https://bugs.webkit.org/show_bug.cgi?id=137193
+
+ Reviewed by Gustavo Noronha Silva.
+
+ GtkPopupMenu was in platform only to have a common implementation to
+ be shared with WebKit1. Now that it's only used by
+ WebPopupMenuProxyGtk in WebKit2, we can simplify the code by
+ merging the implementation directly into WebPopupMenuProxyGtk.
+
+ * UIProcess/gtk/WebPopupMenuProxyGtk.cpp:
+ (WebKit::WebPopupMenuProxyGtk::WebPopupMenuProxyGtk):
+ (WebKit::WebPopupMenuProxyGtk::~WebPopupMenuProxyGtk):
+ (WebKit::WebPopupMenuProxyGtk::populatePopupMenu):
+ (WebKit::WebPopupMenuProxyGtk::showPopupMenu):
+ (WebKit::WebPopupMenuProxyGtk::hidePopupMenu):
+ (WebKit::WebPopupMenuProxyGtk::typeAheadFind):
+ (WebKit::WebPopupMenuProxyGtk::resetTypeAheadFindState):
+ (WebKit::WebPopupMenuProxyGtk::selectItemCallback):
+ (WebKit::WebPopupMenuProxyGtk::keyPressEventCallback):
+ * UIProcess/gtk/WebPopupMenuProxyGtk.h:
+ (WebKit::WebPopupMenuProxyGtk::setCurrentlySelectedMenuItem):
+
2014-09-29 Christophe Dumez <[email protected]>
Use is<>() / downcast<>() for Document
Modified: trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp (174102 => 174103)
--- trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp 2014-09-30 13:28:07 UTC (rev 174103)
@@ -29,6 +29,7 @@
#include "NativeWebMouseEvent.h"
#include "WebPopupItem.h"
#include <WebCore/GtkUtilities.h>
+#include <WebCore/IntRect.h>
#include <gtk/gtk.h>
#include <wtf/gobject/GUniquePtr.h>
#include <wtf/text/CString.h>
@@ -40,15 +41,21 @@
WebPopupMenuProxyGtk::WebPopupMenuProxyGtk(GtkWidget* webView, WebPopupMenuProxy::Client* client)
: WebPopupMenuProxy(client)
, m_webView(webView)
+ , m_popup(gtk_menu_new())
, m_activeItem(-1)
+ , m_previousKeyEventCharacter(0)
+ , m_previousKeyEventTimestamp(0)
+ , m_currentlySelectedMenuItem(nullptr)
{
+ g_signal_connect(m_popup, "key-press-event", G_CALLBACK(keyPressEventCallback), this);
}
WebPopupMenuProxyGtk::~WebPopupMenuProxyGtk()
{
if (m_popup) {
- g_signal_handlers_disconnect_matched(m_popup->platformMenu(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ g_signal_handlers_disconnect_matched(m_popup, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
hidePopupMenu();
+ gtk_widget_destroy(m_popup);
}
}
@@ -63,39 +70,89 @@
return action;
}
-void WebPopupMenuProxyGtk::showPopupMenu(const IntRect& rect, TextDirection, double /* pageScaleFactor */, const Vector<WebPopupItem>& items, const PlatformPopupMenuData&, int32_t selectedIndex)
+void WebPopupMenuProxyGtk::populatePopupMenu(const Vector<WebPopupItem>& items)
{
- if (m_popup)
- m_popup->clear();
- else
- m_popup = GtkPopupMenu::create();
+ int itemIndex = 0;
+ for (const auto& item : items) {
+ if (item.m_type == WebPopupItem::Separator) {
+ GtkWidget* menuItem = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_popup), menuItem);
+ gtk_widget_show(menuItem);
+ } else {
+ GRefPtr<GtkAction> action = "" itemIndex));
+ GtkWidget* menuItem = gtk_action_create_menu_item(action.get());
+ gtk_widget_set_tooltip_text(menuItem, gtk_action_get_tooltip(action.get()));
+ g_signal_connect(menuItem, "select", G_CALLBACK(selectItemCallback), this);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_popup), menuItem);
- const int size = items.size();
- for (int i = 0; i < size; i++) {
- if (items[i].m_type == WebPopupItem::Separator)
- m_popup->appendSeparator();
- else {
- GRefPtr<GtkAction> action = "" i));
- m_popup->appendItem(action.get());
+ if (gtk_action_is_visible(action.get()))
+ gtk_widget_show(menuItem);
}
+ itemIndex++;
}
+}
+void WebPopupMenuProxyGtk::showPopupMenu(const IntRect& rect, TextDirection, double /* pageScaleFactor */, const Vector<WebPopupItem>& items, const PlatformPopupMenuData&, int32_t selectedIndex)
+{
+ populatePopupMenu(items);
+ gtk_menu_set_active(GTK_MENU(m_popup), selectedIndex);
+
+ resetTypeAheadFindState();
+
+
IntPoint menuPosition = convertWidgetPointToScreenPoint(m_webView, rect.location());
menuPosition.move(0, rect.height());
- gulong unmapHandler = g_signal_connect(m_popup->platformMenu(), "unmap", G_CALLBACK(menuUnmapped), this);
- m_popup->popUp(rect.size(), menuPosition, size, selectedIndex, m_client->currentlyProcessedMouseDownEvent() ? m_client->currentlyProcessedMouseDownEvent()->nativeEvent() : 0);
+ // This approach follows the one in gtkcombobox.c.
+ GtkRequisition requisition;
+ gtk_widget_set_size_request(m_popup, -1, -1);
+ gtk_widget_get_preferred_size(m_popup, &requisition, nullptr);
+ gtk_widget_set_size_request(m_popup, std::max(rect.width(), requisition.width), -1);
+ if (int itemCount = items.size()) {
+ GUniquePtr<GList> children(gtk_container_get_children(GTK_CONTAINER(m_popup)));
+ int i;
+ GList* child;
+ for (i = 0, child = children.get(); i < itemCount; i++, child = g_list_next(child)) {
+ if (i > selectedIndex)
+ break;
+
+ GtkWidget* item = GTK_WIDGET(child->data);
+ GtkRequisition itemRequisition;
+ gtk_widget_get_preferred_size(item, &itemRequisition, nullptr);
+ menuPosition.setY(menuPosition.y() - itemRequisition.height);
+ }
+ } else {
+ // Center vertically the empty popup in the combo box area.
+ menuPosition.setY(menuPosition.y() - rect.height() / 2);
+ }
+
+ gulong unmapHandler = g_signal_connect(m_popup, "unmap", G_CALLBACK(menuUnmapped), this);
+
+ const GdkEvent* event = m_client->currentlyProcessedMouseDownEvent() ? m_client->currentlyProcessedMouseDownEvent()->nativeEvent() : nullptr;
+ gtk_menu_popup_for_device(GTK_MENU(m_popup), event ? gdk_event_get_device(event) : nullptr, nullptr, nullptr,
+ [](GtkMenu*, gint* x, gint* y, gboolean* pushIn, gpointer userData) {
+ // We can pass a pointer to the menuPosition local variable because the nested main loop ensures this is called in the function context.
+ IntPoint* menuPosition = static_cast<IntPoint*>(userData);
+ *x = menuPosition->x();
+ *y = menuPosition->y();
+ *pushIn = TRUE;
+ }, &menuPosition, nullptr, event && event->type == GDK_BUTTON_PRESS ? event->button.button : 1,
+ event ? gdk_event_get_time(event) : GDK_CURRENT_TIME);
+
+ // Now that the menu has a position, schedule a resize to make sure it's resized to fit vertically in the work area.
+ gtk_widget_queue_resize(m_popup);
+
// PopupMenu can fail to open when there is no mouse grab.
// Ensure WebCore does not go into some pesky state.
- if (!gtk_widget_get_visible(m_popup->platformMenu())) {
+ if (!gtk_widget_get_visible(m_popup)) {
m_client->failedToShowPopupMenu();
return;
}
// WebPageProxy expects the menu to run in a nested run loop, since it invalidates the
// menu right after calling WebPopupMenuProxy::showPopupMenu().
- m_runLoop = adoptGRef(g_main_loop_new(0, FALSE));
+ m_runLoop = adoptGRef(g_main_loop_new(nullptr, FALSE));
// This is to suppress warnings about gdk_threads_leave and gdk_threads_enter.
#pragma GCC diagnostic push
@@ -107,7 +164,7 @@
m_runLoop.clear();
- g_signal_handler_disconnect(m_popup->platformMenu(), unmapHandler);
+ g_signal_handler_disconnect(m_popup, unmapHandler);
if (!m_client)
return;
@@ -117,9 +174,88 @@
void WebPopupMenuProxyGtk::hidePopupMenu()
{
- m_popup->popDown();
+ gtk_menu_popdown(GTK_MENU(m_popup));
+ resetTypeAheadFindState();
}
+bool WebPopupMenuProxyGtk::typeAheadFind(GdkEventKey* event)
+{
+ // If we were given a non-printable character just skip it.
+ gunichar unicodeCharacter = gdk_keyval_to_unicode(event->keyval);
+ if (!g_unichar_isprint(unicodeCharacter)) {
+ resetTypeAheadFindState();
+ return false;
+ }
+
+ glong charactersWritten;
+ GUniquePtr<gunichar2> utf16String(g_ucs4_to_utf16(&unicodeCharacter, 1, nullptr, &charactersWritten, nullptr));
+ if (!utf16String) {
+ resetTypeAheadFindState();
+ return false;
+ }
+
+ // If the character is the same as the last character, the user is probably trying to
+ // cycle through the menulist entries. This matches the WebCore behavior for collapsed menulists.
+ static const uint32_t searchTimeoutMs = 1000;
+ bool repeatingCharacter = unicodeCharacter != m_previousKeyEventCharacter;
+ if (event->time - m_previousKeyEventTimestamp > searchTimeoutMs)
+ m_currentSearchString = String(reinterpret_cast<UChar*>(utf16String.get()), charactersWritten);
+ else if (repeatingCharacter)
+ m_currentSearchString.append(String(reinterpret_cast<UChar*>(utf16String.get()), charactersWritten));
+
+ m_previousKeyEventTimestamp = event->time;
+ m_previousKeyEventCharacter = unicodeCharacter;
+
+ GUniquePtr<GList> children(gtk_container_get_children(GTK_CONTAINER(m_popup)));
+ if (!children)
+ return true;
+
+ // We case fold before searching, because strncmp does not handle non-ASCII characters.
+ GUniquePtr<gchar> searchStringWithCaseFolded(g_utf8_casefold(m_currentSearchString.utf8().data(), -1));
+ size_t prefixLength = strlen(searchStringWithCaseFolded.get());
+
+ // If a menu item has already been selected, start searching from the current
+ // item down the list. This will make multiple key presses of the same character
+ // advance the selection.
+ GList* currentChild = children.get();
+ if (m_currentlySelectedMenuItem) {
+ currentChild = g_list_find(children.get(), m_currentlySelectedMenuItem);
+ if (!currentChild) {
+ m_currentlySelectedMenuItem = nullptr;
+ currentChild = children.get();
+ }
+
+ // Repeating characters should iterate.
+ if (repeatingCharacter) {
+ if (GList* nextChild = g_list_next(currentChild))
+ currentChild = nextChild;
+ }
+ }
+
+ GList* firstChild = currentChild;
+ do {
+ currentChild = g_list_next(currentChild);
+ if (!currentChild)
+ currentChild = children.get();
+
+ GUniquePtr<gchar> itemText(g_utf8_casefold(gtk_menu_item_get_label(GTK_MENU_ITEM(currentChild->data)), -1));
+ if (!strncmp(searchStringWithCaseFolded.get(), itemText.get(), prefixLength)) {
+ gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup), GTK_WIDGET(currentChild->data));
+ break;
+ }
+ } while (currentChild != firstChild);
+
+ return true;
+}
+
+void WebPopupMenuProxyGtk::resetTypeAheadFindState()
+{
+ m_currentlySelectedMenuItem = nullptr;
+ m_previousKeyEventCharacter = 0;
+ m_previousKeyEventTimestamp = 0;
+ m_currentSearchString = emptyString();
+}
+
void WebPopupMenuProxyGtk::shutdownRunLoop()
{
if (g_main_loop_is_running(m_runLoop.get()))
@@ -137,4 +273,14 @@
popupMenu->shutdownRunLoop();
}
+void WebPopupMenuProxyGtk::selectItemCallback(GtkWidget* item, WebPopupMenuProxyGtk* popupMenu)
+{
+ popupMenu->setCurrentlySelectedMenuItem(item);
+}
+
+gboolean WebPopupMenuProxyGtk::keyPressEventCallback(GtkWidget*, GdkEventKey* event, WebPopupMenuProxyGtk* popupMenu)
+{
+ return popupMenu->typeAheadFind(event);
+}
+
} // namespace WebKit
Modified: trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h (174102 => 174103)
--- trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h 2014-09-30 12:50:48 UTC (rev 174102)
+++ trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h 2014-09-30 13:28:07 UTC (rev 174103)
@@ -21,12 +21,16 @@
#define WebPopupMenuProxyGtk_h
#include "WebPopupMenuProxy.h"
-#include <WebCore/GtkPopupMenu.h>
-#include <WebCore/IntRect.h>
#include <wtf/gobject/GRefPtr.h>
+#include <wtf/text/WTFString.h>
typedef struct _GMainLoop GMainLoop;
+typedef struct _GdkEventKey GdkEventKey;
+namespace WebCore {
+class IntRect;
+}
+
namespace WebKit {
class WebPageProxy;
@@ -46,15 +50,28 @@
WebPopupMenuProxyGtk(GtkWidget*, WebPopupMenuProxy::Client*);
void shutdownRunLoop();
void setActiveItem(int activeItem) { m_activeItem = activeItem; }
+ void setCurrentlySelectedMenuItem(GtkWidget* item) { m_currentlySelectedMenuItem = item; }
GtkAction* createGtkActionForMenuItem(const WebPopupItem&, int itemIndex);
+ void populatePopupMenu(const Vector<WebPopupItem>&);
+ bool typeAheadFind(GdkEventKey*);
+ void resetTypeAheadFindState();
+
static void menuItemActivated(GtkAction*, WebPopupMenuProxyGtk*);
static void menuUnmapped(GtkWidget*, WebPopupMenuProxyGtk*);
+ static void selectItemCallback(GtkWidget*, WebPopupMenuProxyGtk*);
+ static gboolean keyPressEventCallback(GtkWidget*, GdkEventKey*, WebPopupMenuProxyGtk*);
GtkWidget* m_webView;
- OwnPtr<WebCore::GtkPopupMenu> m_popup;
+ GtkWidget* m_popup;
int m_activeItem;
GRefPtr<GMainLoop> m_runLoop;
+
+ // Typeahead find.
+ unsigned m_previousKeyEventCharacter;
+ uint32_t m_previousKeyEventTimestamp;
+ GtkWidget* m_currentlySelectedMenuItem;
+ String m_currentSearchString;
};
} // namespace WebKit