[Qt][WK2] Convert touch-point area
[WebKit-https.git] / Tools / WebKitTestRunner / qt / EventSenderProxyQt.cpp
1 /*
2  * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "EventSenderProxy.h"
28
29 #include "PlatformWebView.h"
30 #include "TestController.h"
31 #include <QGraphicsSceneMouseEvent>
32 #include <QKeyEvent>
33 #include <QtTest/QtTest>
34 #include <WebKit2/WKPagePrivate.h>
35 #include <WebKit2/WKStringQt.h>
36
37 namespace WTR {
38
39 #define KEYCODE_DEL         127
40 #define KEYCODE_BACKSPACE   8
41 #define KEYCODE_LEFTARROW   0xf702
42 #define KEYCODE_RIGHTARROW  0xf703
43 #define KEYCODE_UPARROW     0xf700
44 #define KEYCODE_DOWNARROW   0xf701
45
46 struct WTREventQueue {
47     QEvent* m_event;
48     int m_delay;
49 };
50
51 static WTREventQueue eventQueue[1024];
52 static unsigned endOfQueue;
53 static bool isReplayingEvents;
54
55 EventSenderProxy::EventSenderProxy(TestController* testController)
56     : m_testController(testController)
57     , m_time(0)
58     , m_position()
59     , m_leftMouseButtonDown(false)
60     , m_clickCount(0)
61     , m_clickTime(0)
62     , m_clickPosition()
63     , m_clickButton(kWKEventMouseButtonNoButton)
64     , m_mouseButtons(0)
65 #if ENABLE(TOUCH_EVENTS)
66     , m_touchActive(false)
67 #endif
68 {
69     memset(eventQueue, 0, sizeof(eventQueue));
70     endOfQueue = 0;
71     isReplayingEvents = false;
72 }
73
74 static Qt::MouseButton getMouseButton(unsigned button)
75 {
76     Qt::MouseButton mouseButton;
77     switch (button) {
78     case 0:
79         mouseButton = Qt::LeftButton;
80         break;
81     case 1:
82         mouseButton = Qt::MidButton;
83         break;
84     case 2:
85         mouseButton = Qt::RightButton;
86         break;
87     case 3:
88         // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
89         mouseButton = Qt::MidButton;
90         break;
91     default:
92         mouseButton = Qt::LeftButton;
93         break;
94     }
95     return mouseButton;
96 }
97
98 static Qt::KeyboardModifiers getModifiers(WKEventModifiers modifiersRef)
99 {
100     Qt::KeyboardModifiers modifiers = 0;
101
102     if (modifiersRef & kWKEventModifiersControlKey)
103         modifiers |= Qt::ControlModifier;
104     if (modifiersRef & kWKEventModifiersShiftKey)
105         modifiers |= Qt::ShiftModifier;
106     if (modifiersRef & kWKEventModifiersAltKey)
107         modifiers |= Qt::AltModifier;
108     if (modifiersRef & kWKEventModifiersMetaKey)
109         modifiers |= Qt::MetaModifier;
110
111     return modifiers;
112 }
113
114 void EventSenderProxy::keyDown(WKStringRef keyRef, WKEventModifiers modifiersRef, unsigned location)
115 {
116     const QString key = WKStringCopyQString(keyRef);
117     QString keyText = key;
118
119     Qt::KeyboardModifiers modifiers = getModifiers(modifiersRef);
120
121     if (location == 3)
122         modifiers |= Qt::KeypadModifier;
123     int code = 0;
124     if (key.length() == 1) {
125         code = key.unicode()->unicode();
126         // map special keycodes used by the tests to something that works for Qt/X11
127         if (code == '\r') {
128             code = Qt::Key_Return;
129         } else if (code == '\t') {
130             code = Qt::Key_Tab;
131             if (modifiers == Qt::ShiftModifier)
132                 code = Qt::Key_Backtab;
133             keyText = QString();
134         } else if (code == KEYCODE_DEL || code == KEYCODE_BACKSPACE) {
135             code = Qt::Key_Backspace;
136             if (modifiers == Qt::AltModifier)
137                 modifiers = Qt::ControlModifier;
138             keyText = QString();
139         } else if (code == 'o' && modifiers == Qt::ControlModifier) {
140             // Mimic the emacs ctrl-o binding on Mac by inserting a paragraph
141             // separator and then putting the cursor back to its original
142             // position. Allows us to pass emacs-ctrl-o.html
143             keyText = QLatin1String("\n");
144             code = '\n';
145             modifiers = 0;
146             QKeyEvent event(QEvent::KeyPress, code, modifiers, keyText);
147             m_testController->mainWebView()->sendEvent(&event);
148             QKeyEvent event2(QEvent::KeyRelease, code, modifiers, keyText);
149             m_testController->mainWebView()->sendEvent(&event2);
150             keyText = QString();
151             code = Qt::Key_Left;
152         } else if (code == 'y' && modifiers == Qt::ControlModifier) {
153             keyText = QLatin1String("c");
154             code = 'c';
155         } else if (code == 'k' && modifiers == Qt::ControlModifier) {
156             keyText = QLatin1String("x");
157             code = 'x';
158         } else if (code == 'a' && modifiers == Qt::ControlModifier) {
159             keyText = QString();
160             code = Qt::Key_Home;
161             modifiers = 0;
162         } else if (code == KEYCODE_LEFTARROW) {
163             keyText = QString();
164             code = Qt::Key_Left;
165             if (modifiers & Qt::MetaModifier) {
166                 code = Qt::Key_Home;
167                 modifiers &= ~Qt::MetaModifier;
168             }
169         } else if (code == KEYCODE_RIGHTARROW) {
170             keyText = QString();
171             code = Qt::Key_Right;
172             if (modifiers & Qt::MetaModifier) {
173                 code = Qt::Key_End;
174                 modifiers &= ~Qt::MetaModifier;
175             }
176         } else if (code == KEYCODE_UPARROW) {
177             keyText = QString();
178             code = Qt::Key_Up;
179             if (modifiers & Qt::MetaModifier) {
180                 code = Qt::Key_PageUp;
181                 modifiers &= ~Qt::MetaModifier;
182             }
183         } else if (code == KEYCODE_DOWNARROW) {
184             keyText = QString();
185             code = Qt::Key_Down;
186             if (modifiers & Qt::MetaModifier) {
187                 code = Qt::Key_PageDown;
188                 modifiers &= ~Qt::MetaModifier;
189             }
190         } else if (code == 'a' && modifiers == Qt::ControlModifier) {
191             keyText = QString();
192             code = Qt::Key_Home;
193             modifiers = 0;
194         } else
195             code = key.unicode()->toUpper().unicode();
196     } else {
197         if (key.startsWith(QLatin1Char('F')) && key.count() <= 3) {
198             keyText = keyText.mid(1);
199             int functionKey = keyText.toInt();
200             Q_ASSERT(functionKey >= 1 && functionKey <= 35);
201             code = Qt::Key_F1 + (functionKey - 1);
202         // map special keycode strings used by the tests to something that works for Qt/X11
203         } else if (key == QLatin1String("leftArrow")) {
204             keyText = QString();
205             code = Qt::Key_Left;
206         } else if (key == QLatin1String("rightArrow")) {
207             keyText = QString();
208             code = Qt::Key_Right;
209         } else if (key == QLatin1String("upArrow")) {
210             keyText = QString();
211             code = Qt::Key_Up;
212         } else if (key == QLatin1String("downArrow")) {
213             keyText = QString();
214             code = Qt::Key_Down;
215         } else if (key == QLatin1String("pageUp")) {
216             keyText = QString();
217             code = Qt::Key_PageUp;
218         } else if (key == QLatin1String("pageDown")) {
219             keyText = QString();
220             code = Qt::Key_PageDown;
221         } else if (key == QLatin1String("home")) {
222             keyText = QString();
223             code = Qt::Key_Home;
224         } else if (key == QLatin1String("end")) {
225             keyText = QString();
226             code = Qt::Key_End;
227         } else if (key == QLatin1String("insert")) {
228             keyText = QString();
229             code = Qt::Key_Insert;
230         } else if (key == QLatin1String("delete")) {
231             keyText = QString();
232             code = Qt::Key_Delete;
233         } else if (key == QLatin1String("printScreen")) {
234             keyText = QString();
235             code = Qt::Key_Print;
236         } else if (key == QLatin1String("menu")) {
237             keyText = QString();
238             code = Qt::Key_Menu;
239         }
240     }
241     QKeyEvent event(QEvent::KeyPress, code, modifiers, keyText);
242     m_testController->mainWebView()->sendEvent(&event);
243     QKeyEvent event2(QEvent::KeyRelease, code, modifiers, keyText);
244     m_testController->mainWebView()->sendEvent(&event2);
245 }
246
247 void EventSenderProxy::updateClickCountForButton(int button)
248 {
249     if (m_time - m_clickTime < QApplication::doubleClickInterval() && m_position == m_clickPosition && button == m_clickButton) {
250         ++m_clickCount;
251         m_clickTime = m_time;
252         return;
253     }
254
255     m_clickCount = 1;
256     m_clickTime = m_time;
257     m_clickPosition = m_position;
258     m_clickButton = button;
259 }
260
261 void EventSenderProxy::mouseDown(unsigned button, WKEventModifiers wkModifiers)
262 {
263     Qt::KeyboardModifiers modifiers = getModifiers(wkModifiers);
264     Qt::MouseButton mouseButton = getMouseButton(button);
265
266     updateClickCountForButton(button);
267
268     m_mouseButtons |= mouseButton;
269
270     QPoint mousePos(m_position.x, m_position.y);
271     QMouseEvent* event = new QMouseEvent((m_clickCount == 2) ? QEvent::MouseButtonDblClick : QEvent::MouseButtonPress,
272         mousePos, mousePos, mouseButton, m_mouseButtons, modifiers);
273
274     sendOrQueueEvent(event);
275 }
276
277 void EventSenderProxy::mouseUp(unsigned button, WKEventModifiers)
278 {
279     Qt::MouseButton mouseButton = getMouseButton(button);
280     m_mouseButtons &= ~mouseButton;
281
282     QPoint mousePos(m_position.x, m_position.y);
283     QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonRelease,
284         mousePos, mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
285
286     sendOrQueueEvent(event);
287 }
288
289 void EventSenderProxy::mouseMoveTo(double x, double y)
290 {
291     m_position.x = x;
292     m_position.y = y;
293
294     QPoint mousePos(m_position.x, m_position.y);
295     QMouseEvent* event = new QMouseEvent(QEvent::MouseMove,
296         mousePos, mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier);
297
298     sendOrQueueEvent(event);
299 }
300
301 void EventSenderProxy::mouseScrollBy(int, int)
302 {
303     // FIXME: Implement this.
304 }
305
306 void EventSenderProxy::leapForward(int ms)
307 {
308     eventQueue[endOfQueue].m_delay = ms;
309     m_time += ms;
310 }
311
312 #if ENABLE(TOUCH_EVENTS)
313 void EventSenderProxy::addTouchPoint(int x, int y)
314 {
315     const int id = m_touchPoints.isEmpty() ? 0 : m_touchPoints.last().id() + 1;
316     const QPointF pos(x, y);
317     QTouchEvent::TouchPoint point(id);
318     point.setPos(pos);
319     point.setStartPos(pos);
320     point.setState(Qt::TouchPointPressed);
321     if (!m_touchPointRadius.isNull())
322         point.setRect(QRectF(pos - m_touchPointRadius, pos + m_touchPointRadius));
323     m_touchPoints.append(point);
324 }
325
326 void EventSenderProxy::updateTouchPoint(int index, int x, int y)
327 {
328     ASSERT(index >= 0 && index < m_touchPoints.count());
329     QPointF pos(x, y);
330     QTouchEvent::TouchPoint &p = m_touchPoints[index];
331     p.setPos(pos);
332     p.setState(Qt::TouchPointMoved);
333     if (!m_touchPointRadius.isNull())
334         p.setRect(QRectF(pos - m_touchPointRadius, pos + m_touchPointRadius));
335 }
336
337 void EventSenderProxy::setTouchModifier(WKEventModifiers modifier, bool enable)
338 {
339     Qt::KeyboardModifiers mod = getModifiers(modifier);
340
341     if (enable)
342         m_touchModifiers |= mod;
343     else
344         m_touchModifiers &= ~mod;
345 }
346
347 void EventSenderProxy::setTouchPointRadius(int radiusX, int radiusY)
348 {
349     m_touchPointRadius = QPoint(radiusX, radiusY);
350 }
351
352 void EventSenderProxy::touchStart()
353 {
354     if (!m_touchActive) {
355         sendTouchEvent(QEvent::TouchBegin);
356         m_touchActive = true;
357     } else
358         sendTouchEvent(QEvent::TouchUpdate);
359 }
360
361 void EventSenderProxy::touchMove()
362 {
363     sendTouchEvent(QEvent::TouchUpdate);
364 }
365
366 void EventSenderProxy::touchEnd()
367 {
368     for (int i = 0; i < m_touchPoints.count(); ++i) {
369         if (m_touchPoints[i].state() != Qt::TouchPointReleased) {
370             sendTouchEvent(QEvent::TouchUpdate);
371             return;
372         }
373     }
374     sendTouchEvent(QEvent::TouchEnd);
375     m_touchActive = false;
376 }
377
378 void EventSenderProxy::touchCancel()
379 {
380     sendTouchEvent(QEvent::TouchCancel);
381     m_touchActive = false;
382 }
383
384 void EventSenderProxy::clearTouchPoints()
385 {
386     m_touchPoints.clear();
387     m_touchModifiers = Qt::KeyboardModifiers();
388     m_touchActive = false;
389     m_touchPointRadius = QPoint();
390 }
391
392 void EventSenderProxy::releaseTouchPoint(int index)
393 {
394     if (index < 0 || index >= m_touchPoints.count())
395         return;
396
397     m_touchPoints[index].setState(Qt::TouchPointReleased);
398 }
399
400 void EventSenderProxy::cancelTouchPoint(int index)
401 {
402     // FIXME: No cancellation state in Qt 5, mapped to release instead.
403     // PlatformTouchEvent conversion later will map all touch points to
404     // cancelled.
405     releaseTouchPoint(index);
406 }
407
408 void EventSenderProxy::sendTouchEvent(QEvent::Type type)
409 {
410     static QTouchDevice* device = 0;
411     if (!device) {
412         device = new QTouchDevice;
413         device->setType(QTouchDevice::TouchScreen);
414         QWindowSystemInterface::registerTouchDevice(device);
415     }
416
417     QTouchEvent event(type, device, m_touchModifiers);
418     event.setTouchPoints(m_touchPoints);
419     m_testController->mainWebView()->sendEvent(&event);
420     QList<QTouchEvent::TouchPoint>::Iterator it = m_touchPoints.begin();
421     while (it != m_touchPoints.end()) {
422         if (it->state() == Qt::TouchPointReleased)
423             it = m_touchPoints.erase(it);
424         else {
425             it->setState(Qt::TouchPointStationary);
426             ++it;
427         }
428     }
429 }
430 #endif
431
432 void EventSenderProxy::sendOrQueueEvent(QEvent* event)
433 {
434     if (!endOfQueue && !eventQueue[endOfQueue].m_delay) {
435         m_testController->mainWebView()->sendEvent(event);
436         delete event;
437         return;
438     }
439
440     eventQueue[endOfQueue++].m_event = event;
441     replaySavedEvents();
442 }
443
444 void EventSenderProxy::replaySavedEvents()
445 {
446     if (isReplayingEvents)
447         return;
448
449     isReplayingEvents = true;
450     int i = 0;
451
452     while (i < endOfQueue) {
453         WTREventQueue& ev = eventQueue[i];
454         if (ev.m_delay)
455             QTest::qWait(ev.m_delay);
456         i++;
457         m_testController->mainWebView()->sendEvent(ev.m_event);
458         delete ev.m_event;
459         ev.m_delay = 0;
460     }
461
462     endOfQueue = 0;
463     isReplayingEvents = false;
464 }
465
466 } // namespace WTR