• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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