diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp | 204 |
1 files changed, 116 insertions, 88 deletions
diff --git a/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp b/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp index cc25e644b..5c6a8a08e 100644 --- a/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp +++ b/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp @@ -28,152 +28,180 @@ #if ENABLE(CONTEXT_MENUS) +#include "APIContextMenuClient.h" #include "NativeWebMouseEvent.h" +#include "WebContextMenuItem.h" #include "WebContextMenuItemData.h" #include "WebKitWebViewBasePrivate.h" #include "WebPageProxy.h" +#include "WebProcessProxy.h" #include <WebCore/GtkUtilities.h> +#include <gio/gio.h> #include <gtk/gtk.h> #include <wtf/text/CString.h> - static const char* gContextMenuActionId = "webkit-context-menu-action"; +static const char* gContextMenuTitle = "webkit-context-menu-title"; +static const char* gContextMenuItemGroup = "webkitContextMenu"; using namespace WebCore; namespace WebKit { -static void contextMenuItemActivatedCallback(GtkAction* action, WebPageProxy* page) +static void contextMenuItemActivatedCallback(GAction* action, GVariant*, WebPageProxy* page) { - gboolean isToggle = GTK_IS_TOGGLE_ACTION(action); - WebKit::WebContextMenuItemData item(isToggle ? WebCore::CheckableActionType : WebCore::ActionType, - static_cast<WebCore::ContextMenuAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), gContextMenuActionId))), - String::fromUTF8(gtk_action_get_label(action)), gtk_action_get_sensitive(action), - isToggle ? gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)) : false); + auto* stateType = g_action_get_state_type(action); + gboolean isToggle = stateType && g_variant_type_equal(stateType, G_VARIANT_TYPE_BOOLEAN); + GRefPtr<GVariant> state = isToggle ? adoptGRef(g_action_get_state(action)) : nullptr; + WebContextMenuItemData item(isToggle ? CheckableActionType : ActionType, + static_cast<ContextMenuAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), gContextMenuActionId))), + String::fromUTF8(static_cast<const char*>(g_object_get_data(G_OBJECT(action), gContextMenuTitle))), g_action_get_enabled(action), + state ? g_variant_get_boolean(state.get()) : false); page->contextMenuItemSelected(item); } -static void contextMenuItemVisibilityChanged(GtkAction* action, GParamSpec*, WebContextMenuProxyGtk* contextMenuProxy) +void WebContextMenuProxyGtk::append(GMenu* menu, const WebContextMenuItemGtk& menuItem) { - GtkMenu* menu = contextMenuProxy->gtkMenu(); - if (!menu) - return; - - GUniquePtr<GList> items(gtk_container_get_children(GTK_CONTAINER(menu))); - bool previousVisibleItemIsNotASeparator = false; - GtkWidget* lastItemVisibleSeparator = 0; - for (GList* iter = items.get(); iter; iter = g_list_next(iter)) { - GtkWidget* widget = GTK_WIDGET(iter->data); - - if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) { - if (previousVisibleItemIsNotASeparator) { - gtk_widget_show(widget); - lastItemVisibleSeparator = widget; - previousVisibleItemIsNotASeparator = false; - } else - gtk_widget_hide(widget); - } else if (gtk_widget_get_visible(widget)) { - lastItemVisibleSeparator = 0; - previousVisibleItemIsNotASeparator = true; - } + unsigned long signalHandlerId; + GRefPtr<GMenuItem> gMenuItem; + GAction* action = menuItem.gAction(); + ASSERT(action); +#if GTK_CHECK_VERSION(3, 16, 0) + g_action_map_add_action(G_ACTION_MAP(gtk_widget_get_action_group(GTK_WIDGET(m_menu), gContextMenuItemGroup)), action); +#else + g_action_map_add_action(G_ACTION_MAP(g_object_get_data(G_OBJECT(m_menu), gContextMenuItemGroup)), action); +#endif + + switch (menuItem.type()) { + case ActionType: + case CheckableActionType: { + GUniquePtr<char> actionName(g_strdup_printf("%s.%s", gContextMenuItemGroup, g_action_get_name(action))); + gMenuItem = adoptGRef(g_menu_item_new(menuItem.title().utf8().data(), nullptr)); + g_menu_item_set_action_and_target_value(gMenuItem.get(), actionName.get(), nullptr); + + g_object_set_data(G_OBJECT(action), gContextMenuActionId, GINT_TO_POINTER(menuItem.action())); + g_object_set_data_full(G_OBJECT(action), gContextMenuTitle, g_strdup(menuItem.title().utf8().data()), g_free); + signalHandlerId = g_signal_connect(action, "activate", G_CALLBACK(contextMenuItemActivatedCallback), m_page); + m_signalHandlers.set(signalHandlerId, action); + break; + } + case SubmenuType: { + GRefPtr<GMenu> submenu = buildMenu(menuItem.submenuItems()); + gMenuItem = adoptGRef(g_menu_item_new_submenu(menuItem.title().utf8().data(), G_MENU_MODEL(submenu.get()))); + break; + } + case SeparatorType: + ASSERT_NOT_REACHED(); + break; } - if (lastItemVisibleSeparator) - gtk_widget_hide(lastItemVisibleSeparator); + g_menu_append_item(menu, gMenuItem.get()); } -void WebContextMenuProxyGtk::append(ContextMenuItem& menuItem) +GRefPtr<GMenu> WebContextMenuProxyGtk::buildMenu(const Vector<WebContextMenuItemGtk>& items) { - unsigned long signalHandlerId; - GtkAction* action = menuItem.gtkAction(); - if (action) { - switch (menuItem.type()) { - case ActionType: - case CheckableActionType: - g_object_set_data(G_OBJECT(action), gContextMenuActionId, GINT_TO_POINTER(menuItem.action())); - signalHandlerId = g_signal_connect(action, "activate", G_CALLBACK(contextMenuItemActivatedCallback), m_page); - m_signalHandlers.set(signalHandlerId, action); - // Fall through. - case SubmenuType: - signalHandlerId = g_signal_connect(action, "notify::visible", G_CALLBACK(contextMenuItemVisibilityChanged), this); - m_signalHandlers.set(signalHandlerId, action); - break; - case SeparatorType: - break; - } + GRefPtr<GMenu> menu = adoptGRef(g_menu_new()); + GMenu* sectionMenu = menu.get(); + for (const auto& item : items) { + if (item.type() == SeparatorType) { + GRefPtr<GMenu> section = adoptGRef(g_menu_new()); + g_menu_append_section(menu.get(), nullptr, G_MENU_MODEL(section.get())); + sectionMenu = section.get(); + } else + append(sectionMenu, item); } - m_menu.appendItem(menuItem); + return menu; } -// Populate the context menu ensuring that: -// - There aren't separators next to each other. -// - There aren't separators at the beginning of the menu. -// - There aren't separators at the end of the menu. -void WebContextMenuProxyGtk::populate(Vector<ContextMenuItem>& items) +void WebContextMenuProxyGtk::populate(const Vector<WebContextMenuItemGtk>& items) { - bool previousIsSeparator = false; - bool isEmpty = true; - for (size_t i = 0; i < items.size(); i++) { - ContextMenuItem& menuItem = items.at(i); - if (menuItem.type() == SeparatorType) { - previousIsSeparator = true; - continue; - } - - if (previousIsSeparator && !isEmpty) - append(items.at(i - 1)); - previousIsSeparator = false; - - append(menuItem); - isEmpty = false; - } + GRefPtr<GMenu> menu = buildMenu(items); + gtk_menu_shell_bind_model(GTK_MENU_SHELL(m_menu), G_MENU_MODEL(menu.get()), nullptr, TRUE); } -void WebContextMenuProxyGtk::populate(const Vector<WebContextMenuItemData>& items) +void WebContextMenuProxyGtk::populate(const Vector<RefPtr<WebContextMenuItem>>& items) { - for (size_t i = 0; i < items.size(); i++) { - ContextMenuItem menuitem = items.at(i).core(); - append(menuitem); + GRefPtr<GMenu> menu = adoptGRef(g_menu_new()); + GMenu* sectionMenu = menu.get(); + for (const auto& item : items) { + if (item->data().type() == SeparatorType) { + GRefPtr<GMenu> section = adoptGRef(g_menu_new()); + g_menu_append_section(menu.get(), nullptr, G_MENU_MODEL(section.get())); + sectionMenu = section.get(); + } else { + WebContextMenuItemGtk menuitem(item->data()); + append(sectionMenu, menuitem); + } } + gtk_menu_shell_bind_model(GTK_MENU_SHELL(m_menu), G_MENU_MODEL(menu.get()), nullptr, TRUE); } -void WebContextMenuProxyGtk::showContextMenu(const WebCore::IntPoint& position, const Vector<WebContextMenuItemData>& items) +void WebContextMenuProxyGtk::show() { + Vector<RefPtr<WebContextMenuItem>> proposedAPIItems; + for (auto& item : m_context.menuItems()) { + if (item.action() != ContextMenuItemTagShareMenu) + proposedAPIItems.append(WebContextMenuItem::create(item)); + } + + Vector<RefPtr<WebContextMenuItem>> clientItems; + bool useProposedItems = true; + + if (m_page->contextMenuClient().getContextMenuFromProposedMenu(*m_page, proposedAPIItems, clientItems, m_context.webHitTestResultData(), m_page->process().transformHandlesToObjects(m_userData.object()).get())) + useProposedItems = false; + + const Vector<RefPtr<WebContextMenuItem>>& items = useProposedItems ? proposedAPIItems : clientItems; + if (!items.isEmpty()) populate(items); - if (!m_menu.itemCount()) + unsigned childCount = 0; + gtk_container_foreach(GTK_CONTAINER(m_menu), [](GtkWidget*, gpointer data) { (*static_cast<unsigned*>(data))++; }, &childCount); + if (!childCount) return; - m_popupPosition = convertWidgetPointToScreenPoint(m_webView, position); + m_popupPosition = convertWidgetPointToScreenPoint(m_webView, m_context.menuLocation()); // Display menu initiated by right click (mouse button pressed = 3). NativeWebMouseEvent* mouseEvent = m_page->currentlyProcessedMouseDownEvent(); const GdkEvent* event = mouseEvent ? mouseEvent->nativeEvent() : 0; - gtk_menu_attach_to_widget(m_menu.platformDescription(), GTK_WIDGET(m_webView), 0); - gtk_menu_popup(m_menu.platformDescription(), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, + gtk_menu_attach_to_widget(m_menu, GTK_WIDGET(m_webView), nullptr); + gtk_menu_popup(m_menu, nullptr, nullptr, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, event ? event->button.button : 3, event ? event->button.time : GDK_CURRENT_TIME); } -void WebContextMenuProxyGtk::hideContextMenu() +void WebContextMenuProxyGtk::showContextMenuWithItems(const Vector<WebContextMenuItemData>& items) { - gtk_menu_popdown(m_menu.platformDescription()); } -WebContextMenuProxyGtk::WebContextMenuProxyGtk(GtkWidget* webView, WebPageProxy* page) - : m_webView(webView) - , m_page(page) +WebContextMenuProxyGtk::WebContextMenuProxyGtk(GtkWidget* webView, WebPageProxy& page, const ContextMenuContextData& context, const UserData& userData) + : WebContextMenuProxy(context, userData) + , m_webView(webView) + , m_page(&page) + , m_menu(GTK_MENU(gtk_menu_new())) { + GRefPtr<GSimpleActionGroup> group = adoptGRef(g_simple_action_group_new()); + gtk_widget_insert_action_group(GTK_WIDGET(m_menu), gContextMenuItemGroup, G_ACTION_GROUP(group.get())); +#if !GTK_CHECK_VERSION(3, 16, 0) + g_object_set_data(G_OBJECT(m_menu), gContextMenuItemGroup, group.get()); +#endif webkitWebViewBaseSetActiveContextMenuProxy(WEBKIT_WEB_VIEW_BASE(m_webView), this); } WebContextMenuProxyGtk::~WebContextMenuProxyGtk() { - for (auto iter = m_signalHandlers.begin(); iter != m_signalHandlers.end(); ++iter) - g_signal_handler_disconnect(iter->value, iter->key); + gtk_menu_popdown(m_menu); + + for (auto& handler : m_signalHandlers) + g_signal_handler_disconnect(handler.value, handler.key); + m_signalHandlers.clear(); - webkitWebViewBaseSetActiveContextMenuProxy(WEBKIT_WEB_VIEW_BASE(m_webView), 0); + gtk_widget_insert_action_group(GTK_WIDGET(m_menu), gContextMenuItemGroup, nullptr); +#if !GTK_CHECK_VERSION(3, 16, 0) + g_object_set_data(G_OBJECT(m_menu), gContextMenuItemGroup, nullptr); +#endif + gtk_widget_destroy(GTK_WIDGET(m_menu)); } void WebContextMenuProxyGtk::menuPositionFunction(GtkMenu* menu, gint* x, gint* y, gboolean* pushIn, WebContextMenuProxyGtk* popupMenu) |