summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp')
-rw-r--r--Source/WebKit2/UIProcess/gtk/WebContextMenuProxyGtk.cpp204
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)