diff options
Diffstat (limited to 'WebKitTools/DumpRenderTree/gtk/EventSender.cpp')
-rw-r--r-- | WebKitTools/DumpRenderTree/gtk/EventSender.cpp | 384 |
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; } |