92b78899121ca8a4b0e02e1234ae33c52f8953e5
[WebKit-https.git] / Tools / DumpRenderTree / efl / EventSender.cpp
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  * Copyright (C) 2010 Igalia S.L.
6  * Copyright (C) 2011 ProFUSION Embedded Systems
7  * Copyright (C) 2011, 2012 Samsung Electronics
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1.  Redistributions of source code must retain the above copyright
14  *     notice, this list of conditions and the following disclaimer.
15  * 2.  Redistributions in binary form must reproduce the above copyright
16  *     notice, this list of conditions and the following disclaimer in the
17  *     documentation and/or other materials provided with the distribution.
18  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
19  *     its contributors may be used to endorse or promote products derived
20  *     from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "config.h"
35 #include "EventSender.h"
36
37 #include "DumpRenderTree.h"
38 #include "DumpRenderTreeChrome.h"
39 #include "IntPoint.h"
40 #include "JSStringUtils.h"
41 #include "NotImplemented.h"
42 #include "PlatformEvent.h"
43 #include "WebCoreSupport/DumpRenderTreeSupportEfl.h"
44 #include "ewk_private.h"
45 #include <EWebKit.h>
46 #include <Ecore_Input.h>
47 #include <JavaScriptCore/JSObjectRef.h>
48 #include <JavaScriptCore/JSRetainPtr.h>
49 #include <JavaScriptCore/JSStringRef.h>
50 #include <JavaScriptCore/OpaqueJSString.h>
51 #include <wtf/ASCIICType.h>
52 #include <wtf/Platform.h>
53 #include <wtf/text/CString.h>
54
55 static bool gDragMode;
56 static int gTimeOffset = 0;
57
58 static int gLastMousePositionX;
59 static int gLastMousePositionY;
60 static int gLastClickPositionX;
61 static int gLastClickPositionY;
62 static int gLastClickTimeOffset;
63 static int gLastClickButton;
64 static int gButtonCurrentlyDown;
65 static int gClickCount;
66
67 static const float zoomMultiplierRatio = 1.2f;
68
69 // Key event location code defined in DOM Level 3.
70 enum KeyLocationCode {
71     DomKeyLocationStandard,
72     DomKeyLocationLeft,
73     DomKeyLocationRight,
74     DomKeyLocationNumpad
75 };
76
77 enum EvasKeyModifier {
78     EvasKeyModifierNone    = 0,
79     EvasKeyModifierControl = 1 << 0,
80     EvasKeyModifierShift   = 1 << 1,
81     EvasKeyModifierAlt     = 1 << 2,
82     EvasKeyModifierMeta    = 1 << 3
83 };
84
85 enum EvasMouseButton {
86     EvasMouseButtonNone,
87     EvasMouseButtonLeft,
88     EvasMouseButtonMiddle,
89     EvasMouseButtonRight
90 };
91
92 enum EvasMouseEvent {
93     EvasMouseEventNone        = 0,
94     EvasMouseEventDown        = 1 << 0,
95     EvasMouseEventUp          = 1 << 1,
96     EvasMouseEventMove        = 1 << 2,
97     EvasMouseEventScrollUp    = 1 << 3,
98     EvasMouseEventScrollDown  = 1 << 4,
99     EvasMouseEventScrollLeft  = 1 << 5,
100     EvasMouseEventScrollRight = 1 << 6,
101     EvasMouseEventClick       = EvasMouseEventMove | EvasMouseEventDown | EvasMouseEventUp,
102 };
103
104 enum ZoomEvent {
105     ZoomIn,
106     ZoomOut
107 };
108
109 enum EventQueueStrategy {
110     FeedQueuedEvents,
111     DoNotFeedQueuedEvents
112 };
113
114 struct KeyEventInfo {
115     KeyEventInfo(const CString& keyName, unsigned modifiers, const CString& keyString = CString())
116         : keyName(keyName)
117         , keyString(keyString)
118         , modifiers(modifiers)
119     {
120     }
121
122     const CString keyName;
123     const CString keyString;
124     unsigned modifiers;
125 };
126
127 struct MouseEventInfo {
128     MouseEventInfo(EvasMouseEvent event, unsigned modifiers = EvasKeyModifierNone, EvasMouseButton button = EvasMouseButtonNone, int horizontalDelta = 0, int verticalDelta = 0)
129         : event(event)
130         , modifiers(modifiers)
131         , button(button)
132         , horizontalDelta(horizontalDelta)
133         , verticalDelta(verticalDelta)
134     {
135     }
136
137     EvasMouseEvent event;
138     unsigned modifiers;
139     EvasMouseButton button;
140     int horizontalDelta;
141     int verticalDelta;
142 };
143
144 struct DelayedEvent {
145     DelayedEvent(MouseEventInfo* eventInfo, unsigned long delay = 0)
146         : eventInfo(eventInfo)
147         , delay(delay)
148     {
149     }
150
151     MouseEventInfo* eventInfo;
152     unsigned long delay;
153 };
154
155 struct TouchEventInfo {
156     TouchEventInfo(unsigned id, Ewk_Touch_Point_Type state, const WebCore::IntPoint& point)
157         : state(state)
158         , point(point)
159         , id(id)
160     {
161     }
162
163     unsigned id;
164     Ewk_Touch_Point_Type state;
165     WebCore::IntPoint point;
166 };
167
168 static unsigned touchModifiers;
169
170 WTF::Vector<TouchEventInfo>& touchPointList()
171 {
172     DEFINE_STATIC_LOCAL(WTF::Vector<TouchEventInfo>, staticTouchPointList, ());
173     return staticTouchPointList;
174 }
175
176 WTF::Vector<DelayedEvent>& delayedEventQueue()
177 {
178     DEFINE_STATIC_LOCAL(WTF::Vector<DelayedEvent>, staticDelayedEventQueue, ());
179     return staticDelayedEventQueue;
180 }
181
182
183 static void feedOrQueueMouseEvent(MouseEventInfo*, EventQueueStrategy);
184 static void feedMouseEvent(MouseEventInfo*);
185 static void feedQueuedMouseEvents();
186
187 static void setEvasModifiers(Evas* evas, unsigned modifiers)
188 {
189     static const char* modifierNames[] = { "Control", "Shift", "Alt", "Super" };
190     for (unsigned modifier = 0; modifier < 4; ++modifier) {
191         if (modifiers & (1 << modifier))
192             evas_key_modifier_on(evas, modifierNames[modifier]);
193         else
194             evas_key_modifier_off(evas, modifierNames[modifier]);
195     }
196 }
197
198 static EvasMouseButton translateMouseButtonNumber(int eventSenderButtonNumber)
199 {
200     static const EvasMouseButton translationTable[] = {
201         EvasMouseButtonLeft,
202         EvasMouseButtonMiddle,
203         EvasMouseButtonRight,
204         EvasMouseButtonMiddle // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
205     };
206     static const unsigned translationTableSize = sizeof(translationTable) / sizeof(translationTable[0]);
207
208     if (eventSenderButtonNumber < translationTableSize)
209         return translationTable[eventSenderButtonNumber];
210
211     return EvasMouseButtonLeft;
212 }
213
214 static Eina_Bool sendClick(void*)
215 {
216     MouseEventInfo* eventInfo = new MouseEventInfo(EvasMouseEventClick, EvasKeyModifierNone, EvasMouseButtonLeft);
217     feedOrQueueMouseEvent(eventInfo, FeedQueuedEvents);
218     return ECORE_CALLBACK_CANCEL;
219 }
220
221 static JSValueRef scheduleAsynchronousClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
222 {
223     ecore_idler_add(sendClick, 0);
224     return JSValueMakeUndefined(context);
225 }
226
227 static void updateClickCount(int button)
228 {
229     if (gLastClickPositionX != gLastMousePositionX
230         || gLastClickPositionY != gLastMousePositionY
231         || gLastClickButton != button
232         || gTimeOffset - gLastClickTimeOffset >= 1)
233         gClickCount = 1;
234     else
235         gClickCount++;
236 }
237
238 static EvasKeyModifier modifierFromJSValue(JSContextRef context, const JSValueRef value)
239 {
240     JSRetainPtr<JSStringRef> jsKeyValue(Adopt, JSValueToStringCopy(context, value, 0));
241
242     if (equals(jsKeyValue, "ctrlKey") || equals(jsKeyValue, "addSelectionKey"))
243         return EvasKeyModifierControl;
244     if (equals(jsKeyValue, "shiftKey") || equals(jsKeyValue, "rangeSelectionKey"))
245         return EvasKeyModifierShift;
246     if (equals(jsKeyValue, "altKey"))
247         return EvasKeyModifierAlt;
248     if (equals(jsKeyValue, "metaKey"))
249         return EvasKeyModifierMeta;
250
251     return EvasKeyModifierNone;
252 }
253
254 static unsigned modifiersFromJSValue(JSContextRef context, const JSValueRef modifiers)
255 {
256     // The value may either be a string with a single modifier or an array of modifiers.
257     if (JSValueIsString(context, modifiers))
258         return modifierFromJSValue(context, modifiers);
259
260     JSObjectRef modifiersArray = JSValueToObject(context, modifiers, 0);
261     if (!modifiersArray)
262         return EvasKeyModifierNone;
263
264     unsigned modifier = EvasKeyModifierNone;
265     JSRetainPtr<JSStringRef> lengthProperty(Adopt, JSStringCreateWithUTF8CString("length"));
266     int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty.get(), 0), 0);
267     for (int i = 0; i < modifiersCount; ++i)
268         modifier |= modifierFromJSValue(context, JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0));
269     return modifier;
270 }
271
272 static JSValueRef getMenuItemTitleCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
273 {
274     Ewk_Context_Menu_Item* item = static_cast<Ewk_Context_Menu_Item*>(JSObjectGetPrivate(object));
275     CString label;
276     if (ewk_context_menu_item_type_get(item) == EWK_SEPARATOR_TYPE)
277         label = "<separator>";
278     else
279         label = ewk_context_menu_item_title_get(item);
280
281     return JSValueMakeString(context, JSStringCreateWithUTF8CString(label.data()));
282 }
283
284 static bool setMenuItemTitleCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
285 {
286     return true;
287 }
288
289 static JSValueRef menuItemClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
290 {
291     Ewk_Context_Menu_Item* item = static_cast<Ewk_Context_Menu_Item*>(JSObjectGetPrivate(thisObject));
292     ewk_context_menu_item_select(ewk_context_menu_item_parent_get(item), item);
293     return JSValueMakeUndefined(context);
294 }
295
296 static JSStaticFunction staticMenuItemFunctions[] = {
297     { "click", menuItemClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
298     { 0, 0, 0 }
299 };
300
301 static JSStaticValue staticMenuItemValues[] = {
302     { "title", getMenuItemTitleCallback, setMenuItemTitleCallback, kJSPropertyAttributeNone },
303     { 0, 0, 0, 0 }
304 };
305
306 static JSClassRef getMenuItemClass()
307 {
308     static JSClassRef menuItemClass = 0;
309
310     if (!menuItemClass) {
311         JSClassDefinition classDefinition = {
312             0, 0, 0, 0, 0, 0,
313             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
314         classDefinition.staticFunctions = staticMenuItemFunctions;
315         classDefinition.staticValues = staticMenuItemValues;
316
317         menuItemClass = JSClassCreate(&classDefinition);
318     }
319
320     return menuItemClass;
321 }
322
323 static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
324 {
325     Evas_Object* view = ewk_frame_view_get(browser->mainFrame());
326     if (!view)
327         return JSValueMakeUndefined(context);
328
329     Evas* evas = evas_object_evas_get(view);
330     if (!evas)
331         return JSValueMakeUndefined(context);
332
333     Evas_Event_Mouse_Down mouseDown;
334     mouseDown.button = 3;
335     mouseDown.output.x = gLastMousePositionX;
336     mouseDown.output.y = gLastMousePositionY;
337     mouseDown.canvas.x = gLastMousePositionX;
338     mouseDown.canvas.y = gLastMousePositionY;
339     mouseDown.data = 0;
340     mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
341     mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
342     mouseDown.flags = EVAS_BUTTON_NONE;
343     mouseDown.timestamp = ecore_loop_time_get();
344     mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
345     mouseDown.dev = 0;
346
347     ewk_view_context_menu_forward_event(view, &mouseDown);
348     Ewk_Context_Menu* ewkMenu = ewk_view_context_menu_get(view);
349
350     JSValueRef valueRef = JSObjectMakeArray(context, 0, 0, 0);
351     if (ewkMenu) {
352         const Eina_List* ewkMenuItems = ewk_context_menu_item_list_get(ewkMenu);
353         JSValueRef arrayValues[eina_list_count(ewkMenuItems)];
354
355         const Eina_List* listIterator;
356         void* data;
357         int index = 0;
358         EINA_LIST_FOREACH(ewkMenuItems, listIterator, data)
359             arrayValues[index++] = JSObjectMake(context, getMenuItemClass(), data);
360
361         if (index)
362             valueRef = JSObjectMakeArray(context, index - 1, arrayValues, 0);
363     }
364
365     return valueRef;
366 }
367
368 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
369 {
370     int button = 0;
371     if (argumentCount == 1) {
372         button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
373
374         if (exception && *exception)
375             return JSValueMakeUndefined(context);
376     }
377
378     button = translateMouseButtonNumber(button);
379     // If the same mouse button is already in the down position don't send another event as it may confuse Xvfb.
380     if (gButtonCurrentlyDown == button)
381         return JSValueMakeUndefined(context);
382
383     updateClickCount(button);
384
385     unsigned modifiers = argumentCount >= 2 ? modifiersFromJSValue(context, arguments[1]) : EvasKeyModifierNone;
386     MouseEventInfo* eventInfo = new MouseEventInfo(EvasMouseEventDown, modifiers, static_cast<EvasMouseButton>(button));
387     feedOrQueueMouseEvent(eventInfo, FeedQueuedEvents);
388     gButtonCurrentlyDown = button;
389     return JSValueMakeUndefined(context);
390 }
391
392 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
393 {
394     int button = 0;
395     if (argumentCount == 1) {
396         button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
397         if (exception && *exception)
398             return JSValueMakeUndefined(context);
399     }
400
401     gLastClickPositionX = gLastMousePositionX;
402     gLastClickPositionY = gLastMousePositionY;
403     gLastClickButton = gButtonCurrentlyDown;
404     gLastClickTimeOffset = gTimeOffset;
405     gButtonCurrentlyDown = 0;
406
407     unsigned modifiers = argumentCount >= 2 ? modifiersFromJSValue(context, arguments[1]) : EvasKeyModifierNone;
408     MouseEventInfo* eventInfo = new MouseEventInfo(EvasMouseEventUp, modifiers, translateMouseButtonNumber(button));
409     feedOrQueueMouseEvent(eventInfo, FeedQueuedEvents);
410     return JSValueMakeUndefined(context);
411 }
412
413 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
414 {
415     if (argumentCount < 2)
416         return JSValueMakeUndefined(context);
417
418     gLastMousePositionX = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
419     if (exception && *exception)
420         return JSValueMakeUndefined(context);
421     gLastMousePositionY = static_cast<int>(JSValueToNumber(context, arguments[1], exception));
422     if (exception && *exception)
423         return JSValueMakeUndefined(context);
424
425     MouseEventInfo* eventInfo = new MouseEventInfo(EvasMouseEventMove);
426     feedOrQueueMouseEvent(eventInfo, DoNotFeedQueuedEvents);
427     return JSValueMakeUndefined(context);
428 }
429
430 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
431 {
432     if (argumentCount > 0) {
433         const unsigned long leapForwardDelay = JSValueToNumber(context, arguments[0], exception);
434         if (delayedEventQueue().isEmpty())
435             delayedEventQueue().append(DelayedEvent(0, leapForwardDelay));
436         else
437             delayedEventQueue().last().delay = leapForwardDelay;
438         gTimeOffset += leapForwardDelay;
439     }
440
441     return JSValueMakeUndefined(context);
442 }
443
444 static EvasMouseEvent evasMouseEventFromHorizontalAndVerticalOffsets(int horizontalOffset, int verticalOffset)
445 {
446     unsigned mouseEvent = 0;
447
448     if (verticalOffset > 0)
449         mouseEvent |= EvasMouseEventScrollUp;
450     else if (verticalOffset < 0)
451         mouseEvent |= EvasMouseEventScrollDown;
452
453     if (horizontalOffset > 0)
454         mouseEvent |= EvasMouseEventScrollRight;
455     else if (horizontalOffset < 0)
456         mouseEvent |= EvasMouseEventScrollLeft;
457
458     return static_cast<EvasMouseEvent>(mouseEvent);
459 }
460
461 static JSValueRef mouseScrollByCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
462 {
463     if (argumentCount < 2)
464         return JSValueMakeUndefined(context);
465
466     // We need to invert scrolling values since in EFL negative z value means that
467     // canvas is scrolling down
468     const int horizontal = -(static_cast<int>(JSValueToNumber(context, arguments[0], exception)));
469     if (exception && *exception)
470         return JSValueMakeUndefined(context);
471     const int vertical = -(static_cast<int>(JSValueToNumber(context, arguments[1], exception)));
472     if (exception && *exception)
473         return JSValueMakeUndefined(context);
474
475     MouseEventInfo* eventInfo = new MouseEventInfo(evasMouseEventFromHorizontalAndVerticalOffsets(horizontal, vertical), EvasKeyModifierNone, EvasMouseButtonNone, horizontal, vertical);
476     feedOrQueueMouseEvent(eventInfo, FeedQueuedEvents);
477     return JSValueMakeUndefined(context);
478 }
479
480 static JSValueRef continuousMouseScrollByCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
481 {
482     return JSValueMakeUndefined(context);
483 }
484
485 static KeyEventInfo* keyPadNameFromJSValue(JSStringRef character, unsigned modifiers)
486 {
487     if (equals(character, "leftArrow"))
488         return new KeyEventInfo("KP_Left", modifiers);
489     if (equals(character, "rightArrow"))
490         return new KeyEventInfo("KP_Right", modifiers);
491     if (equals(character, "upArrow"))
492         return new KeyEventInfo("KP_Up", modifiers);
493     if (equals(character, "downArrow"))
494         return new KeyEventInfo("KP_Down", modifiers);
495     if (equals(character, "pageUp"))
496         return new KeyEventInfo("KP_Prior", modifiers);
497     if (equals(character, "pageDown"))
498         return new KeyEventInfo("KP_Next", modifiers);
499     if (equals(character, "home"))
500         return new KeyEventInfo("KP_Home", modifiers);
501     if (equals(character, "end"))
502         return new KeyEventInfo("KP_End", modifiers);
503     if (equals(character, "insert"))
504         return new KeyEventInfo("KP_Insert", modifiers);
505     if (equals(character, "delete"))
506         return new KeyEventInfo("KP_Delete", modifiers);
507
508     return new KeyEventInfo(character->string().utf8(), modifiers, character->string().utf8());
509 }
510
511 static KeyEventInfo* keyNameFromJSValue(JSStringRef character, unsigned modifiers)
512 {
513     if (equals(character, "leftArrow"))
514         return new KeyEventInfo("Left", modifiers);
515     if (equals(character, "rightArrow"))
516         return new KeyEventInfo("Right", modifiers);
517     if (equals(character, "upArrow"))
518         return new KeyEventInfo("Up", modifiers);
519     if (equals(character, "downArrow"))
520         return new KeyEventInfo("Down", modifiers);
521     if (equals(character, "pageUp"))
522         return new KeyEventInfo("Prior", modifiers);
523     if (equals(character, "pageDown"))
524         return new KeyEventInfo("Next", modifiers);
525     if (equals(character, "home"))
526         return new KeyEventInfo("Home", modifiers);
527     if (equals(character, "end"))
528         return new KeyEventInfo("End", modifiers);
529     if (equals(character, "insert"))
530         return new KeyEventInfo("Insert", modifiers);
531     if (equals(character, "delete"))
532         return new KeyEventInfo("Delete", modifiers);
533     if (equals(character, "printScreen"))
534         return new KeyEventInfo("Print", modifiers);
535     if (equals(character, "menu"))
536         return new KeyEventInfo("Menu", modifiers);
537     if (equals(character, "leftControl"))
538         return new KeyEventInfo("Control_L", modifiers);
539     if (equals(character, "rightControl"))
540         return new KeyEventInfo("Control_R", modifiers);
541     if (equals(character, "leftShift"))
542         return new KeyEventInfo("Shift_L", modifiers);
543     if (equals(character, "rightShift"))
544         return new KeyEventInfo("Shift_R", modifiers);
545     if (equals(character, "leftAlt"))
546         return new KeyEventInfo("Alt_L", modifiers);
547     if (equals(character, "rightAlt"))
548         return new KeyEventInfo("Alt_R", modifiers);
549     if (equals(character, "F1"))
550         return new KeyEventInfo("F1", modifiers);
551     if (equals(character, "F2"))
552         return new KeyEventInfo("F2", modifiers);
553     if (equals(character, "F3"))
554         return new KeyEventInfo("F3", modifiers);
555     if (equals(character, "F4"))
556         return new KeyEventInfo("F4", modifiers);
557     if (equals(character, "F5"))
558         return new KeyEventInfo("F5", modifiers);
559     if (equals(character, "F6"))
560         return new KeyEventInfo("F6", modifiers);
561     if (equals(character, "F7"))
562         return new KeyEventInfo("F7", modifiers);
563     if (equals(character, "F8"))
564         return new KeyEventInfo("F8", modifiers);
565     if (equals(character, "F9"))
566         return new KeyEventInfo("F9", modifiers);
567     if (equals(character, "F10"))
568         return new KeyEventInfo("F10", modifiers);
569     if (equals(character, "F11"))
570         return new KeyEventInfo("F11", modifiers);
571     if (equals(character, "F12"))
572         return new KeyEventInfo("F12", modifiers);
573
574     int charCode = JSStringGetCharactersPtr(character)[0];
575     if (charCode == '\n' || charCode == '\r')
576         return new KeyEventInfo("Return", modifiers, "\r");
577     if (charCode == '\t')
578         return new KeyEventInfo("Tab", modifiers, "\t");
579     if (charCode == '\x8')
580         return new KeyEventInfo("BackSpace", modifiers, "\x8");
581     if (charCode == ' ')
582         return new KeyEventInfo("space", modifiers, " ");
583     if (charCode == '\x1B')
584         return new KeyEventInfo("Escape", modifiers, "\x1B");
585
586     if ((character->length() == 1) && (charCode >= 'A' && charCode <= 'Z'))
587         modifiers |= EvasKeyModifierShift;
588
589     return new KeyEventInfo(character->string().utf8(), modifiers, character->string().utf8());
590 }
591
592 static KeyEventInfo* createKeyEventInfo(JSContextRef context, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
593 {
594     if (!ewk_frame_view_get(browser->mainFrame()))
595         return 0;
596
597     if (argumentCount < 1)
598         return 0;
599
600     // handle location argument.
601     int location = DomKeyLocationStandard;
602     if (argumentCount > 2)
603         location = static_cast<int>(JSValueToNumber(context, arguments[2], exception));
604
605     JSRetainPtr<JSStringRef> character(Adopt, JSValueToStringCopy(context, arguments[0], exception));
606     if (exception && *exception)
607         return 0;
608
609     unsigned modifiers = EvasKeyModifierNone;
610     if (argumentCount >= 2)
611         modifiers = modifiersFromJSValue(context, arguments[1]);
612
613     return (location == DomKeyLocationNumpad) ? keyPadNameFromJSValue(character.get(), modifiers) : keyNameFromJSValue(character.get(), modifiers);
614 }
615
616 static void sendKeyDown(Evas* evas, KeyEventInfo* keyEventInfo)
617 {
618     if (!keyEventInfo)
619         return;
620
621     const char* keyName = keyEventInfo->keyName.data();
622     const char* keyString = keyEventInfo->keyString.data();
623     unsigned modifiers = keyEventInfo->modifiers;
624
625     DumpRenderTreeSupportEfl::layoutFrame(browser->mainFrame());
626
627     ASSERT(evas);
628
629     int eventIndex = 0;
630     // Mimic the emacs ctrl-o binding by inserting a paragraph
631     // separator and then putting the cursor back to its original
632     // position. Allows us to pass emacs-ctrl-o.html
633     if ((modifiers & EvasKeyModifierControl) && !strcmp(keyName, "o")) {
634         setEvasModifiers(evas, EvasKeyModifierNone);
635         evas_event_feed_key_down(evas, "Return", "Return", "\r", 0, eventIndex++, 0);
636         evas_event_feed_key_up(evas, "Return", "Return", "\r", 0, eventIndex++, 0);
637
638         modifiers = EvasKeyModifierNone;
639         keyName = "Left";
640         keyString = 0;
641     }
642
643     setEvasModifiers(evas, modifiers);
644     evas_event_feed_key_down(evas, keyName, keyName, keyString, 0, eventIndex++, 0);
645     evas_event_feed_key_up(evas, keyName, keyName, keyString, 0, eventIndex++, 0);
646     setEvasModifiers(evas, EvasKeyModifierNone);
647
648     DumpRenderTreeSupportEfl::deliverAllMutationsIfNecessary();
649 }
650
651 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
652 {
653     OwnPtr<KeyEventInfo> keyEventInfo = adoptPtr(createKeyEventInfo(context, argumentCount, arguments, exception));
654     sendKeyDown(evas_object_evas_get(browser->mainFrame()), keyEventInfo.get());
655
656     return JSValueMakeUndefined(context);
657 }
658
659 static JSValueRef scalePageByCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
660 {
661     if (argumentCount < 3)
662         return JSValueMakeUndefined(context);
663
664     Evas_Object* view = ewk_frame_view_get(browser->mainFrame());
665     if (!view)
666         return JSValueMakeUndefined(context);
667
668     float scaleFactor = JSValueToNumber(context, arguments[0], exception);
669     float x = JSValueToNumber(context, arguments[1], exception);
670     float y = JSValueToNumber(context, arguments[2], exception);
671     ewk_view_scale_set(view, scaleFactor, x, y);
672
673     return JSValueMakeUndefined(context);
674 }
675
676 static void textZoom(ZoomEvent zoomEvent)
677 {
678     Evas_Object* view = ewk_frame_view_get(browser->mainFrame());
679     if (!view)
680         return;
681
682     float zoomFactor = ewk_view_text_zoom_get(view);
683     if (zoomEvent == ZoomIn)
684         zoomFactor *= zoomMultiplierRatio;
685     else
686         zoomFactor /= zoomMultiplierRatio;
687
688     ewk_view_text_zoom_set(view, zoomFactor);
689 }
690
691 static void pageZoom(ZoomEvent zoomEvent)
692 {
693     Evas_Object* view = ewk_frame_view_get(browser->mainFrame());
694     if (!view)
695         return;
696
697     float zoomFactor = ewk_view_page_zoom_get(view);
698     if (zoomEvent == ZoomIn)
699         zoomFactor *= zoomMultiplierRatio;
700     else
701         zoomFactor /= zoomMultiplierRatio;
702
703     ewk_view_page_zoom_set(view, zoomFactor);
704 }
705
706 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
707 {
708     textZoom(ZoomIn);
709     return JSValueMakeUndefined(context);
710 }
711
712 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
713 {
714     textZoom(ZoomOut);
715     return JSValueMakeUndefined(context);
716 }
717
718 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
719 {
720     pageZoom(ZoomIn);
721     return JSValueMakeUndefined(context);
722 }
723
724 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
725 {
726     pageZoom(ZoomOut);
727     return JSValueMakeUndefined(context);
728 }
729
730 static Eina_Bool sendAsynchronousKeyDown(void* userData)
731 {
732     OwnPtr<KeyEventInfo> keyEventInfo = adoptPtr(static_cast<KeyEventInfo*>(userData));
733     sendKeyDown(evas_object_evas_get(browser->mainFrame()), keyEventInfo.get());
734     return ECORE_CALLBACK_CANCEL;
735 }
736
737 static JSValueRef scheduleAsynchronousKeyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
738 {
739     KeyEventInfo* keyEventInfo = createKeyEventInfo(context, argumentCount, arguments, exception);
740     ecore_idler_add(sendAsynchronousKeyDown, static_cast<void*>(keyEventInfo));
741     return JSValueMakeUndefined(context);
742 }
743
744 static void sendTouchEvent(Ewk_Touch_Event_Type type)
745 {
746     Eina_List* eventList = 0;
747
748     for (unsigned i = 0; i < touchPointList().size(); ++i) {
749         Ewk_Touch_Point* event = new Ewk_Touch_Point;
750         WebCore::IntPoint point = touchPointList().at(i).point;
751         event->id = touchPointList().at(i).id;
752         event->x = point.x();
753         event->y = point.y();
754         event->state = touchPointList().at(i).state;
755         eventList = eina_list_append(eventList, event);
756     }
757
758     ewk_frame_feed_touch_event(browser->mainFrame(), type, eventList, touchModifiers);
759
760     void* listData;
761     EINA_LIST_FREE(eventList, listData) {
762         Ewk_Touch_Point* event = static_cast<Ewk_Touch_Point*>(listData);
763         delete event;
764     }
765
766     for (unsigned i = 0; i < touchPointList().size(); ) {
767         if (touchPointList().at(i).state == EWK_TOUCH_POINT_RELEASED)
768             touchPointList().remove(i);
769         else {
770             touchPointList().at(i).state = EWK_TOUCH_POINT_STATIONARY;
771             ++i;
772         }
773     }
774 }
775
776 static JSValueRef addTouchPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
777 {
778     if (argumentCount != 2)
779         return JSValueMakeUndefined(context);
780
781     int x = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
782     ASSERT(!exception || !*exception);
783     int y = static_cast<int>(JSValueToNumber(context, arguments[1], exception));
784     ASSERT(!exception || !*exception);
785
786     const WebCore::IntPoint point(x, y);
787     const unsigned id = touchPointList().isEmpty() ? 0 : touchPointList().last().id + 1;
788     TouchEventInfo eventInfo(id, EWK_TOUCH_POINT_PRESSED, point);
789     touchPointList().append(eventInfo);
790
791     return JSValueMakeUndefined(context);
792 }
793
794 static JSValueRef touchStartCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
795 {
796     sendTouchEvent(EWK_TOUCH_START);
797     return JSValueMakeUndefined(context);
798 }
799
800 static JSValueRef updateTouchPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
801 {
802     if (argumentCount != 3)
803         return JSValueMakeUndefined(context);
804
805     int index = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
806     ASSERT(!exception || !*exception);
807     int x = static_cast<int>(JSValueToNumber(context, arguments[1], exception));
808     ASSERT(!exception || !*exception);
809     int y = static_cast<int>(JSValueToNumber(context, arguments[2], exception));
810     ASSERT(!exception || !*exception);
811
812     if (index < 0 || index >= touchPointList().size())
813         return JSValueMakeUndefined(context);
814
815     WebCore::IntPoint& point = touchPointList().at(index).point;
816     point.setX(x);
817     point.setY(y);
818     touchPointList().at(index).state = EWK_TOUCH_POINT_MOVED;
819
820     return JSValueMakeUndefined(context);
821 }
822
823 static JSValueRef touchMoveCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
824 {
825     sendTouchEvent(EWK_TOUCH_MOVE);
826     return JSValueMakeUndefined(context);
827 }
828
829 static JSValueRef cancelTouchPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
830 {
831     if (argumentCount != 1)
832         return JSValueMakeUndefined(context);
833
834     int index = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
835     ASSERT(!exception || !*exception);
836     if (index < 0 || index >= touchPointList().size())
837         return JSValueMakeUndefined(context);
838
839     touchPointList().at(index).state = EWK_TOUCH_POINT_CANCELLED;
840     return JSValueMakeUndefined(context);
841 }
842
843 static JSValueRef touchCancelCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
844 {
845     sendTouchEvent(EWK_TOUCH_CANCEL);
846     return JSValueMakeUndefined(context);
847 }
848
849 static JSValueRef releaseTouchPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
850 {
851     if (argumentCount != 1)
852         return JSValueMakeUndefined(context);
853
854     int index = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
855     ASSERT(!exception || !*exception);
856     if (index < 0 || index >= touchPointList().size())
857         return JSValueMakeUndefined(context);
858
859     touchPointList().at(index).state = EWK_TOUCH_POINT_RELEASED;
860     return JSValueMakeUndefined(context);
861 }
862
863 static JSValueRef touchEndCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
864 {
865     sendTouchEvent(EWK_TOUCH_END);
866     touchModifiers = 0;
867     return JSValueMakeUndefined(context);
868 }
869
870 static JSValueRef clearTouchPointsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
871 {
872     touchPointList().clear();
873     return JSValueMakeUndefined(context);
874 }
875
876 static JSValueRef setTouchModifierCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
877 {
878     if (argumentCount != 2)
879         return JSValueMakeUndefined(context);
880
881     JSRetainPtr<JSStringRef> jsModifier(Adopt, JSValueToStringCopy(context, arguments[0], exception));
882     unsigned mask = 0;
883
884     if (equals(jsModifier, "alt"))
885         mask |= ECORE_EVENT_MODIFIER_ALT;
886     else if (equals(jsModifier, "ctrl"))
887         mask |= ECORE_EVENT_MODIFIER_CTRL;
888     else if (equals(jsModifier, "meta"))
889         mask |= ECORE_EVENT_MODIFIER_WIN;
890     else if (equals(jsModifier, "shift"))
891         mask |= ECORE_EVENT_MODIFIER_SHIFT;
892
893     if (JSValueToBoolean(context, arguments[1]))
894         touchModifiers |= mask;
895     else
896         touchModifiers &= ~mask;
897
898     return JSValueMakeUndefined(context);
899 }
900
901 static JSStaticFunction staticFunctions[] = {
902     { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
903     { "mouseScrollBy", mouseScrollByCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
904     { "continuousMouseScrollBy", continuousMouseScrollByCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
905     { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
906     { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
907     { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
908     { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
909     { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
910     { "scheduleAsynchronousClick", scheduleAsynchronousClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
911     { "scheduleAsynchronousKeyDown", scheduleAsynchronousKeyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
912     { "scalePageBy", scalePageByCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
913     { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
914     { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
915     { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
916     { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
917     { "addTouchPoint", addTouchPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
918     { "touchStart", touchStartCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
919     { "updateTouchPoint", updateTouchPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
920     { "touchMove", touchMoveCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
921     { "releaseTouchPoint", releaseTouchPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
922     { "touchEnd", touchEndCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
923     { "cancelTouchPoint", cancelTouchPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
924     { "touchCancel", touchCancelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
925     { "clearTouchPoints", clearTouchPointsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
926     { "setTouchModifier", setTouchModifierCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
927     { 0, 0, 0 }
928 };
929
930 static JSStaticValue staticValues[] = {
931     { 0, 0, 0, 0 }
932 };
933
934 static JSClassRef getClass(JSContextRef context)
935 {
936     static JSClassRef eventSenderClass = 0;
937
938     if (!eventSenderClass) {
939         JSClassDefinition classDefinition = {
940                 0, 0, 0, 0, 0, 0,
941                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
942         classDefinition.staticFunctions = staticFunctions;
943         classDefinition.staticValues = staticValues;
944
945         eventSenderClass = JSClassCreate(&classDefinition);
946     }
947
948     return eventSenderClass;
949 }
950
951 JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
952 {
953     if (isTopFrame) {
954         gDragMode = true;
955
956         // Fly forward in time one second when the main frame loads. This will
957         // ensure that when a test begins clicking in the same location as
958         // a previous test, those clicks won't be interpreted as continuations
959         // of the previous test's click sequences.
960         gTimeOffset += 1000;
961
962         gLastMousePositionX = gLastMousePositionY = 0;
963         gLastClickPositionX = gLastClickPositionY = 0;
964         gLastClickTimeOffset = 0;
965         gLastClickButton = 0;
966         gButtonCurrentlyDown = 0;
967         gClickCount = 0;
968     }
969
970     return JSObjectMake(context, getClass(context), 0);
971 }
972
973 static void feedOrQueueMouseEvent(MouseEventInfo* eventInfo, EventQueueStrategy strategy)
974 {
975     if (!delayedEventQueue().isEmpty()) {
976         if (delayedEventQueue().last().eventInfo)
977             delayedEventQueue().append(DelayedEvent(eventInfo));
978         else
979             delayedEventQueue().last().eventInfo = eventInfo;
980
981         if (strategy == FeedQueuedEvents)
982             feedQueuedMouseEvents();
983     } else
984         feedMouseEvent(eventInfo);
985 }
986
987 static void feedMouseEvent(MouseEventInfo* eventInfo)
988 {
989     if (!eventInfo)
990         return;
991
992     unsigned timeStamp = 0;
993     Evas_Object* mainFrame = browser->mainFrame();
994     Evas* evas = evas_object_evas_get(mainFrame);
995     EvasMouseEvent event = eventInfo->event;
996
997     setEvasModifiers(evas, eventInfo->modifiers);
998
999     Evas_Button_Flags flags = EVAS_BUTTON_NONE;
1000
1001     // FIXME: We need to pass additional information with our events, so that
1002     // we could construct correct PlatformWheelEvent. At the moment, max number
1003     // of clicks is 3
1004     if (gClickCount == 3)
1005         flags = EVAS_BUTTON_TRIPLE_CLICK;
1006     else if (gClickCount == 2)
1007         flags = EVAS_BUTTON_DOUBLE_CLICK;
1008
1009     if (event & EvasMouseEventMove)
1010         evas_event_feed_mouse_move(evas, gLastMousePositionX, gLastMousePositionY, timeStamp++, 0);
1011     if (event & EvasMouseEventDown)
1012         evas_event_feed_mouse_down(evas, eventInfo->button, flags, timeStamp++, 0);
1013     if (event & EvasMouseEventUp)
1014         evas_event_feed_mouse_up(evas, eventInfo->button, flags, timeStamp++, 0);
1015     if (event & EvasMouseEventScrollLeft | event & EvasMouseEventScrollRight)
1016         evas_event_feed_mouse_wheel(evas, 1, eventInfo->horizontalDelta, timeStamp, 0);
1017     if (event & EvasMouseEventScrollUp | event & EvasMouseEventScrollDown)
1018         evas_event_feed_mouse_wheel(evas, 0, eventInfo->verticalDelta, timeStamp, 0);
1019
1020     setEvasModifiers(evas, EvasKeyModifierNone);
1021
1022     delete eventInfo;
1023 }
1024
1025 static void feedQueuedMouseEvents()
1026 {
1027     WTF::Vector<DelayedEvent>::const_iterator it = delayedEventQueue().begin();
1028     for (; it != delayedEventQueue().end(); it++) {
1029         DelayedEvent delayedEvent = *it;
1030         if (delayedEvent.delay)
1031             usleep(delayedEvent.delay * 1000);
1032         feedMouseEvent(delayedEvent.eventInfo);
1033     }
1034     delayedEventQueue().clear();
1035 }