Skip to content
Permalink
Browse files
[GTK] Add a signal to allow applications to handle its own context menu
https://bugs.webkit.org/show_bug.cgi?id=49904

Reviewed by Gustavo Noronha Silva.

* tests/testhittestresult.c:
(load_status_cb): Test x, y properties of WebKitHitTestResult.
* webkit/webkithittestresult.cpp:
(webkit_hit_test_result_get_property):
(webkit_hit_test_result_set_property):
(webkit_hit_test_result_class_init): Add x and y construct only
properties to keep the coordinates of the event relative to view's
widget.
(WebKit::kit):
* webkit/webkitwebsettings.cpp:
(webkit_web_settings_class_init): Mark enable-default-context-menu
property as deprecated.
* webkit/webkitwebview.cpp:
(prepareMouseEventForFrame):
(defaultContextMenuEnabled): Helper function to check whether
default context menu setting is enabled or not.
(webkit_web_view_forward_context_menu_event): Emit the new
context-menu signal.
(webkit_web_view_popup_menu_handler):
(webkit_web_view_button_press_event):
(webkit_web_view_class_init): Add new context-menu signal and mark
populate-popup as deprecated.
* webkitmarshal.list:

Canonical link: https://commits.webkit.org/99329@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111843 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
carlosgcampos committed Mar 23, 2012
1 parent 3a910d7 commit 683abb6593a053fb2b8124ca4e71dd6d4b5831b7
@@ -1,3 +1,34 @@
2012-03-23 Carlos Garcia Campos <cgarcia@igalia.com>

[GTK] Add a signal to allow applications to handle its own context menu
https://bugs.webkit.org/show_bug.cgi?id=49904

Reviewed by Gustavo Noronha Silva.

* tests/testhittestresult.c:
(load_status_cb): Test x, y properties of WebKitHitTestResult.
* webkit/webkithittestresult.cpp:
(webkit_hit_test_result_get_property):
(webkit_hit_test_result_set_property):
(webkit_hit_test_result_class_init): Add x and y construct only
properties to keep the coordinates of the event relative to view's
widget.
(WebKit::kit):
* webkit/webkitwebsettings.cpp:
(webkit_web_settings_class_init): Mark enable-default-context-menu
property as deprecated.
* webkit/webkitwebview.cpp:
(prepareMouseEventForFrame):
(defaultContextMenuEnabled): Helper function to check whether
default context menu setting is enabled or not.
(webkit_web_view_forward_context_menu_event): Emit the new
context-menu signal.
(webkit_web_view_popup_menu_handler):
(webkit_web_view_button_press_event):
(webkit_web_view_class_init): Add new context-menu signal and mark
populate-popup as deprecated.
* webkitmarshal.list:

2012-03-23 Carlos Garcia Campos <cgarcia@igalia.com>

