2 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "EventSenderProxy.h"
29 #include "PlatformWebView.h"
30 #include "TestController.h"
31 #include <QGraphicsSceneMouseEvent>
33 #include <QtTest/QtTest>
34 #include <WebKit2/WKPagePrivate.h>
35 #include <WebKit2/WKStringQt.h>
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
46 struct WTREventQueue {
51 static WTREventQueue eventQueue[1024];
52 static unsigned endOfQueue;
53 static unsigned startOfQueue;
55 EventSenderProxy::EventSenderProxy(TestController* testController)
56 : m_testController(testController)
59 , m_leftMouseButtonDown(false)
63 , m_clickButton(kWKEventMouseButtonNoButton)
65 #if ENABLE(TOUCH_EVENTS)
66 , m_touchActive(false)
69 memset(eventQueue, 0, sizeof(eventQueue));
74 static Qt::MouseButton getMouseButton(unsigned button)
76 Qt::MouseButton mouseButton;
79 mouseButton = Qt::LeftButton;
82 mouseButton = Qt::MidButton;
85 mouseButton = Qt::RightButton;
88 // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
89 mouseButton = Qt::MidButton;
92 mouseButton = Qt::LeftButton;
98 static Qt::KeyboardModifiers getModifiers(WKEventModifiers modifiersRef)
100 Qt::KeyboardModifiers modifiers = 0;
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;
114 void EventSenderProxy::keyDown(WKStringRef keyRef, WKEventModifiers modifiersRef, unsigned location)
116 const QString key = WKStringCopyQString(keyRef);
117 QString keyText = key;
119 Qt::KeyboardModifiers modifiers = getModifiers(modifiersRef);
122 modifiers |= Qt::KeypadModifier;
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
128 code = Qt::Key_Return;
129 } else if (code == '\t') {
131 if (modifiers == Qt::ShiftModifier)
132 code = Qt::Key_Backtab;
134 } else if (code == KEYCODE_DEL || code == KEYCODE_BACKSPACE) {
135 code = Qt::Key_Backspace;
136 if (modifiers == Qt::AltModifier)
137 modifiers = Qt::ControlModifier;
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");
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);
152 } else if (code == 'y' && modifiers == Qt::ControlModifier) {
153 keyText = QLatin1String("c");
155 } else if (code == 'k' && modifiers == Qt::ControlModifier) {
156 keyText = QLatin1String("x");
158 } else if (code == 'a' && modifiers == Qt::ControlModifier) {
162 } else if (code == KEYCODE_LEFTARROW) {
165 if (modifiers & Qt::MetaModifier) {
167 modifiers &= ~Qt::MetaModifier;
169 } else if (code == KEYCODE_RIGHTARROW) {
171 code = Qt::Key_Right;
172 if (modifiers & Qt::MetaModifier) {
174 modifiers &= ~Qt::MetaModifier;
176 } else if (code == KEYCODE_UPARROW) {
179 if (modifiers & Qt::MetaModifier) {
180 code = Qt::Key_PageUp;
181 modifiers &= ~Qt::MetaModifier;
183 } else if (code == KEYCODE_DOWNARROW) {
186 if (modifiers & Qt::MetaModifier) {
187 code = Qt::Key_PageDown;
188 modifiers &= ~Qt::MetaModifier;
190 } else if (code == 'a' && modifiers == Qt::ControlModifier) {
195 code = key.unicode()->toUpper().unicode();
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")) {
206 } else if (key == QLatin1String("rightArrow")) {
208 code = Qt::Key_Right;
209 } else if (key == QLatin1String("upArrow")) {
212 } else if (key == QLatin1String("downArrow")) {
215 } else if (key == QLatin1String("pageUp")) {
217 code = Qt::Key_PageUp;
218 } else if (key == QLatin1String("pageDown")) {
220 code = Qt::Key_PageDown;
221 } else if (key == QLatin1String("home")) {
224 } else if (key == QLatin1String("end")) {
227 } else if (key == QLatin1String("insert")) {
229 code = Qt::Key_Insert;
230 } else if (key == QLatin1String("delete")) {
232 code = Qt::Key_Delete;
233 } else if (key == QLatin1String("printScreen")) {
235 code = Qt::Key_Print;
236 } else if (key == QLatin1String("menu")) {
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);
247 void EventSenderProxy::updateClickCountForButton(int button)
249 if (m_time - m_clickTime < QApplication::doubleClickInterval() / 1000.0 && m_position == m_clickPosition && button == m_clickButton) {
251 m_clickTime = m_time;
256 m_clickTime = m_time;
257 m_clickPosition = m_position;
258 m_clickButton = button;
261 void EventSenderProxy::mouseDown(unsigned button, WKEventModifiers wkModifiers)
263 Qt::KeyboardModifiers modifiers = getModifiers(wkModifiers);
264 Qt::MouseButton mouseButton = getMouseButton(button);
266 updateClickCountForButton(button);
268 m_mouseButtons |= mouseButton;
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);
274 sendOrQueueEvent(event);
277 void EventSenderProxy::mouseUp(unsigned button, WKEventModifiers)
279 Qt::MouseButton mouseButton = getMouseButton(button);
280 m_mouseButtons &= ~mouseButton;
282 QPoint mousePos(m_position.x, m_position.y);
283 QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonRelease,
284 mousePos, mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
286 sendOrQueueEvent(event);
289 void EventSenderProxy::mouseMoveTo(double x, double y)
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);
298 sendOrQueueEvent(event);
301 void EventSenderProxy::mouseScrollBy(int, int)
303 // FIXME: Implement this.
306 void EventSenderProxy::leapForward(int ms)
308 eventQueue[endOfQueue].m_delay = ms;
311 #if ENABLE(TOUCH_EVENTS)
312 void EventSenderProxy::addTouchPoint(int x, int y)
314 const int id = m_touchPoints.isEmpty() ? 0 : m_touchPoints.last().id() + 1;
315 const QPointF pos(x, y);
316 QTouchEvent::TouchPoint point(id);
318 point.setStartPos(pos);
319 point.setState(Qt::TouchPointPressed);
320 m_touchPoints.append(point);
323 void EventSenderProxy::updateTouchPoint(int index, int x, int y)
325 ASSERT(index >= 0 && index < m_touchPoints.count());
326 QTouchEvent::TouchPoint &p = m_touchPoints[index];
327 p.setPos(QPointF(x, y));
328 p.setState(Qt::TouchPointMoved);
331 void EventSenderProxy::setTouchModifier(WKEventModifiers modifier, bool enable)
333 Qt::KeyboardModifiers mod = getModifiers(modifier);
336 m_touchModifiers |= mod;
338 m_touchModifiers &= ~mod;
341 void EventSenderProxy::touchStart()
343 if (!m_touchActive) {
344 sendTouchEvent(QEvent::TouchBegin);
345 m_touchActive = true;
347 sendTouchEvent(QEvent::TouchUpdate);
350 void EventSenderProxy::touchMove()
352 sendTouchEvent(QEvent::TouchUpdate);
355 void EventSenderProxy::touchEnd()
357 for (int i = 0; i < m_touchPoints.count(); ++i) {
358 if (m_touchPoints[i].state() != Qt::TouchPointReleased) {
359 sendTouchEvent(QEvent::TouchUpdate);
363 sendTouchEvent(QEvent::TouchEnd);
364 m_touchActive = false;
367 void EventSenderProxy::clearTouchPoints()
369 m_touchPoints.clear();
370 m_touchModifiers = Qt::KeyboardModifiers();
371 m_touchActive = false;
374 void EventSenderProxy::releaseTouchPoint(int index)
376 if (index < 0 || index >= m_touchPoints.count())
379 m_touchPoints[index].setState(Qt::TouchPointReleased);
382 void EventSenderProxy::sendTouchEvent(QEvent::Type type)
384 QTouchEvent event(type, QTouchEvent::TouchScreen, m_touchModifiers);
385 event.setTouchPoints(m_touchPoints);
386 m_testController->mainWebView()->sendEvent(&event);
387 QList<QTouchEvent::TouchPoint>::Iterator it = m_touchPoints.begin();
388 while (it != m_touchPoints.end()) {
389 if (it->state() == Qt::TouchPointReleased)
390 it = m_touchPoints.erase(it);
392 it->setState(Qt::TouchPointStationary);
399 void EventSenderProxy::sendOrQueueEvent(QEvent* event)
401 if (endOfQueue == startOfQueue && !eventQueue[endOfQueue].m_delay) {
402 m_testController->mainWebView()->sendEvent(event);
407 eventQueue[endOfQueue++].m_event = event;
408 eventQueue[endOfQueue].m_delay = 0;
412 void EventSenderProxy::replaySavedEvents()
414 if (startOfQueue < endOfQueue) {
415 while (!eventQueue[startOfQueue].m_delay && startOfQueue < endOfQueue) {
416 QEvent* ev = eventQueue[startOfQueue++].m_event;
417 m_testController->mainWebView()->postEvent(ev);
419 if (startOfQueue == endOfQueue) {
423 QTest::qWait(eventQueue[startOfQueue].m_delay);
424 eventQueue[startOfQueue].m_delay = 0;