[WK2] [GTK] Implement a MouseDown/MouseUp/MouseMoveTo/MouseScrollBy/LeapForward funct...
[WebKit-https.git] / Tools / WebKitTestRunner / gtk / EventSenderProxyGtk.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) 2010 Motorola Mobility, Inc.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1.  Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  * 2.  Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
18  *     its contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "EventSenderProxy.h"
35
36 #include "PlatformWebView.h"
37 #include "TestController.h"
38 #include <GOwnPtr.h>
39 #include <GtkVersioning.h>
40 #include <wtf/OwnArrayPtr.h>
41 #include <wtf/PassOwnArrayPtr.h>
42 #include <gdk/gdkkeysyms.h>
43 #include <gtk/gtk.h>
44 #include <wtf/text/WTFString.h>
45
46 namespace WTR {
47
48 // Key event location code defined in DOM Level 3.
49 enum KeyLocationCode {
50     DOMKeyLocationStandard      = 0x00,
51     DOMKeyLocationLeft          = 0x01,
52     DOMKeyLocationRight         = 0x02,
53     DOMKeyLocationNumpad        = 0x03
54 };
55
56
57 struct WTREventQueueItem {
58     GdkEvent* event;
59     gulong delay;
60
61     WTREventQueueItem()
62         : event(0)
63         , delay(0)
64     {
65     }
66     WTREventQueueItem(GdkEvent* event, gulong delay)
67         : event(event)
68         , delay(delay)
69     {
70     }
71 };
72
73 EventSenderProxy::EventSenderProxy(TestController* testController)
74     : m_testController(testController)
75     , m_time(0)
76     , m_leftMouseButtonDown(false)
77     , m_clickCount(0)
78     , m_clickTime(0)
79     , m_clickButton(kWKEventMouseButtonNoButton)
80     , m_mouseButtonCurrentlyDown(0)
81 {
82 }
83
84 static guint getMouseButtonModifiers(int gdkButton)
85 {
86     if (gdkButton == 1)
87         return GDK_BUTTON1_MASK;
88     if (gdkButton == 2)
89         return GDK_BUTTON2_MASK;
90     if (gdkButton == 3)
91         return GDK_BUTTON3_MASK;
92     return 0;
93 }
94
95 static unsigned eventSenderButtonToGDKButton(unsigned button)
96 {
97     int mouseButton = 3;
98     if (button <= 2)
99         mouseButton = button + 1;
100     // fast/events/mouse-click-events expects the 4th button to be treated as the middle button.
101     else if (button == 3)
102         mouseButton = 2;
103
104     return mouseButton;
105 }
106
107 GdkEvent* EventSenderProxy::createMouseButtonEvent(GdkEventType eventType, unsigned button, WKEventModifiers modifiers)
108 {
109     GdkEvent* mouseEvent = gdk_event_new(eventType);
110
111     mouseEvent->button.button = eventSenderButtonToGDKButton(button);
112     mouseEvent->button.x = m_position.x;
113     mouseEvent->button.y = m_position.y;
114     mouseEvent->button.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
115     g_object_ref(mouseEvent->button.window);
116     mouseEvent->button.device = getDefaultGDKPointerDevice(mouseEvent->button.window);
117     mouseEvent->button.state = modifiers | getMouseButtonModifiers(mouseEvent->button.button);
118     mouseEvent->button.time = GDK_CURRENT_TIME;
119     mouseEvent->button.axes = 0;
120
121     int xRoot, yRoot;
122     gdk_window_get_root_coords(mouseEvent->button.window, m_position.x, m_position.y, &xRoot, &yRoot);
123     mouseEvent->button.x_root = xRoot;
124     mouseEvent->button.y_root = yRoot;
125
126     return mouseEvent;
127 }
128
129 void EventSenderProxy::updateClickCountForButton(int button)
130 {
131     if (m_time - m_clickTime < 1 && m_position == m_clickPosition && button == m_clickButton) {
132         ++m_clickCount;
133         m_clickTime = m_time;
134         return;
135     }
136
137     m_clickCount = 1;
138     m_clickTime = m_time;
139     m_clickPosition = m_position;
140     m_clickButton = button;
141 }
142
143 static void dispatchEvent(GdkEvent* event)
144 {
145     gtk_main_do_event(event);
146     gdk_event_free(event);
147 }
148
149 void EventSenderProxy::replaySavedEvents()
150 {
151     while (!m_eventQueue.isEmpty()) {
152         WTREventQueueItem item = m_eventQueue.first();
153         if (item.delay)
154             g_usleep(item.delay * 1000);
155
156         dispatchEvent(item.event);
157         m_eventQueue.remove(0);
158     }
159 }
160
161 void EventSenderProxy::sendOrQueueEvent(GdkEvent* event)
162 {
163     if (m_eventQueue.isEmpty() || !m_eventQueue.last().delay) {
164         dispatchEvent(event);
165         return;
166     }
167
168     m_eventQueue.last().event = event;
169     replaySavedEvents();
170 }
171
172 static guint webkitModifiersToGDKModifiers(WKEventModifiers wkModifiers)
173 {
174     guint modifiers = 0;
175
176     if (wkModifiers & kWKEventModifiersControlKey)
177         modifiers |= GDK_CONTROL_MASK;
178     if (wkModifiers & kWKEventModifiersShiftKey)
179         modifiers |= GDK_SHIFT_MASK;
180     if (wkModifiers & kWKEventModifiersAltKey)
181         modifiers |= GDK_MOD1_MASK;
182     if (wkModifiers & kWKEventModifiersMetaKey)
183         modifiers |= GDK_META_MASK;
184
185     return modifiers;
186 }
187
188 int getGDKKeySymForKeyRef(WKStringRef keyRef, unsigned location, guint* modifiers)
189 {
190     if (location == DOMKeyLocationNumpad) {
191         if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow"))
192             return GDK_KEY_KP_Left;
193         if (WKStringIsEqualToUTF8CString(keyRef, "rightArror"))
194             return GDK_KEY_KP_Right;
195         if (WKStringIsEqualToUTF8CString(keyRef, "upArrow"))
196             return GDK_KEY_KP_Up;
197         if (WKStringIsEqualToUTF8CString(keyRef, "downArrow"))
198             return GDK_KEY_KP_Down;
199         if (WKStringIsEqualToUTF8CString(keyRef, "pageUp"))
200             return GDK_KEY_KP_Page_Up;
201         if (WKStringIsEqualToUTF8CString(keyRef, "pageDown"))
202             return GDK_KEY_KP_Page_Down;
203         if (WKStringIsEqualToUTF8CString(keyRef, "home"))
204             return GDK_KEY_KP_Home;
205         if (WKStringIsEqualToUTF8CString(keyRef, "end"))
206             return GDK_KEY_KP_End;
207         if (WKStringIsEqualToUTF8CString(keyRef, "insert"))
208             return GDK_KEY_KP_Insert;
209         if (WKStringIsEqualToUTF8CString(keyRef, "delete"))
210             return GDK_KEY_KP_Delete;
211
212         return GDK_KEY_VoidSymbol;
213     }
214
215     if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow"))
216         return GDK_KEY_Left;
217     if (WKStringIsEqualToUTF8CString(keyRef, "rightArrow"))
218         return GDK_KEY_Right;
219     if (WKStringIsEqualToUTF8CString(keyRef, "upArrow"))
220         return GDK_KEY_Up;
221     if (WKStringIsEqualToUTF8CString(keyRef, "downArrow"))
222         return GDK_KEY_Down;
223     if (WKStringIsEqualToUTF8CString(keyRef, "pageUp"))
224         return GDK_KEY_Page_Up;
225     if (WKStringIsEqualToUTF8CString(keyRef, "pageDown"))
226         return GDK_KEY_Page_Down;
227     if (WKStringIsEqualToUTF8CString(keyRef, "home"))
228         return GDK_KEY_Home;
229     if (WKStringIsEqualToUTF8CString(keyRef, "end"))
230         return GDK_KEY_End;
231     if (WKStringIsEqualToUTF8CString(keyRef, "insert"))
232         return GDK_KEY_Insert;
233     if (WKStringIsEqualToUTF8CString(keyRef, "delete"))
234         return GDK_KEY_Delete;
235     if (WKStringIsEqualToUTF8CString(keyRef, "printScreen"))
236         return GDK_KEY_Print;
237     if (WKStringIsEqualToUTF8CString(keyRef, "menu"))
238         return GDK_KEY_Menu;
239     if (WKStringIsEqualToUTF8CString(keyRef, "F1"))
240         return GDK_KEY_F1;
241     if (WKStringIsEqualToUTF8CString(keyRef, "F2"))
242         return GDK_KEY_F2;
243     if (WKStringIsEqualToUTF8CString(keyRef, "F3"))
244         return GDK_KEY_F3;
245     if (WKStringIsEqualToUTF8CString(keyRef, "F4"))
246         return GDK_KEY_F4;
247     if (WKStringIsEqualToUTF8CString(keyRef, "F5"))
248         return GDK_KEY_F5;
249     if (WKStringIsEqualToUTF8CString(keyRef, "F6"))
250         return GDK_KEY_F6;
251     if (WKStringIsEqualToUTF8CString(keyRef, "F7"))
252         return GDK_KEY_F7;
253     if (WKStringIsEqualToUTF8CString(keyRef, "F8"))
254         return GDK_KEY_F8;
255     if (WKStringIsEqualToUTF8CString(keyRef, "F9"))
256         return GDK_KEY_F9;
257     if (WKStringIsEqualToUTF8CString(keyRef, "F10"))
258         return GDK_KEY_F10;
259     if (WKStringIsEqualToUTF8CString(keyRef, "F11"))
260         return GDK_KEY_F11;
261     if (WKStringIsEqualToUTF8CString(keyRef, "F12"))
262         return GDK_KEY_F12;
263
264     size_t bufferSize = WKStringGetMaximumUTF8CStringSize(keyRef);
265     OwnArrayPtr<char> buffer = adoptArrayPtr(new char[bufferSize]);
266     WKStringGetUTF8CString(keyRef, buffer.get(), bufferSize);
267     char charCode = buffer.get()[0];
268
269     if (charCode == '\n' || charCode == '\r')
270         return GDK_KEY_Return;
271     if (charCode == '\t')
272         return GDK_KEY_Tab;
273     if (charCode == '\x8')
274         return GDK_KEY_BackSpace;
275
276     if (WTF::isASCIIUpper(charCode))
277         *modifiers |= GDK_SHIFT_MASK;
278
279     return gdk_unicode_to_keyval(static_cast<guint32>(buffer.get()[0]));
280 }
281
282 void EventSenderProxy::keyDown(WKStringRef keyRef, WKEventModifiers wkModifiers, unsigned location)
283 {
284     guint modifiers = webkitModifiersToGDKModifiers(wkModifiers);
285     int gdkKeySym = getGDKKeySymForKeyRef(keyRef, location, &modifiers);
286
287     GdkEvent* pressEvent = gdk_event_new(GDK_KEY_PRESS);
288     pressEvent->key.keyval = gdkKeySym;
289     pressEvent->key.state = modifiers;
290     pressEvent->key.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformWindow()));
291     g_object_ref(pressEvent->key.window);
292     gdk_event_set_device(pressEvent, getDefaultGDKPointerDevice(pressEvent->key.window));
293
294     GOwnPtr<GdkKeymapKey> keys;
295     gint nKeys;
296     if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys.outPtr(), &nKeys))
297         pressEvent->key.hardware_keycode = keys.get()[0].keycode;
298
299     GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
300     dispatchEvent(pressEvent);
301     releaseEvent->key.type = GDK_KEY_RELEASE;
302     dispatchEvent(releaseEvent);
303 }
304
305 void EventSenderProxy::mouseDown(unsigned button, WKEventModifiers wkModifiers)
306 {
307     // If the same mouse button is already in the down position don't
308     // send another event as it may confuse Xvfb.
309     unsigned gdkButton = eventSenderButtonToGDKButton(button);
310     if (m_mouseButtonCurrentlyDown == gdkButton)
311         return;
312
313     m_mouseButtonCurrentlyDown = gdkButton;
314
315     // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for
316     // the second button press during double-clicks. WebKit GTK+ selectively
317     // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek.
318     // Since our events aren't ever going onto the GDK event queue, WebKit won't
319     // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send
320     // it here. Eventually this code should probably figure out a way to get all
321     // appropriate events onto the event queue and this work-around should be
322     // removed.
323     updateClickCountForButton(button);
324
325     GdkEventType eventType;
326     if (m_clickCount == 2)
327         eventType = GDK_2BUTTON_PRESS;
328     else if (m_clickCount == 3)
329         eventType = GDK_3BUTTON_PRESS;
330     else
331         eventType = GDK_BUTTON_PRESS;
332
333     GdkEvent* event = createMouseButtonEvent(eventType, button, wkModifiers);
334     sendOrQueueEvent(event);
335 }
336
337 void EventSenderProxy::mouseUp(unsigned button, WKEventModifiers wkModifiers)
338 {
339     m_clickButton = kWKEventMouseButtonNoButton;
340     GdkEvent* event = createMouseButtonEvent(GDK_BUTTON_RELEASE, button, wkModifiers);
341     sendOrQueueEvent(event);
342
343     if (m_mouseButtonCurrentlyDown == event->button.button)
344         m_mouseButtonCurrentlyDown = 0;
345     m_clickPosition = m_position;
346     m_clickTime = GDK_CURRENT_TIME;
347 }
348
349 void EventSenderProxy::mouseMoveTo(double x, double y)
350 {
351     m_position.x = x;
352     m_position.y = y;
353
354     GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
355     event->motion.x = m_position.x;
356     event->motion.y = m_position.y;
357
358     event->motion.time = GDK_CURRENT_TIME;
359     event->motion.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
360     g_object_ref(event->motion.window);
361     event->button.device = getDefaultGDKPointerDevice(event->motion.window);
362     event->motion.state = 0 | getMouseButtonModifiers(m_mouseButtonCurrentlyDown);
363     event->motion.axes = 0;
364
365     int xRoot, yRoot;
366     gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())), m_position.x, m_position.y , &xRoot, &yRoot);
367     event->motion.x_root = xRoot;
368     event->motion.y_root = yRoot;
369
370     sendOrQueueEvent(event);
371 }
372
373 void EventSenderProxy::mouseScrollBy(int horizontal, int vertical)
374 {
375     GdkEvent* event = gdk_event_new(GDK_SCROLL);
376     event->scroll.x = m_position.x;
377     event->scroll.y = m_position.y;
378     event->scroll.time = GDK_CURRENT_TIME;
379     event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformWindow()));
380     g_object_ref(event->scroll.window);
381
382     if (horizontal < 0)
383         event->scroll.direction = GDK_SCROLL_RIGHT;
384     else if (horizontal > 0)
385         event->scroll.direction = GDK_SCROLL_LEFT;
386     else if (vertical < 0)
387         event->scroll.direction = GDK_SCROLL_DOWN;
388     else if (vertical > 0)
389         event->scroll.direction = GDK_SCROLL_UP;
390     else
391         g_assert_not_reached();
392
393     sendOrQueueEvent(event);
394 }
395
396 void EventSenderProxy::leapForward(int milliseconds)
397 {
398     if (m_eventQueue.isEmpty())
399         m_eventQueue.append(WTREventQueueItem());
400
401     m_eventQueue.last().delay = milliseconds;
402     m_time += milliseconds / 1000.0;
403 }
404
405 } // namespace WTR