summaryrefslogtreecommitdiffstats
path: root/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/DumpRenderTree/gtk/EventSender.cpp')
-rw-r--r--WebKitTools/DumpRenderTree/gtk/EventSender.cpp384
1 files changed, 176 insertions, 208 deletions
diff --git a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
index 9c27d8c..0ef4a42 100644
--- a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
+++ b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp
@@ -45,28 +45,27 @@
#include <gdk/gdkkeysyms.h>
#include <string.h>
-// TODO: Currently drag and drop related code is left out and
-// should be merged once we have drag and drop support in WebCore.
+// FIXME: Implement support for synthesizing drop events.
extern "C" {
extern void webkit_web_frame_layout(WebKitWebFrame* frame);
}
-static bool down = false;
-static bool currentEventButton = 1;
-static bool dragMode = true;
-static bool replayingSavedEvents = false;
+static bool dragMode;
+static int timeOffset = 0;
+
static int lastMousePositionX;
static int lastMousePositionY;
-
static int lastClickPositionX;
static int lastClickPositionY;
-static int clickCount = 0;
+static int lastClickTimeOffset;
+static int lastClickButton;
+static int buttonCurrentlyDown;
+static int clickCount;
struct DelayedMessage {
GdkEvent event;
gulong delay;
- gboolean isDragEvent;
};
static DelayedMessage msgQueue[1024];
@@ -84,6 +83,10 @@ enum KeyLocationCode {
DOM_KEY_LOCATION_NUMPAD = 0x03
};
+static void sendOrQueueEvent(GdkEvent, bool = true);
+static void dispatchEvent(GdkEvent event);
+static guint getStateFlags();
+
static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
return JSValueMakeBoolean(context, dragMode);
@@ -97,46 +100,15 @@ static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStri
static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
- // FIXME: Add proper support for forward leaps
- return JSValueMakeUndefined(context);
-}
-
-static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
-{
- webkit_web_frame_layout(mainFrame);
-
- WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
- if (!view)
- return JSValueMakeUndefined(context);
-
- GdkEvent event;
- memset(&event, 0, sizeof(event));
- event.button.button = 3;
- event.button.x = lastMousePositionX;
- event.button.y = lastMousePositionY;
- event.button.window = GTK_WIDGET(view)->window;
-
- gboolean return_val;
- down = true;
- event.type = GDK_BUTTON_PRESS;
- g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
-
- down = false;
- event.type = GDK_BUTTON_RELEASE;
- g_signal_emit_by_name(view, "button_release_event", &event, &return_val);
+ if (argumentCount > 0) {
+ msgQueue[endOfQueue].delay = JSValueToNumber(context, arguments[0], exception);
+ timeOffset += msgQueue[endOfQueue].delay;
+ ASSERT(!exception || !*exception);
+ }
return JSValueMakeUndefined(context);
}
-static void updateClickCount(int button)
-{
- // FIXME: take the last clicked button number and the time of last click into account.
- if (lastClickPositionX != lastMousePositionX || lastClickPositionY != lastMousePositionY || currentEventButton != button)
- clickCount = 1;
- else
- clickCount++;
-}
-
#if !GTK_CHECK_VERSION(2,17,3)
static void getRootCoords(GtkWidget* view, int* rootX, int* rootY)
{
@@ -152,131 +124,139 @@ static void getRootCoords(GtkWidget* view, int* rootX, int* rootY)
}
#endif
-static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+bool prepareMouseButtonEvent(GdkEvent* event, int eventSenderButtonNumber)
{
WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
if (!view)
- return JSValueMakeUndefined(context);
+ return false;
+
+ // The logic for mapping EventSender button numbers to GDK button
+ // numbers originates from the Windows EventSender.
+ int gdkButtonNumber = 3;
+ if (eventSenderButtonNumber >= 0 && eventSenderButtonNumber <= 2)
+ gdkButtonNumber = eventSenderButtonNumber + 1;
+
+ // fast/events/mouse-click-events expects the 4th button
+ // to be event.button = 1, so send a middle-button event.
+ else if (eventSenderButtonNumber == 3)
+ gdkButtonNumber = 2;
+
+ memset(event, 0, sizeof(event));
+ event->button.button = gdkButtonNumber;
+ event->button.x = lastMousePositionX;
+ event->button.y = lastMousePositionY;
+ event->button.window = GTK_WIDGET(view)->window;
+ event->button.device = gdk_device_get_core_pointer();
+ event->button.state = getStateFlags();
+
+ // Mouse up & down events dispatched via g_signal_emit_by_name must offset
+ // their time value, so that WebKit can detect where sequences of mouse
+ // clicks begin and end. This should not interfere with GDK or GTK+ event
+ // processing, because the event is only seen by the widget.
+ event->button.time = GDK_CURRENT_TIME + timeOffset;
+
+ int xRoot, yRoot;
+#if GTK_CHECK_VERSION(2, 17, 3)
+ gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
+#else
+ getRootCoords(GTK_WIDGET(view), &xRoot, &yRoot);
+#endif
+ event->button.x_root = xRoot;
+ event->button.y_root = yRoot;
- down = true;
+ return true;
+}
+static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
GdkEvent event;
- memset(&event, 0, sizeof(event));
+ if (!prepareMouseButtonEvent(&event, 2))
+ return JSValueMakeUndefined(context);
+
event.type = GDK_BUTTON_PRESS;
- event.button.button = 1;
+ sendOrQueueEvent(event);
+ event.type = GDK_BUTTON_RELEASE;
+ sendOrQueueEvent(event);
+
+ return JSValueMakeUndefined(context);
+}
+
+static void updateClickCount(int button)
+{
+ if (lastClickPositionX != lastMousePositionX
+ || lastClickPositionY != lastMousePositionY
+ || lastClickButton != button
+ || timeOffset - lastClickTimeOffset >= 1)
+ clickCount = 1;
+ else
+ clickCount++;
+}
+static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ int button = 0;
if (argumentCount == 1) {
- event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1;
+ button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
}
- currentEventButton = event.button.button;
-
- event.button.x = lastMousePositionX;
- event.button.y = lastMousePositionY;
- event.button.window = GTK_WIDGET(view)->window;
- event.button.time = GDK_CURRENT_TIME;
- event.button.device = gdk_device_get_core_pointer();
-
- int x_root, y_root;
-#if GTK_CHECK_VERSION(2,17,3)
- gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root);
-#else
- getRootCoords(GTK_WIDGET(view), &x_root, &y_root);
-#endif
+ GdkEvent event;
+ if (!prepareMouseButtonEvent(&event, button))
+ return JSValueMakeUndefined(context);
- event.button.x_root = x_root;
- event.button.y_root = y_root;
+ buttonCurrentlyDown = event.button.button;
+ // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for
+ // the second button press during double-clicks. WebKit GTK+ selectively
+ // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek.
+ // Since our events aren't ever going onto the GDK event queue, WebKit won't
+ // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send
+ // it here. Eventually this code should probably figure out a way to get all
+ // appropriate events onto the event queue and this work-around should be
+ // removed.
updateClickCount(event.button.button);
+ if (clickCount == 2)
+ event.type = GDK_2BUTTON_PRESS;
+ else if (clickCount == 3)
+ event.type = GDK_3BUTTON_PRESS;
+ else
+ event.type = GDK_BUTTON_PRESS;
- if (!msgQueue[endOfQueue].delay) {
- webkit_web_frame_layout(mainFrame);
-
- gboolean return_val;
- g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
- if (clickCount == 2) {
- event.type = GDK_2BUTTON_PRESS;
- g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
- }
- } else {
- // replaySavedEvents should have the required logic to make leapForward delays work
- msgQueue[endOfQueue++].event = event;
- replaySavedEvents();
- }
-
+ sendOrQueueEvent(event);
return JSValueMakeUndefined(context);
}
static guint getStateFlags()
{
- guint state = 0;
-
- if (down) {
- if (currentEventButton == 1)
- state = GDK_BUTTON1_MASK;
- else if (currentEventButton == 2)
- state = GDK_BUTTON2_MASK;
- else if (currentEventButton == 3)
- state = GDK_BUTTON3_MASK;
- } else
- state = 0;
-
- return state;
+ if (buttonCurrentlyDown == 1)
+ return GDK_BUTTON1_MASK;
+ if (buttonCurrentlyDown == 2)
+ return GDK_BUTTON2_MASK;
+ if (buttonCurrentlyDown == 3)
+ return GDK_BUTTON3_MASK;
+ return 0;
}
static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
-
- WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
- if (!view)
- return JSValueMakeUndefined(context);
-
- GdkEvent event;
- memset(&event, 0, sizeof(event));
- event.type = GDK_BUTTON_RELEASE;
- event.button.button = 1;
-
+ int button = 0;
if (argumentCount == 1) {
- event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1;
+ button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
}
- currentEventButton = event.button.button;
-
- event.button.x = lastMousePositionX;
- event.button.y = lastMousePositionY;
- event.button.window = GTK_WIDGET(view)->window;
- event.button.time = GDK_CURRENT_TIME;
- event.button.device = gdk_device_get_core_pointer();
- event.button.state = getStateFlags();
-
- down = false;
-
- int x_root, y_root;
-#if GTK_CHECK_VERSION(2,17,3)
- gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root);
-#else
- getRootCoords(GTK_WIDGET(view), &x_root, &y_root);
-#endif
-
- event.button.x_root = x_root;
- event.button.y_root = y_root;
-
- if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) {
- msgQueue[endOfQueue].event = event;
- msgQueue[endOfQueue++].isDragEvent = true;
- replaySavedEvents();
- } else {
- webkit_web_frame_layout(mainFrame);
-
- gboolean return_val;
- g_signal_emit_by_name(view, "button_release_event", &event, &return_val);
- }
+ GdkEvent event;
+ if (!prepareMouseButtonEvent(&event, button))
+ return JSValueMakeUndefined(context);
lastClickPositionX = lastMousePositionX;
lastClickPositionY = lastMousePositionY;
+ lastClickButton = buttonCurrentlyDown;
+ lastClickTimeOffset = timeOffset;
+ buttonCurrentlyDown = 0;
+ event.type = GDK_BUTTON_RELEASE;
+ sendOrQueueEvent(event);
return JSValueMakeUndefined(context);
}
@@ -299,32 +279,22 @@ static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function
event.type = GDK_MOTION_NOTIFY;
event.motion.x = lastMousePositionX;
event.motion.y = lastMousePositionY;
+
event.motion.time = GDK_CURRENT_TIME;
event.motion.window = GTK_WIDGET(view)->window;
event.motion.device = gdk_device_get_core_pointer();
+ event.motion.state = getStateFlags();
- int x_root, y_root;
+ int xRoot, yRoot;
#if GTK_CHECK_VERSION(2,17,3)
- gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root);
+ gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
#else
- getRootCoords(GTK_WIDGET(view), &x_root, &y_root);
+ getRootCoords(GTK_WIDGET(view), &xRoot, &yRoot);
#endif
+ event.motion.x_root = xRoot;
+ event.motion.y_root = yRoot;
- event.motion.x_root = x_root;
- event.motion.y_root = y_root;
-
- event.motion.state = getStateFlags();
-
- if (dragMode && down && !replayingSavedEvents) {
- msgQueue[endOfQueue].event = event;
- msgQueue[endOfQueue++].isDragEvent = true;
- } else {
- webkit_web_frame_layout(mainFrame);
-
- gboolean return_val;
- g_signal_emit_by_name(view, "motion_notify_event", &event, &return_val);
- }
-
+ sendOrQueueEvent(event, false);
return JSValueMakeUndefined(context);
}
@@ -363,14 +333,7 @@ static JSValueRef mouseWheelToCallback(JSContextRef context, JSObjectRef functio
else
g_assert_not_reached();
- if (dragMode && down && !replayingSavedEvents) {
- msgQueue[endOfQueue].event = event;
- msgQueue[endOfQueue++].isDragEvent = true;
- } else {
- webkit_web_frame_layout(mainFrame);
- gtk_main_do_event(&event);
- }
-
+ sendOrQueueEvent(event);
return JSValueMakeUndefined(context);
}
@@ -383,49 +346,49 @@ static JSValueRef beginDragWithFilesCallback(JSContextRef context, JSObjectRef f
return JSValueMakeUndefined(context);
}
-void replaySavedEvents()
+static void sendOrQueueEvent(GdkEvent event, bool shouldReplaySavedEvents)
{
- // FIXME: This doesn't deal with forward leaps, but it should.
+ // Mouse move events are queued if the previous event was queued or if a
+ // delay was set up by leapForward().
+ if (buttonCurrentlyDown || endOfQueue != startOfQueue || msgQueue[endOfQueue].delay) {
+ msgQueue[endOfQueue++].event = event;
+
+ if (shouldReplaySavedEvents)
+ replaySavedEvents();
+
+ return;
+ }
+
+ dispatchEvent(event);
+}
+static void dispatchEvent(GdkEvent event)
+{
+ webkit_web_frame_layout(mainFrame);
WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
if (!view)
return;
- replayingSavedEvents = true;
-
- for (unsigned queuePos = 0; queuePos < endOfQueue; queuePos++) {
- GdkEvent event = msgQueue[queuePos].event;
- gboolean return_val;
-
- switch (event.type) {
- case GDK_BUTTON_RELEASE:
- g_signal_emit_by_name(view, "button_release_event", &event, &return_val);
- break;
- case GDK_BUTTON_PRESS:
- g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
- break;
- case GDK_MOTION_NOTIFY:
- g_signal_emit_by_name(view, "motion_notify_event", &event, &return_val);
- break;
- default:
- continue;
- }
+ gtk_main_do_event(&event);
+}
- startOfQueue++;
- }
+void replaySavedEvents()
+{
+ // FIXME: Eventually we may need to have more sophisticated logic to
+ // track drag-and-drop operations.
+
+ // First send all the events that are ready to be sent
+ while (startOfQueue < endOfQueue) {
+ if (msgQueue[startOfQueue].delay) {
+ g_usleep(msgQueue[startOfQueue].delay * 1000);
+ msgQueue[startOfQueue].delay = 0;
+ }
- int numQueuedMessages = endOfQueue - startOfQueue;
- if (!numQueuedMessages) {
- startOfQueue = 0;
- endOfQueue = 0;
- replayingSavedEvents = false;
- return;
+ dispatchEvent(msgQueue[startOfQueue++].event);
}
startOfQueue = 0;
endOfQueue = 0;
-
- replayingSavedEvents = false;
}
static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
@@ -566,13 +529,11 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS
g_free(keys);
}
- gboolean return_val;
event.key.type = GDK_KEY_PRESS;
-
- g_signal_emit_by_name(view, "key-press-event", &event.key, &return_val);
+ dispatchEvent(event);
event.key.type = GDK_KEY_RELEASE;
- g_signal_emit_by_name(view, "key-release-event", &event.key, &return_val);
+ dispatchEvent(event);
return JSValueMakeUndefined(context);
}
@@ -661,17 +622,24 @@ static JSClassRef getClass(JSContextRef context)
return eventSenderClass;
}
-JSObjectRef makeEventSender(JSContextRef context)
+JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
{
- down = false;
- dragMode = true;
- lastMousePositionX = lastMousePositionY = 0;
- lastClickPositionX = lastClickPositionY = 0;
-
- if (!replayingSavedEvents) {
- // This function can be called in the middle of a test, even
- // while replaying saved events. Resetting these while doing that
- // can break things.
+ if (isTopFrame) {
+ dragMode = true;
+
+ // Fly forward in time one second when the main frame loads. This will
+ // ensure that when a test begins clicking in the same location as
+ // a previous test, those clicks won't be interpreted as continuations
+ // of the previous test's click sequences.
+ timeOffset += 1000;
+
+ lastMousePositionX = lastMousePositionY = 0;
+ lastClickPositionX = lastClickPositionY = 0;
+ lastClickTimeOffset = 0;
+ lastClickButton = 0;
+ buttonCurrentlyDown = 0;
+ clickCount = 0;
+
endOfQueue = 0;
startOfQueue = 0;
}