1 /*
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 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "EventSender.h"
33
34 #include "DumpRenderTree.h"
35
36 #include <JavaScriptCore/JSObjectRef.h>
37 #include <JavaScriptCore/JSRetainPtr.h>
38 #include <JavaScriptCore/JSStringRef.h>
39 #include <webkit/webkitwebframe.h>
40 #include <webkit/webkitwebview.h>
41 #include <wtf/ASCIICType.h>
42 #include <wtf/Platform.h>
43
44 #include <gdk/gdk.h>
45 #include <gdk/gdkkeysyms.h>
46 #include <string.h>
47
48 // TODO: Currently drag and drop related code is left out and
49 // should be merged once we have drag and drop support in WebCore.
50
51 extern "C" {
52 extern void webkit_web_frame_layout(WebKitWebFrame* frame);
53 }
54
55 static bool down = false;
56 static bool currentEventButton = 1;
57 static bool dragMode = true;
58 static bool replayingSavedEvents = false;
59 static int lastMousePositionX;
60 static int lastMousePositionY;
61
62 static int lastClickPositionX;
63 static int lastClickPositionY;
64 static int clickCount = 0;
65
66 struct DelayedMessage {
67 GdkEvent event;
68 gulong delay;
69 gboolean isDragEvent;
70 };
71
72 static DelayedMessage msgQueue[1024];
73
74 static unsigned endOfQueue;
75 static unsigned startOfQueue;
76
77 static const float zoomMultiplierRatio = 1.2f;
78
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
85 };
86
getDragModeCallback(JSContextRef context,JSObjectRef object,JSStringRef propertyName,JSValueRef * exception)87 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
88 {
89 return JSValueMakeBoolean(context, dragMode);
90 }
91
setDragModeCallback(JSContextRef context,JSObjectRef object,JSStringRef propertyName,JSValueRef value,JSValueRef * exception)92 static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
93 {
94 dragMode = JSValueToBoolean(context, value);
95 return true;
96 }
97
leapForwardCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)98 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
99 {
100 // FIXME: Add proper support for forward leaps
101 return JSValueMakeUndefined(context);
102 }
103
contextClickCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)104 static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
105 {
106 webkit_web_frame_layout(mainFrame);
107
108 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
109 if (!view)
110 return JSValueMakeUndefined(context);
111
112 GdkEvent event;
113 memset(&event, 0, sizeof(event));
114 event.button.button = 3;
115 event.button.x = lastMousePositionX;
116 event.button.y = lastMousePositionY;
117 event.button.window = GTK_WIDGET(view)->window;
118
119 gboolean return_val;
120 down = true;
121 event.type = GDK_BUTTON_PRESS;
122 g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
123
124 down = false;
125 event.type = GDK_BUTTON_RELEASE;
126 g_signal_emit_by_name(view, "button_release_event", &event, &return_val);
127
128 return JSValueMakeUndefined(context);
129 }
130
updateClickCount(int button)131 static void updateClickCount(int button)
132 {
133 // FIXME: take the last clicked button number and the time of last click into account.
134 if (lastClickPositionX != lastMousePositionX || lastClickPositionY != lastMousePositionY || currentEventButton != button)
135 clickCount = 1;
136 else
137 clickCount++;
138 }
139
140 #if !GTK_CHECK_VERSION(2,17,3)
getRootCoords(GtkWidget * view,int * rootX,int * rootY)141 static void getRootCoords(GtkWidget* view, int* rootX, int* rootY)
142 {
143 GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(view));
144 int tmpX, tmpY;
145
146 gtk_widget_translate_coordinates(view, window, lastMousePositionX, lastMousePositionY, &tmpX, &tmpY);
147
148 gdk_window_get_origin(window->window, rootX, rootY);
149
150 *rootX += tmpX;
151 *rootY += tmpY;
152 }
153 #endif
154
mouseDownCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)155 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
156 {
157 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
158 if (!view)
159 return JSValueMakeUndefined(context);
160
161 down = true;
162
163 GdkEvent event;
164 memset(&event, 0, sizeof(event));
165 event.type = GDK_BUTTON_PRESS;
166 event.button.button = 1;
167
168 if (argumentCount == 1) {
169 event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1;
170 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
171 }
172
173 currentEventButton = event.button.button;
174
175 event.button.x = lastMousePositionX;
176 event.button.y = lastMousePositionY;
177 event.button.window = GTK_WIDGET(view)->window;
178 event.button.time = GDK_CURRENT_TIME;
179 event.button.device = gdk_device_get_core_pointer();
180
181 int x_root, y_root;
182 #if GTK_CHECK_VERSION(2,17,3)
183 gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root);
184 #else
185 getRootCoords(GTK_WIDGET(view), &x_root, &y_root);
186 #endif
187
188 event.button.x_root = x_root;
189 event.button.y_root = y_root;
190
191 updateClickCount(event.button.button);
192
193 if (!msgQueue[endOfQueue].delay) {
194 webkit_web_frame_layout(mainFrame);
195
196 gboolean return_val;
197 g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
198 if (clickCount == 2) {
199 event.type = GDK_2BUTTON_PRESS;
200 g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
201 }
202 } else {
203 // replaySavedEvents should have the required logic to make leapForward delays work
204 msgQueue[endOfQueue++].event = event;
205 replaySavedEvents();
206 }
207
208 return JSValueMakeUndefined(context);
209 }
210
getStateFlags()211 static guint getStateFlags()
212 {
213 guint state = 0;
214
215 if (down) {
216 if (currentEventButton == 1)
217 state = GDK_BUTTON1_MASK;
218 else if (currentEventButton == 2)
219 state = GDK_BUTTON2_MASK;
220 else if (currentEventButton == 3)
221 state = GDK_BUTTON3_MASK;
222 } else
223 state = 0;
224
225 return state;
226 }
227
mouseUpCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)228 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
229 {
230
231 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
232 if (!view)
233 return JSValueMakeUndefined(context);
234
235 GdkEvent event;
236 memset(&event, 0, sizeof(event));
237 event.type = GDK_BUTTON_RELEASE;
238 event.button.button = 1;
239
240 if (argumentCount == 1) {
241 event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1;
242 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
243 }
244
245 currentEventButton = event.button.button;
246
247 event.button.x = lastMousePositionX;
248 event.button.y = lastMousePositionY;
249 event.button.window = GTK_WIDGET(view)->window;
250 event.button.time = GDK_CURRENT_TIME;
251 event.button.device = gdk_device_get_core_pointer();
252 event.button.state = getStateFlags();
253
254 down = false;
255
256 int x_root, y_root;
257 #if GTK_CHECK_VERSION(2,17,3)
258 gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root);
259 #else
260 getRootCoords(GTK_WIDGET(view), &x_root, &y_root);
261 #endif
262
263 event.button.x_root = x_root;
264 event.button.y_root = y_root;
265
266 if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) {
267 msgQueue[endOfQueue].event = event;
268 msgQueue[endOfQueue++].isDragEvent = true;
269 replaySavedEvents();
270 } else {
271 webkit_web_frame_layout(mainFrame);
272
273 gboolean return_val;
274 g_signal_emit_by_name(view, "button_release_event", &event, &return_val);
275 }
276
277 lastClickPositionX = lastMousePositionX;
278 lastClickPositionY = lastMousePositionY;
279
280 return JSValueMakeUndefined(context);
281 }
282
mouseMoveToCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)283 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
284 {
285 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
286 if (!view)
287 return JSValueMakeUndefined(context);
288
289 if (argumentCount < 2)
290 return JSValueMakeUndefined(context);
291
292 lastMousePositionX = (int)JSValueToNumber(context, arguments[0], exception);
293 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
294 lastMousePositionY = (int)JSValueToNumber(context, arguments[1], exception);
295 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
296
297 GdkEvent event;
298 memset(&event, 0, sizeof(event));
299 event.type = GDK_MOTION_NOTIFY;
300 event.motion.x = lastMousePositionX;
301 event.motion.y = lastMousePositionY;
302 event.motion.time = GDK_CURRENT_TIME;
303 event.motion.window = GTK_WIDGET(view)->window;
304 event.motion.device = gdk_device_get_core_pointer();
305
306 int x_root, y_root;
307 #if GTK_CHECK_VERSION(2,17,3)
308 gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root);
309 #else
310 getRootCoords(GTK_WIDGET(view), &x_root, &y_root);
311 #endif
312
313 event.motion.x_root = x_root;
314 event.motion.y_root = y_root;
315
316 event.motion.state = getStateFlags();
317
318 if (dragMode && down && !replayingSavedEvents) {
319 msgQueue[endOfQueue].event = event;
320 msgQueue[endOfQueue++].isDragEvent = true;
321 } else {
322 webkit_web_frame_layout(mainFrame);
323
324 gboolean return_val;
325 g_signal_emit_by_name(view, "motion_notify_event", &event, &return_val);
326 }
327
328 return JSValueMakeUndefined(context);
329 }
330
mouseWheelToCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)331 static JSValueRef mouseWheelToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
332 {
333 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
334 if (!view)
335 return JSValueMakeUndefined(context);
336
337 if (argumentCount < 2)
338 return JSValueMakeUndefined(context);
339
340 int horizontal = (int)JSValueToNumber(context, arguments[0], exception);
341 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
342 int vertical = (int)JSValueToNumber(context, arguments[1], exception);
343 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
344
345 // GTK+ doesn't support multiple direction scrolls in the same event!
346 g_return_val_if_fail((!vertical || !horizontal), JSValueMakeUndefined(context));
347
348 GdkEvent event;
349 event.type = GDK_SCROLL;
350 event.scroll.x = lastMousePositionX;
351 event.scroll.y = lastMousePositionY;
352 event.scroll.time = GDK_CURRENT_TIME;
353 event.scroll.window = GTK_WIDGET(view)->window;
354
355 if (horizontal < 0)
356 event.scroll.direction = GDK_SCROLL_LEFT;
357 else if (horizontal > 0)
358 event.scroll.direction = GDK_SCROLL_RIGHT;
359 else if (vertical < 0)
360 event.scroll.direction = GDK_SCROLL_UP;
361 else if (vertical > 0)
362 event.scroll.direction = GDK_SCROLL_DOWN;
363 else
364 g_assert_not_reached();
365
366 if (dragMode && down && !replayingSavedEvents) {
367 msgQueue[endOfQueue].event = event;
368 msgQueue[endOfQueue++].isDragEvent = true;
369 } else {
370 webkit_web_frame_layout(mainFrame);
371 gtk_main_do_event(&event);
372 }
373
374 return JSValueMakeUndefined(context);
375 }
376
beginDragWithFilesCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)377 static JSValueRef beginDragWithFilesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
378 {
379 if (argumentCount < 1)
380 return JSValueMakeUndefined(context);
381
382 // FIXME: Implement this completely once WebCore has complete drag and drop support
383 return JSValueMakeUndefined(context);
384 }
385
replaySavedEvents()386 void replaySavedEvents()
387 {
388 // FIXME: This doesn't deal with forward leaps, but it should.
389
390 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
391 if (!view)
392 return;
393
394 replayingSavedEvents = true;
395
396 for (unsigned queuePos = 0; queuePos < endOfQueue; queuePos++) {
397 GdkEvent event = msgQueue[queuePos].event;
398 gboolean return_val;
399
400 switch (event.type) {
401 case GDK_BUTTON_RELEASE:
402 g_signal_emit_by_name(view, "button_release_event", &event, &return_val);
403 break;
404 case GDK_BUTTON_PRESS:
405 g_signal_emit_by_name(view, "button_press_event", &event, &return_val);
406 break;
407 case GDK_MOTION_NOTIFY:
408 g_signal_emit_by_name(view, "motion_notify_event", &event, &return_val);
409 break;
410 default:
411 continue;
412 }
413
414 startOfQueue++;
415 }
416
417 int numQueuedMessages = endOfQueue - startOfQueue;
418 if (!numQueuedMessages) {
419 startOfQueue = 0;
420 endOfQueue = 0;
421 replayingSavedEvents = false;
422 return;
423 }
424
425 startOfQueue = 0;
426 endOfQueue = 0;
427
428 replayingSavedEvents = false;
429 }
430
keyDownCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)431 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
432 {
433 if (argumentCount < 1)
434 return JSValueMakeUndefined(context);
435
436 static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length");
437
438 webkit_web_frame_layout(mainFrame);
439
440 // handle modifier keys.
441 int state = 0;
442 if (argumentCount > 1) {
443 JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], exception);
444 if (modifiersArray) {
445 for (int i = 0; i < JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0); ++i) {
446 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0);
447 JSStringRef string = JSValueToStringCopy(context, value, 0);
448 if (JSStringIsEqualToUTF8CString(string, "ctrlKey"))
449 state |= GDK_CONTROL_MASK;
450 else if (JSStringIsEqualToUTF8CString(string, "shiftKey"))
451 state |= GDK_SHIFT_MASK;
452 else if (JSStringIsEqualToUTF8CString(string, "altKey"))
453 state |= GDK_MOD1_MASK;
454
455 JSStringRelease(string);
456 }
457 }
458 }
459
460 // handle location argument.
461 int location = DOM_KEY_LOCATION_STANDARD;
462 if (argumentCount > 2)
463 location = (int)JSValueToNumber(context, arguments[2], exception);
464
465 JSStringRef character = JSValueToStringCopy(context, arguments[0], exception);
466 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
467 int gdkKeySym = GDK_VoidSymbol;
468 if (location == DOM_KEY_LOCATION_NUMPAD) {
469 if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
470 gdkKeySym = GDK_KP_Left;
471 else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
472 gdkKeySym = GDK_KP_Right;
473 else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
474 gdkKeySym = GDK_KP_Up;
475 else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
476 gdkKeySym = GDK_KP_Down;
477 else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
478 gdkKeySym = GDK_KP_Page_Up;
479 else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
480 gdkKeySym = GDK_KP_Page_Down;
481 else if (JSStringIsEqualToUTF8CString(character, "home"))
482 gdkKeySym = GDK_KP_Home;
483 else if (JSStringIsEqualToUTF8CString(character, "end"))
484 gdkKeySym = GDK_KP_End;
485 else
486 // Assume we only get arrow/pgUp/pgDn/home/end keys with
487 // location=NUMPAD for now.
488 g_assert_not_reached();
489 } else {
490 if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
491 gdkKeySym = GDK_Left;
492 else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
493 gdkKeySym = GDK_Right;
494 else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
495 gdkKeySym = GDK_Up;
496 else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
497 gdkKeySym = GDK_Down;
498 else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
499 gdkKeySym = GDK_Page_Up;
500 else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
501 gdkKeySym = GDK_Page_Down;
502 else if (JSStringIsEqualToUTF8CString(character, "home"))
503 gdkKeySym = GDK_Home;
504 else if (JSStringIsEqualToUTF8CString(character, "end"))
505 gdkKeySym = GDK_End;
506 else if (JSStringIsEqualToUTF8CString(character, "delete"))
507 gdkKeySym = GDK_BackSpace;
508 else if (JSStringIsEqualToUTF8CString(character, "F1"))
509 gdkKeySym = GDK_F1;
510 else if (JSStringIsEqualToUTF8CString(character, "F2"))
511 gdkKeySym = GDK_F2;
512 else if (JSStringIsEqualToUTF8CString(character, "F3"))
513 gdkKeySym = GDK_F3;
514 else if (JSStringIsEqualToUTF8CString(character, "F4"))
515 gdkKeySym = GDK_F4;
516 else if (JSStringIsEqualToUTF8CString(character, "F5"))
517 gdkKeySym = GDK_F5;
518 else if (JSStringIsEqualToUTF8CString(character, "F6"))
519 gdkKeySym = GDK_F6;
520 else if (JSStringIsEqualToUTF8CString(character, "F7"))
521 gdkKeySym = GDK_F7;
522 else if (JSStringIsEqualToUTF8CString(character, "F8"))
523 gdkKeySym = GDK_F8;
524 else if (JSStringIsEqualToUTF8CString(character, "F9"))
525 gdkKeySym = GDK_F9;
526 else if (JSStringIsEqualToUTF8CString(character, "F10"))
527 gdkKeySym = GDK_F10;
528 else if (JSStringIsEqualToUTF8CString(character, "F11"))
529 gdkKeySym = GDK_F11;
530 else if (JSStringIsEqualToUTF8CString(character, "F12"))
531 gdkKeySym = GDK_F12;
532 else {
533 int charCode = JSStringGetCharactersPtr(character)[0];
534 if (charCode == '\n' || charCode == '\r')
535 gdkKeySym = GDK_Return;
536 else if (charCode == '\t')
537 gdkKeySym = GDK_Tab;
538 else if (charCode == '\x8')
539 gdkKeySym = GDK_BackSpace;
540 else {
541 gdkKeySym = gdk_unicode_to_keyval(charCode);
542 if (WTF::isASCIIUpper(charCode))
543 state |= GDK_SHIFT_MASK;
544 }
545 }
546 }
547 JSStringRelease(character);
548
549 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
550 if (!view)
551 return JSValueMakeUndefined(context);
552
553 // create and send the event
554 GdkEvent event;
555 memset(&event, 0, sizeof(event));
556 event.key.keyval = gdkKeySym;
557 event.key.state = state;
558 event.key.window = GTK_WIDGET(view)->window;
559
560 gboolean return_val;
561 event.key.type = GDK_KEY_PRESS;
562 g_signal_emit_by_name(view, "key-press-event", &event.key, &return_val);
563
564 event.key.type = GDK_KEY_RELEASE;
565 g_signal_emit_by_name(view, "key-release-event", &event.key, &return_val);
566
567 return JSValueMakeUndefined(context);
568 }
569
textZoomInCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)570 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
571 {
572 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
573 if (!view)
574 return JSValueMakeUndefined(context);
575
576 gfloat currentZoom = webkit_web_view_get_zoom_level(view);
577 webkit_web_view_set_zoom_level(view, currentZoom * zoomMultiplierRatio);
578
579 return JSValueMakeUndefined(context);
580 }
581
textZoomOutCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)582 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
583 {
584 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
585 if (!view)
586 return JSValueMakeUndefined(context);
587
588 gfloat currentZoom = webkit_web_view_get_zoom_level(view);
589 webkit_web_view_set_zoom_level(view, currentZoom / zoomMultiplierRatio);
590
591 return JSValueMakeUndefined(context);
592 }
593
zoomPageInCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)594 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
595 {
596 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
597 if (!view)
598 return JSValueMakeUndefined(context);
599
600 webkit_web_view_zoom_in(view);
601 return JSValueMakeUndefined(context);
602 }
603
zoomPageOutCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)604 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
605 {
606 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
607 if (!view)
608 return JSValueMakeUndefined(context);
609
610 webkit_web_view_zoom_out(view);
611 return JSValueMakeUndefined(context);
612 }
613
614 static JSStaticFunction staticFunctions[] = {
615 { "mouseWheelTo", mouseWheelToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
616 { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
617 { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
618 { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
619 { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
620 { "beginDragWithFiles", beginDragWithFilesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
621 { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
622 { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
623 { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
624 { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
625 { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
626 { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
627 { 0, 0, 0 }
628 };
629
630 static JSStaticValue staticValues[] = {
631 { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
632 { 0, 0, 0, 0 }
633 };
634
getClass(JSContextRef context)635 static JSClassRef getClass(JSContextRef context)
636 {
637 static JSClassRef eventSenderClass = 0;
638
639 if (!eventSenderClass) {
640 JSClassDefinition classDefinition = {
641 0, 0, 0, 0, 0, 0,
642 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
643 classDefinition.staticFunctions = staticFunctions;
644 classDefinition.staticValues = staticValues;
645
646 eventSenderClass = JSClassCreate(&classDefinition);
647 }
648
649 return eventSenderClass;
650 }
651
makeEventSender(JSContextRef context)652 JSObjectRef makeEventSender(JSContextRef context)
653 {
654 down = false;
655 dragMode = true;
656 lastMousePositionX = lastMousePositionY = 0;
657 lastClickPositionX = lastClickPositionY = 0;
658
659 if (!replayingSavedEvents) {
660 // This function can be called in the middle of a test, even
661 // while replaying saved events. Resetting these while doing that
662 // can break things.
663 endOfQueue = 0;
664 startOfQueue = 0;
665 }
666
667 return JSObjectMake(context, getClass(context), 0);
668 }
669