2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Zan Dobersek <zandobersek@gmail.com>
4 * Copyright (C) 2009 Holger Hans Peter Freyther
5 * Copyright (C) 2010 Igalia S.L.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "EventSender.h"
35 #include "DumpRenderTree.h"
37 #include <GtkVersioning.h>
38 #include <JavaScriptCore/JSObjectRef.h>
39 #include <JavaScriptCore/JSRetainPtr.h>
40 #include <JavaScriptCore/JSStringRef.h>
41 #include <webkit/webkitwebframe.h>
42 #include <webkit/webkitwebview.h>
43 #include <wtf/ASCIICType.h>
44 #include <wtf/Platform.h>
47 #include <gdk/gdkkeysyms.h>
51 extern void webkit_web_frame_layout(WebKitWebFrame* frame);
55 static int timeOffset = 0;
57 static int lastMousePositionX;
58 static int lastMousePositionY;
59 static int lastClickPositionX;
60 static int lastClickPositionY;
61 static int lastClickTimeOffset;
62 static int lastClickButton;
63 static int buttonCurrentlyDown;
64 static int clickCount;
65 GdkDragContext* currentDragSourceContext;
67 struct DelayedMessage {
72 static DelayedMessage msgQueue[1024];
74 static unsigned endOfQueue;
75 static unsigned startOfQueue;
77 static const float zoomMultiplierRatio = 1.2f;
79 // Key event location code defined in DOM Level 3.
80 enum KeyLocationCode {
81 DOM_KEY_LOCATION_STANDARD = 0x00,
82 DOM_KEY_LOCATION_LEFT = 0x01,
83 DOM_KEY_LOCATION_RIGHT = 0x02,
84 DOM_KEY_LOCATION_NUMPAD = 0x03
87 static void sendOrQueueEvent(GdkEvent*, bool = true);
88 static void dispatchEvent(GdkEvent* event);
89 static guint getStateFlags();
91 #if !GTK_CHECK_VERSION(2, 17, 3)
92 static void gdk_window_get_root_coords(GdkWindow* window, gint x, gint y, gint* rootX, gint* rootY)
94 gdk_window_get_root_origin(window, rootX, rootY);
100 #if !GTK_CHECK_VERSION(2, 14, 0)
101 static GdkWindow* gtk_widget_get_window(GtkWidget* widget)
103 g_return_val_if_fail(GTK_IS_WIDGET(widget), 0);
104 return widget->window;
108 #if !GTK_CHECK_VERSION(2, 21, 2)
109 static GdkDragAction gdk_drag_context_get_selected_action(GdkDragContext* context)
111 g_return_val_if_fail(GDK_IS_DRAG_CONTEXT(context), static_cast<GdkDragAction>(0));
112 return context->action;
115 static GdkDragAction gdk_drag_context_get_actions(GdkDragContext* context)
117 g_return_val_if_fail(GDK_IS_DRAG_CONTEXT(context), GDK_ACTION_DEFAULT);
118 return context->actions;
122 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
124 return JSValueMakeBoolean(context, dragMode);
127 static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
129 dragMode = JSValueToBoolean(context, value);
133 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
135 if (argumentCount > 0) {
136 msgQueue[endOfQueue].delay = JSValueToNumber(context, arguments[0], exception);
137 timeOffset += msgQueue[endOfQueue].delay;
138 ASSERT(!exception || !*exception);
141 return JSValueMakeUndefined(context);
144 bool prepareMouseButtonEvent(GdkEvent* event, int eventSenderButtonNumber)
146 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
150 // The logic for mapping EventSender button numbers to GDK button
151 // numbers originates from the Windows EventSender.
152 int gdkButtonNumber = 3;
153 if (eventSenderButtonNumber >= 0 && eventSenderButtonNumber <= 2)
154 gdkButtonNumber = eventSenderButtonNumber + 1;
156 // fast/events/mouse-click-events expects the 4th button
157 // to be event->button = 1, so send a middle-button event.
158 else if (eventSenderButtonNumber == 3)
161 event->button.button = gdkButtonNumber;
162 event->button.x = lastMousePositionX;
163 event->button.y = lastMousePositionY;
164 event->button.window = gtk_widget_get_window(GTK_WIDGET(view));
165 g_object_ref(event->button.window);
166 event->button.device = getDefaultGDKPointerDevice(event->button.window);
167 event->button.state = getStateFlags();
168 event->button.time = GDK_CURRENT_TIME;
169 event->button.axes = 0;
172 gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(view)), lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
173 event->button.x_root = xRoot;
174 event->button.y_root = yRoot;
179 static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
181 GdkEvent* pressEvent = gdk_event_new(GDK_BUTTON_PRESS);
182 if (!prepareMouseButtonEvent(pressEvent, 2))
183 return JSValueMakeUndefined(context);
185 GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
186 sendOrQueueEvent(pressEvent);
187 releaseEvent->type = GDK_BUTTON_RELEASE;
188 sendOrQueueEvent(releaseEvent);
190 return JSValueMakeUndefined(context);
193 static void updateClickCount(int button)
195 if (lastClickPositionX != lastMousePositionX
196 || lastClickPositionY != lastMousePositionY
197 || lastClickButton != button
198 || timeOffset - lastClickTimeOffset >= 1)
204 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
207 if (argumentCount == 1) {
208 button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
209 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
212 GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
213 if (!prepareMouseButtonEvent(event, button))
214 return JSValueMakeUndefined(context);
216 buttonCurrentlyDown = event->button.button;
218 // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for
219 // the second button press during double-clicks. WebKit GTK+ selectively
220 // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek.
221 // Since our events aren't ever going onto the GDK event queue, WebKit won't
222 // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send
223 // it here. Eventually this code should probably figure out a way to get all
224 // appropriate events onto the event queue and this work-around should be
226 updateClickCount(event->button.button);
228 event->type = GDK_2BUTTON_PRESS;
229 else if (clickCount == 3)
230 event->type = GDK_3BUTTON_PRESS;
232 sendOrQueueEvent(event);
233 return JSValueMakeUndefined(context);
236 static guint getStateFlags()
238 if (buttonCurrentlyDown == 1)
239 return GDK_BUTTON1_MASK;
240 if (buttonCurrentlyDown == 2)
241 return GDK_BUTTON2_MASK;
242 if (buttonCurrentlyDown == 3)
243 return GDK_BUTTON3_MASK;
247 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
250 if (argumentCount == 1) {
251 button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
252 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
255 GdkEvent* event = gdk_event_new(GDK_BUTTON_RELEASE);
256 if (!prepareMouseButtonEvent(event, button))
257 return JSValueMakeUndefined(context);
259 lastClickPositionX = lastMousePositionX;
260 lastClickPositionY = lastMousePositionY;
261 lastClickButton = buttonCurrentlyDown;
262 lastClickTimeOffset = timeOffset;
263 buttonCurrentlyDown = 0;
265 sendOrQueueEvent(event);
266 return JSValueMakeUndefined(context);
269 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
271 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
273 return JSValueMakeUndefined(context);
275 if (argumentCount < 2)
276 return JSValueMakeUndefined(context);
278 lastMousePositionX = (int)JSValueToNumber(context, arguments[0], exception);
279 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
280 lastMousePositionY = (int)JSValueToNumber(context, arguments[1], exception);
281 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
283 GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
284 event->motion.x = lastMousePositionX;
285 event->motion.y = lastMousePositionY;
287 event->motion.time = GDK_CURRENT_TIME;
288 event->motion.window = gtk_widget_get_window(GTK_WIDGET(view));
289 g_object_ref(event->motion.window);
290 event->button.device = getDefaultGDKPointerDevice(event->motion.window);
291 event->motion.state = getStateFlags();
292 event->motion.axes = 0;
295 gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(view)), lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
296 event->motion.x_root = xRoot;
297 event->motion.y_root = yRoot;
299 sendOrQueueEvent(event, false);
300 return JSValueMakeUndefined(context);
303 static JSValueRef mouseWheelToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
305 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
307 return JSValueMakeUndefined(context);
309 if (argumentCount < 2)
310 return JSValueMakeUndefined(context);
312 int horizontal = (int)JSValueToNumber(context, arguments[0], exception);
313 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
314 int vertical = (int)JSValueToNumber(context, arguments[1], exception);
315 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
317 // GTK+ doesn't support multiple direction scrolls in the same event!
318 g_return_val_if_fail((!vertical || !horizontal), JSValueMakeUndefined(context));
320 GdkEvent* event = gdk_event_new(GDK_SCROLL);
321 event->scroll.x = lastMousePositionX;
322 event->scroll.y = lastMousePositionY;
323 event->scroll.time = GDK_CURRENT_TIME;
324 event->scroll.window = gtk_widget_get_window(GTK_WIDGET(view));
325 g_object_ref(event->scroll.window);
328 event->scroll.direction = GDK_SCROLL_LEFT;
329 else if (horizontal > 0)
330 event->scroll.direction = GDK_SCROLL_RIGHT;
331 else if (vertical < 0)
332 event->scroll.direction = GDK_SCROLL_UP;
333 else if (vertical > 0)
334 event->scroll.direction = GDK_SCROLL_DOWN;
336 g_assert_not_reached();
338 sendOrQueueEvent(event);
339 return JSValueMakeUndefined(context);
342 static JSValueRef beginDragWithFilesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
344 if (argumentCount < 1)
345 return JSValueMakeUndefined(context);
347 // FIXME: Implement this completely once WebCore has complete drag and drop support
348 return JSValueMakeUndefined(context);
351 static void sendOrQueueEvent(GdkEvent* event, bool shouldReplaySavedEvents)
353 // Mouse move events are queued if the previous event was queued or if a
354 // delay was set up by leapForward().
355 if ((dragMode && buttonCurrentlyDown) || endOfQueue != startOfQueue || msgQueue[endOfQueue].delay) {
356 msgQueue[endOfQueue++].event = event;
358 if (shouldReplaySavedEvents)
364 dispatchEvent(event);
367 static void dispatchEvent(GdkEvent* event)
369 webkit_web_frame_layout(mainFrame);
370 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
372 gdk_event_free(event);
376 gtk_main_do_event(event);
378 if (!currentDragSourceContext) {
379 gdk_event_free(event);
383 if (event->type == GDK_MOTION_NOTIFY) {
384 // WebKit has called gtk_drag_start(), but because the main loop isn't
385 // running GDK internals don't know that the drag has started yet. Pump
386 // the main loop a little bit so that GDK is in the correct state.
387 while (gtk_events_pending())
388 gtk_main_iteration();
390 // Simulate a drag motion on the top-level GDK window.
391 GtkWidget* parentWidget = gtk_widget_get_parent(GTK_WIDGET(view));
392 GdkWindow* parentWidgetWindow = gtk_widget_get_window(parentWidget);
393 gdk_drag_motion(currentDragSourceContext, parentWidgetWindow, GDK_DRAG_PROTO_XDND,
394 event->motion.x_root, event->motion.y_root,
395 gdk_drag_context_get_selected_action(currentDragSourceContext),
396 gdk_drag_context_get_actions(currentDragSourceContext),
399 } else if (currentDragSourceContext && event->type == GDK_BUTTON_RELEASE) {
400 // We've released the mouse button, we should just be able to spin the
401 // event loop here and have GTK+ send the appropriate notifications for
402 // the end of the drag.
403 while (gtk_events_pending())
404 gtk_main_iteration();
407 gdk_event_free(event);
410 void replaySavedEvents()
412 // First send all the events that are ready to be sent
413 while (startOfQueue < endOfQueue) {
414 if (msgQueue[startOfQueue].delay) {
415 g_usleep(msgQueue[startOfQueue].delay * 1000);
416 msgQueue[startOfQueue].delay = 0;
419 dispatchEvent(msgQueue[startOfQueue++].event);
426 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
428 if (argumentCount < 1)
429 return JSValueMakeUndefined(context);
431 static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length");
433 webkit_web_frame_layout(mainFrame);
435 // handle modifier keys.
437 if (argumentCount > 1) {
438 JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], exception);
439 if (modifiersArray) {
440 for (int i = 0; i < JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0); ++i) {
441 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0);
442 JSStringRef string = JSValueToStringCopy(context, value, 0);
443 if (JSStringIsEqualToUTF8CString(string, "ctrlKey"))
444 state |= GDK_CONTROL_MASK;
445 else if (JSStringIsEqualToUTF8CString(string, "shiftKey"))
446 state |= GDK_SHIFT_MASK;
447 else if (JSStringIsEqualToUTF8CString(string, "altKey"))
448 state |= GDK_MOD1_MASK;
450 JSStringRelease(string);
455 // handle location argument.
456 int location = DOM_KEY_LOCATION_STANDARD;
457 if (argumentCount > 2)
458 location = (int)JSValueToNumber(context, arguments[2], exception);
460 JSStringRef character = JSValueToStringCopy(context, arguments[0], exception);
461 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
462 int gdkKeySym = GDK_VoidSymbol;
463 if (location == DOM_KEY_LOCATION_NUMPAD) {
464 if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
465 gdkKeySym = GDK_KP_Left;
466 else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
467 gdkKeySym = GDK_KP_Right;
468 else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
469 gdkKeySym = GDK_KP_Up;
470 else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
471 gdkKeySym = GDK_KP_Down;
472 else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
473 gdkKeySym = GDK_KP_Page_Up;
474 else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
475 gdkKeySym = GDK_KP_Page_Down;
476 else if (JSStringIsEqualToUTF8CString(character, "home"))
477 gdkKeySym = GDK_KP_Home;
478 else if (JSStringIsEqualToUTF8CString(character, "end"))
479 gdkKeySym = GDK_KP_End;
480 else if (JSStringIsEqualToUTF8CString(character, "insert"))
481 gdkKeySym = GDK_KP_Insert;
482 else if (JSStringIsEqualToUTF8CString(character, "delete"))
483 gdkKeySym = GDK_KP_Delete;
485 // If we get some other key specified with the numpad location,
486 // crash here, so we add it sooner rather than later.
487 g_assert_not_reached();
489 if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
490 gdkKeySym = GDK_Left;
491 else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
492 gdkKeySym = GDK_Right;
493 else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
495 else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
496 gdkKeySym = GDK_Down;
497 else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
498 gdkKeySym = GDK_Page_Up;
499 else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
500 gdkKeySym = GDK_Page_Down;
501 else if (JSStringIsEqualToUTF8CString(character, "home"))
502 gdkKeySym = GDK_Home;
503 else if (JSStringIsEqualToUTF8CString(character, "end"))
505 else if (JSStringIsEqualToUTF8CString(character, "insert"))
506 gdkKeySym = GDK_Insert;
507 else if (JSStringIsEqualToUTF8CString(character, "delete"))
508 gdkKeySym = GDK_Delete;
509 else if (JSStringIsEqualToUTF8CString(character, "printScreen"))
510 gdkKeySym = GDK_Print;
511 else if (JSStringIsEqualToUTF8CString(character, "F1"))
513 else if (JSStringIsEqualToUTF8CString(character, "F2"))
515 else if (JSStringIsEqualToUTF8CString(character, "F3"))
517 else if (JSStringIsEqualToUTF8CString(character, "F4"))
519 else if (JSStringIsEqualToUTF8CString(character, "F5"))
521 else if (JSStringIsEqualToUTF8CString(character, "F6"))
523 else if (JSStringIsEqualToUTF8CString(character, "F7"))
525 else if (JSStringIsEqualToUTF8CString(character, "F8"))
527 else if (JSStringIsEqualToUTF8CString(character, "F9"))
529 else if (JSStringIsEqualToUTF8CString(character, "F10"))
531 else if (JSStringIsEqualToUTF8CString(character, "F11"))
533 else if (JSStringIsEqualToUTF8CString(character, "F12"))
536 int charCode = JSStringGetCharactersPtr(character)[0];
537 if (charCode == '\n' || charCode == '\r')
538 gdkKeySym = GDK_Return;
539 else if (charCode == '\t')
541 else if (charCode == '\x8')
542 gdkKeySym = GDK_BackSpace;
544 gdkKeySym = gdk_unicode_to_keyval(charCode);
545 if (WTF::isASCIIUpper(charCode))
546 state |= GDK_SHIFT_MASK;
550 JSStringRelease(character);
552 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
554 return JSValueMakeUndefined(context);
556 // create and send the event
557 GdkEvent* pressEvent = gdk_event_new(GDK_KEY_PRESS);
558 pressEvent->key.keyval = gdkKeySym;
559 pressEvent->key.state = state;
560 pressEvent->key.window = gtk_widget_get_window(GTK_WIDGET(view));
561 g_object_ref(pressEvent->key.window);
562 #ifndef GTK_API_VERSION_2
563 gdk_event_set_device(pressEvent, getDefaultGDKPointerDevice(pressEvent->key.window));
566 // When synthesizing an event, an invalid hardware_keycode value
567 // can cause it to be badly processed by Gtk+.
570 if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys, &n_keys)) {
571 pressEvent->key.hardware_keycode = keys[0].keycode;
575 GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
576 dispatchEvent(pressEvent);
577 releaseEvent->key.type = GDK_KEY_RELEASE;
578 dispatchEvent(releaseEvent);
580 return JSValueMakeUndefined(context);
583 static void zoomIn(gboolean fullContentsZoom)
585 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
589 webkit_web_view_set_full_content_zoom(view, fullContentsZoom);
590 gfloat currentZoom = webkit_web_view_get_zoom_level(view);
591 webkit_web_view_set_zoom_level(view, currentZoom * zoomMultiplierRatio);
594 static void zoomOut(gboolean fullContentsZoom)
596 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
600 webkit_web_view_set_full_content_zoom(view, fullContentsZoom);
601 gfloat currentZoom = webkit_web_view_get_zoom_level(view);
602 webkit_web_view_set_zoom_level(view, currentZoom / zoomMultiplierRatio);
605 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
608 return JSValueMakeUndefined(context);
611 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
614 return JSValueMakeUndefined(context);
617 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
620 return JSValueMakeUndefined(context);
623 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
626 return JSValueMakeUndefined(context);
629 static JSStaticFunction staticFunctions[] = {
630 { "mouseWheelTo", mouseWheelToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
631 { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
632 { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
633 { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
634 { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
635 { "beginDragWithFiles", beginDragWithFilesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
636 { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
637 { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
638 { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
639 { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
640 { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
641 { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
645 static JSStaticValue staticValues[] = {
646 { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
650 static JSClassRef getClass(JSContextRef context)
652 static JSClassRef eventSenderClass = 0;
654 if (!eventSenderClass) {
655 JSClassDefinition classDefinition = {
657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
658 classDefinition.staticFunctions = staticFunctions;
659 classDefinition.staticValues = staticValues;
661 eventSenderClass = JSClassCreate(&classDefinition);
664 return eventSenderClass;
667 JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
672 // Fly forward in time one second when the main frame loads. This will
673 // ensure that when a test begins clicking in the same location as
674 // a previous test, those clicks won't be interpreted as continuations
675 // of the previous test's click sequences.
678 lastMousePositionX = lastMousePositionY = 0;
679 lastClickPositionX = lastClickPositionY = 0;
680 lastClickTimeOffset = 0;
682 buttonCurrentlyDown = 0;
688 currentDragSourceContext = 0;
691 return JSObjectMake(context, getClass(context), 0);
694 void dragBeginCallback(GtkWidget*, GdkDragContext* context, gpointer)
696 currentDragSourceContext = context;
699 void dragEndCallback(GtkWidget*, GdkDragContext* context, gpointer)
701 currentDragSourceContext = 0;
704 gboolean dragFailedCallback(GtkWidget*, GdkDragContext* context, gpointer)
706 // Return TRUE here to disable the stupid GTK+ drag failed animation,
707 // which introduces asynchronous behavior into our drags.