Unreviewed. Fix GTK+ build after r111707.
@@ -83,6 +83,7 @@ load_status_cb(WebKitWebView* webView,
guint context;
GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
WebKitDOMNode* node;
gint x, y;

/* Close enough to 0,0 */
event->button.x = 5;
@@ -98,6 +99,11 @@ load_status_cb(WebKitWebView* webView,
g_object_get(result, "inner-node", &node, NULL);
g_assert(node);
g_assert(WEBKIT_DOM_IS_NODE(node));

g_object_get(result, "x", &x, "y", &y, NULL);
g_assert_cmpint(x, ==, 5);
g_assert_cmpint(y, ==, 5);

/* We can only test these node types at the moment. In the
* input case there seems to be an extra layer with a DIV on
* top of the input, which gets assigned to the inner-node.
@@ -21,6 +21,8 @@
#include "config.h"
#include "webkithittestresult.h"

#include "Frame.h"
#include "FrameView.h"
#include "HitTestResult.h"
#include "KURL.h"
#include "WebKitDOMBinding.h"
@@ -49,6 +51,7 @@ struct _WebKitHitTestResultPrivate {
char* imageURI;
char* mediaURI;
GRefPtr<WebKitDOMNode> innerNode;
WebCore::IntPoint position;
};

enum {
@@ -58,7 +61,9 @@ enum {
PROP_LINK_URI,
PROP_IMAGE_URI,
PROP_MEDIA_URI,
PROP_INNER_NODE
PROP_INNER_NODE,
PROP_X,
PROP_Y
};

static void webkit_hit_test_result_finalize(GObject* object)
@@ -101,6 +106,12 @@ static void webkit_hit_test_result_get_property(GObject* object, guint propertyI
case PROP_INNER_NODE:
g_value_set_object(value, priv->innerNode.get());
break;
case PROP_X:
g_value_set_int(value, priv->position.x());
break;
case PROP_Y:
g_value_set_int(value, priv->position.y());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyID, pspec);
}
@@ -130,6 +141,12 @@ static void webkit_hit_test_result_set_property(GObject* object, guint propertyI
case PROP_INNER_NODE:
priv->innerNode = static_cast<WebKitDOMNode*>(g_value_get_object(value));
break;
case PROP_X:
priv->position.setX(g_value_get_int(value));
break;
case PROP_Y:
priv->position.setY(g_value_get_int(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyID, pspec);
}
@@ -225,6 +242,34 @@ static void webkit_hit_test_result_class_init(WebKitHitTestResultClass* webHitTe
WEBKIT_TYPE_DOM_NODE,
static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));

/**
* WebKitHitTestResult:x:
*
* The x coordintate of the event relative to the view's window.
*
* Since: 1.10
*/
g_object_class_install_property(objectClass, PROP_X,
g_param_spec_int("x",
_("X coordinate"),
_("The x coordintate of the event relative to the view's window."),
G_MININT, G_MAXINT, 0,
static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));

/**
* WebKitHitTestResult:y:
*
* The x coordintate of the event relative to the view's window.
*
* Since: 1.10
*/
g_object_class_install_property(objectClass, PROP_Y,
g_param_spec_int("y",
_("Y coordinate"),
_("The y coordintate of the event relative to the view's window."),
G_MININT, G_MAXINT, 0,
static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));

g_type_class_add_private(webHitTestResultClass, sizeof(WebKitHitTestResultPrivate));
}

@@ -243,6 +288,8 @@ WebKitHitTestResult* kit(const WebCore::HitTestResult& result)
GOwnPtr<char> imageURI(0);
GOwnPtr<char> mediaURI(0);
WebKitDOMNode* node = 0;
WebCore::Frame* targetFrame;
WebCore::IntPoint point;

if (!result.absoluteLinkURL().isEmpty()) {
context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
@@ -268,12 +315,21 @@ WebKitHitTestResult* kit(const WebCore::HitTestResult& result)
if (result.innerNonSharedNode())
node = kit(result.innerNonSharedNode());

targetFrame = result.targetFrame();
if (targetFrame && targetFrame->view()) {
// Convert document coords to widget coords.
point = targetFrame->view()->contentsToWindow(result.point());
} else
point = result.point();

return WEBKIT_HIT_TEST_RESULT(g_object_new(WEBKIT_TYPE_HIT_TEST_RESULT,
"link-uri", linkURI.get(),
"image-uri", imageURI.get(),
"media-uri", mediaURI.get(),
"context", context,
"inner-node", node,
"x", point.x(),
"y", point.y(),
NULL));
}

@@ -754,6 +754,8 @@ static void webkit_web_settings_class_init(WebKitWebSettingsClass* klass)
* right-clicks that are handled by the page itself.
*
* Since: 1.1.18
*
* Deprecated: 1.10: Use #WebKitWebView::context-menu signal instead.
*/
g_object_class_install_property(gobject_class,
PROP_ENABLE_DEFAULT_CONTEXT_MENU,
@@ -219,6 +219,7 @@ enum {
RESOURCE_LOAD_FAILED,
ENTERING_FULLSCREEN,
LEAVING_FULLSCREEN,
CONTEXT_MENU,

LAST_SIGNAL
};
@@ -323,23 +324,36 @@ static void contextMenuConnectActivate(GtkMenuItem* item, ContextMenuController*
g_signal_connect(item, "activate", G_CALLBACK(contextMenuItemActivated), controller);
}

static gboolean webkit_web_view_forward_context_menu_event(WebKitWebView* webView, const PlatformMouseEvent& event)
static MouseEventWithHitTestResults prepareMouseEventForFrame(Frame* frame, const PlatformMouseEvent& event)
{
HitTestRequest request(HitTestRequest::Active);
IntPoint point = frame->view()->windowToContents(event.position());
return frame->document()->prepareMouseEvent(request, point, event);
}

// Check enable-default-context-menu setting for compatibility.
static bool defaultContextMenuEnabled(WebKitWebView* webView)
{
gboolean enableDefaultContextMenu;
g_object_get(webkit_web_view_get_settings(webView), "enable-default-context-menu", &enableDefaultContextMenu, NULL);
return enableDefaultContextMenu;
}

static gboolean webkit_web_view_forward_context_menu_event(WebKitWebView* webView, const PlatformMouseEvent& event, bool triggeredWithKeyboard)
{
Page* page = core(webView);
page->contextMenuController()->clearContextMenu();
Frame* focusedFrame;
Frame* mainFrame = page->mainFrame();
gboolean mousePressEventResult = FALSE;
GRefPtr<WebKitHitTestResult> hitTestResult;

if (!mainFrame->view())
return FALSE;

mainFrame->view()->setCursor(pointerCursor());
if (page->frameCount()) {
HitTestRequest request(HitTestRequest::Active);
IntPoint point = mainFrame->view()->windowToContents(event.position());
MouseEventWithHitTestResults mev = mainFrame->document()->prepareMouseEvent(request, point, event);

MouseEventWithHitTestResults mev = prepareMouseEventForFrame(mainFrame, event);
Frame* targetFrame = EventHandler::subframeForHitTestResult(mev);
if (!targetFrame)
targetFrame = mainFrame;
@@ -349,13 +363,14 @@ static gboolean webkit_web_view_forward_context_menu_event(WebKitWebView* webVie
page->focusController()->setFocusedFrame(targetFrame);
focusedFrame = targetFrame;
}
if (focusedFrame == mainFrame)
hitTestResult = adoptGRef(kit(mev.hitTestResult()));
} else
focusedFrame = mainFrame;

if (focusedFrame->view() && focusedFrame->eventHandler()->handleMousePressEvent(event))
mousePressEventResult = TRUE;


bool handledEvent = focusedFrame->eventHandler()->sendContextMenuEvent(event);
if (!handledEvent)
return FALSE;
@@ -368,37 +383,42 @@ static gboolean webkit_web_view_forward_context_menu_event(WebKitWebView* webVie
if (!coreMenu)
return mousePressEventResult;

// If we reach here, it's because WebCore is going to show the
// default context menu. We check our setting to figure out
// whether we want it or not.
WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
gboolean enableDefaultContextMenu;
g_object_get(settings, "enable-default-context-menu", &enableDefaultContextMenu, NULL);

if (!enableDefaultContextMenu)
return FALSE;

GtkMenu* menu = GTK_MENU(coreMenu->platformDescription());
if (!menu)
return FALSE;
GtkMenu* defaultMenu = coreMenu->platformDescription();
ASSERT(defaultMenu);

// We connect the "activate" signal here rather than in ContextMenuGtk to avoid
// a layering violation. ContextMenuGtk should not know about the ContextMenuController.
gtk_container_foreach(GTK_CONTAINER(menu), (GtkCallback)contextMenuConnectActivate, controller);
gtk_container_foreach(GTK_CONTAINER(defaultMenu), reinterpret_cast<GtkCallback>(contextMenuConnectActivate), controller);

g_signal_emit(webView, webkit_web_view_signals[POPULATE_POPUP], 0, menu);
if (!hitTestResult) {
MouseEventWithHitTestResults mev = prepareMouseEventForFrame(focusedFrame, event);
hitTestResult = adoptGRef(kit(mev.hitTestResult()));
}

gboolean handled;
g_signal_emit(webView, webkit_web_view_signals[CONTEXT_MENU], 0, defaultMenu, hitTestResult.get(), triggeredWithKeyboard, &handled);
if (handled)
return TRUE;

// Return now if default context menu is disabled by enable-default-context-menu setting.
// Check enable-default-context-menu setting for compatibility.
if (!defaultContextMenuEnabled(webView))
return FALSE;

// Emit populate-popup signal for compatibility.
g_signal_emit(webView, webkit_web_view_signals[POPULATE_POPUP], 0, defaultMenu);

// If the context menu is now empty, don't show it.
GOwnPtr<GList> items(gtk_container_get_children(GTK_CONTAINER(menu)));
GOwnPtr<GList> items(gtk_container_get_children(GTK_CONTAINER(defaultMenu)));
if (!items)
return FALSE;

WebKitWebViewPrivate* priv = webView->priv;
priv->currentMenu = menu;
priv->currentMenu = defaultMenu;
priv->lastPopupXPosition = event.globalPosition().x();
priv->lastPopupYPosition = event.globalPosition().y();

gtk_menu_popup(menu, 0, 0, &PopupMenuPositionFunc, webView, event.button() + 1, gtk_get_current_event_time());
gtk_menu_popup(defaultMenu, 0, 0, &PopupMenuPositionFunc, webView, event.button() + 1, gtk_get_current_event_time());
return TRUE;
}

@@ -439,7 +459,7 @@ static gboolean webkit_web_view_popup_menu_handler(GtkWidget* widget)

IntPoint globalPoint(convertWidgetPointToScreenPoint(widget, location));
PlatformMouseEvent event(location, globalPoint, RightButton, PlatformEvent::MousePressed, 0, false, false, false, false, gtk_get_current_event_time());
return webkit_web_view_forward_context_menu_event(WEBKIT_WEB_VIEW(widget), event);
return webkit_web_view_forward_context_menu_event(WEBKIT_WEB_VIEW(widget), event, true);
}

static void setHorizontalAdjustment(WebKitWebView* webView, GtkAdjustment* adjustment)
@@ -740,7 +760,7 @@ static gboolean webkit_web_view_button_press_event(GtkWidget* widget, GdkEventBu
platformEvent.setClickCount(count);

if (event->button == 3)
return webkit_web_view_forward_context_menu_event(webView, PlatformMouseEvent(event));
return webkit_web_view_forward_context_menu_event(webView, PlatformMouseEvent(event), false);

Frame* frame = core(webView)->mainFrame();
if (!frame->view())
@@ -2105,6 +2125,8 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass)
* When a context menu is about to be displayed this signal is emitted.
*
* Add menu items to #menu to extend the context menu.
*
* Deprecated: 1.10: Use #WebKitWebView::context-menu signal instead.
*/
webkit_web_view_signals[POPULATE_POPUP] = g_signal_new("populate-popup",
G_TYPE_FROM_CLASS(webViewClass),
@@ -2825,6 +2847,41 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass)
WEBKIT_TYPE_WEB_RESOURCE,
G_TYPE_POINTER);

/**
* WebKitWebView::context-menu:
* @web_view: the object which received the signal
* @default_menu: the default context menu
* @hit_test_result: a #WebKitHitTestResult with the context of the current position.
* @keyboard_mode: %TRUE if the context menu was triggered using the keyboard
*
* Emmited when a context menu is about to be displayed to give the application
* a chance to create and handle its own context menu. If you only want to add custom
* options to the default context menu you can simply modify the given @default_menu.
*
* When @keyboard_mode is %TRUE the coordinates of the given @hit_test_result should be
* used to position the popup menu. When the context menu has been triggered by a
* mouse event you could either use the @hit_test_result coordinates or pass %NULL
* to the #GtkMenuPositionFunc parameter of gtk_menu_popup() function.
* Note that coordinates of @hit_test_result are relative to @web_view window.
*
* If your application will create and display its own popup menu, %TRUE should be returned.
* Note that when the context menu is handled by the application, the #WebKitWebSettings:enable-default-context-menu
* setting will be ignored and the #WebKitWebView::populate-popup signal won't be emitted.
* If you don't want any context menu to be shown, you can simply connect to this signal
* and return %TRUE without doing anything else.
*
* Since: 1.10
*/
webkit_web_view_signals[CONTEXT_MENU] = g_signal_new("context-menu",
G_TYPE_FROM_CLASS(webViewClass),
G_SIGNAL_RUN_LAST,
0, 0, 0,
webkit_marshal_BOOLEAN__OBJECT_OBJECT_BOOLEAN,
G_TYPE_BOOLEAN, 3,
GTK_TYPE_WIDGET,
WEBKIT_TYPE_HIT_TEST_RESULT,
G_TYPE_BOOLEAN);

/*
* implementations of virtual methods
*/
@@ -2,6 +2,7 @@ BOOLEAN:ENUM,INT
BOOLEAN:INT,INT,STRING
BOOLEAN:OBJECT
BOOLEAN:OBJECT,OBJECT
BOOLEAN:OBJECT,OBJECT,BOOLEAN
BOOLEAN:OBJECT,OBJECT,ENUM
BOOLEAN:OBJECT,OBJECT,ENUM,BOOLEAN
BOOLEAN:OBJECT,OBJECT,OBJECT,OBJECT

0 comments on commit 683abb6

Please sign in to comment.