• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3  * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 #include "config.h"
30 #include "EventSenderQt.h"
31 
32 //#include <QtDebug>
33 
34 #include <QtTest/QtTest>
35 
36 #define KEYCODE_DEL         127
37 #define KEYCODE_BACKSPACE   8
38 #define KEYCODE_LEFTARROW   0xf702
39 #define KEYCODE_RIGHTARROW  0xf703
40 #define KEYCODE_UPARROW     0xf700
41 #define KEYCODE_DOWNARROW   0xf701
42 
43 // Ports like Gtk and Windows expose a different approach for their zooming
44 // API if compared to Qt: they have specific methods for zooming in and out,
45 // as well as a settable zoom factor, while Qt has only a 'setZoomValue' method.
46 // Hence Qt DRT adopts a fixed zoom-factor (1.2) for compatibility.
47 #define ZOOM_STEP           1.2
48 
49 #define DRT_MESSAGE_DONE (QEvent::User + 1)
50 
51 struct DRTEventQueue {
52     QEvent* m_event;
53     int m_delay;
54 };
55 
56 static DRTEventQueue eventQueue[1024];
57 static unsigned endOfQueue;
58 static unsigned startOfQueue;
59 
EventSender(QWebPage * parent)60 EventSender::EventSender(QWebPage* parent)
61     : QObject(parent)
62 {
63     m_page = parent;
64     m_mouseButtonPressed = false;
65     m_drag = false;
66     memset(eventQueue, 0, sizeof(eventQueue));
67     endOfQueue = 0;
68     startOfQueue = 0;
69     m_eventLoop = 0;
70     m_page->view()->installEventFilter(this);
71 }
72 
mouseDown(int button)73 void EventSender::mouseDown(int button)
74 {
75     Qt::MouseButton mouseButton;
76     switch (button) {
77     case 0:
78         mouseButton = Qt::LeftButton;
79         break;
80     case 1:
81         mouseButton = Qt::MidButton;
82         break;
83     case 2:
84         mouseButton = Qt::RightButton;
85         break;
86     case 3:
87         // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
88         mouseButton = Qt::MidButton;
89         break;
90     default:
91         mouseButton = Qt::LeftButton;
92         break;
93     }
94 
95     m_mouseButtons |= mouseButton;
96 
97 //     qDebug() << "EventSender::mouseDown" << frame;
98     QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
99     sendOrQueueEvent(event);
100 }
101 
mouseUp(int button)102 void EventSender::mouseUp(int button)
103 {
104     Qt::MouseButton mouseButton;
105     switch (button) {
106     case 0:
107         mouseButton = Qt::LeftButton;
108         break;
109     case 1:
110         mouseButton = Qt::MidButton;
111         break;
112     case 2:
113         mouseButton = Qt::RightButton;
114         break;
115     case 3:
116         // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
117         mouseButton = Qt::MidButton;
118         break;
119     default:
120         mouseButton = Qt::LeftButton;
121         break;
122     }
123 
124     m_mouseButtons &= ~mouseButton;
125 
126 //     qDebug() << "EventSender::mouseUp" << frame;
127     QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
128     sendOrQueueEvent(event);
129 }
130 
mouseMoveTo(int x,int y)131 void EventSender::mouseMoveTo(int x, int y)
132 {
133 //     qDebug() << "EventSender::mouseMoveTo" << x << y;
134     m_mousePos = QPoint(x, y);
135     QMouseEvent* event = new QMouseEvent(QEvent::MouseMove, m_mousePos, m_mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier);
136     sendOrQueueEvent(event);
137 }
138 
leapForward(int ms)139 void EventSender::leapForward(int ms)
140 {
141     eventQueue[endOfQueue].m_delay = ms;
142     //qDebug() << "EventSender::leapForward" << ms;
143 }
144 
keyDown(const QString & string,const QStringList & modifiers,unsigned int location)145 void EventSender::keyDown(const QString& string, const QStringList& modifiers, unsigned int location)
146 {
147     QString s = string;
148     Qt::KeyboardModifiers modifs = 0;
149     for (int i = 0; i < modifiers.size(); ++i) {
150         const QString& m = modifiers.at(i);
151         if (m == "ctrlKey")
152             modifs |= Qt::ControlModifier;
153         else if (m == "shiftKey")
154             modifs |= Qt::ShiftModifier;
155         else if (m == "altKey")
156             modifs |= Qt::AltModifier;
157         else if (m == "metaKey")
158             modifs |= Qt::MetaModifier;
159     }
160     if (location == 3)
161         modifs |= Qt::KeypadModifier;
162     int code = 0;
163     if (string.length() == 1) {
164         code = string.unicode()->unicode();
165         //qDebug() << ">>>>>>>>> keyDown" << code << (char)code;
166         // map special keycodes used by the tests to something that works for Qt/X11
167         if (code == '\r') {
168             code = Qt::Key_Return;
169         } else if (code == '\t') {
170             code = Qt::Key_Tab;
171             if (modifs == Qt::ShiftModifier)
172                 code = Qt::Key_Backtab;
173             s = QString();
174         } else if (code == KEYCODE_DEL || code == KEYCODE_BACKSPACE) {
175             code = Qt::Key_Backspace;
176             if (modifs == Qt::AltModifier)
177                 modifs = Qt::ControlModifier;
178             s = QString();
179         } else if (code == 'o' && modifs == Qt::ControlModifier) {
180             s = QLatin1String("\n");
181             code = '\n';
182             modifs = 0;
183         } else if (code == 'y' && modifs == Qt::ControlModifier) {
184             s = QLatin1String("c");
185             code = 'c';
186         } else if (code == 'k' && modifs == Qt::ControlModifier) {
187             s = QLatin1String("x");
188             code = 'x';
189         } else if (code == 'a' && modifs == Qt::ControlModifier) {
190             s = QString();
191             code = Qt::Key_Home;
192             modifs = 0;
193         } else if (code == KEYCODE_LEFTARROW) {
194             s = QString();
195             code = Qt::Key_Left;
196             if (modifs & Qt::MetaModifier) {
197                 code = Qt::Key_Home;
198                 modifs &= ~Qt::MetaModifier;
199             }
200         } else if (code == KEYCODE_RIGHTARROW) {
201             s = QString();
202             code = Qt::Key_Right;
203             if (modifs & Qt::MetaModifier) {
204                 code = Qt::Key_End;
205                 modifs &= ~Qt::MetaModifier;
206             }
207         } else if (code == KEYCODE_UPARROW) {
208             s = QString();
209             code = Qt::Key_Up;
210             if (modifs & Qt::MetaModifier) {
211                 code = Qt::Key_PageUp;
212                 modifs &= ~Qt::MetaModifier;
213             }
214         } else if (code == KEYCODE_DOWNARROW) {
215             s = QString();
216             code = Qt::Key_Down;
217             if (modifs & Qt::MetaModifier) {
218                 code = Qt::Key_PageDown;
219                 modifs &= ~Qt::MetaModifier;
220             }
221         } else if (code == 'a' && modifs == Qt::ControlModifier) {
222             s = QString();
223             code = Qt::Key_Home;
224             modifs = 0;
225         } else
226             code = string.unicode()->toUpper().unicode();
227     } else {
228         //qDebug() << ">>>>>>>>> keyDown" << string;
229 
230         if (string.startsWith(QLatin1Char('F')) && string.count() <= 3) {
231             s = s.mid(1);
232             int functionKey = s.toInt();
233             Q_ASSERT(functionKey >= 1 && functionKey <= 35);
234             code = Qt::Key_F1 + (functionKey - 1);
235         // map special keycode strings used by the tests to something that works for Qt/X11
236         } else if (string == QLatin1String("leftArrow")) {
237             s = QString();
238             code = Qt::Key_Left;
239         } else if (string == QLatin1String("rightArrow")) {
240             s = QString();
241             code = Qt::Key_Right;
242         } else if (string == QLatin1String("upArrow")) {
243             s = QString();
244             code = Qt::Key_Up;
245         } else if (string == QLatin1String("downArrow")) {
246             s = QString();
247             code = Qt::Key_Down;
248         } else if (string == QLatin1String("pageUp")) {
249             s = QString();
250             code = Qt::Key_PageUp;
251         } else if (string == QLatin1String("pageDown")) {
252             s = QString();
253             code = Qt::Key_PageDown;
254         } else if (string == QLatin1String("home")) {
255             s = QString();
256             code = Qt::Key_Home;
257         } else if (string == QLatin1String("end")) {
258             s = QString();
259             code = Qt::Key_End;
260         } else if (string == QLatin1String("delete")) {
261             s = QString();
262             code = Qt::Key_Delete;
263         }
264     }
265     QKeyEvent event(QEvent::KeyPress, code, modifs, s);
266     QApplication::sendEvent(m_page, &event);
267     QKeyEvent event2(QEvent::KeyRelease, code, modifs, s);
268     QApplication::sendEvent(m_page, &event2);
269 }
270 
contextClick()271 void EventSender::contextClick()
272 {
273     QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier);
274     QApplication::sendEvent(m_page, &event);
275     QMouseEvent event2(QEvent::MouseButtonRelease, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier);
276     QApplication::sendEvent(m_page, &event2);
277 }
278 
scheduleAsynchronousClick()279 void EventSender::scheduleAsynchronousClick()
280 {
281     QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier);
282     QApplication::postEvent(m_page, event);
283     QMouseEvent* event2 = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier);
284     QApplication::postEvent(m_page, event2);
285 }
286 
addTouchPoint(int x,int y)287 void EventSender::addTouchPoint(int x, int y)
288 {
289 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
290     int id = m_touchPoints.count();
291     QTouchEvent::TouchPoint point(id);
292     m_touchPoints.append(point);
293     updateTouchPoint(id, x, y);
294     m_touchPoints[id].setState(Qt::TouchPointPressed);
295 #endif
296 }
297 
updateTouchPoint(int index,int x,int y)298 void EventSender::updateTouchPoint(int index, int x, int y)
299 {
300 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
301     if (index < 0 || index >= m_touchPoints.count())
302         return;
303 
304     QTouchEvent::TouchPoint &p = m_touchPoints[index];
305     p.setPos(QPointF(x, y));
306     p.setState(Qt::TouchPointMoved);
307 #endif
308 }
309 
setTouchModifier(const QString & modifier,bool enable)310 void EventSender::setTouchModifier(const QString &modifier, bool enable)
311 {
312 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
313     Qt::KeyboardModifier mod = Qt::NoModifier;
314     if (!modifier.compare(QLatin1String("shift"), Qt::CaseInsensitive))
315         mod = Qt::ShiftModifier;
316     else if (!modifier.compare(QLatin1String("alt"), Qt::CaseInsensitive))
317         mod = Qt::AltModifier;
318     else if (!modifier.compare(QLatin1String("meta"), Qt::CaseInsensitive))
319         mod = Qt::MetaModifier;
320     else if (!modifier.compare(QLatin1String("ctrl"), Qt::CaseInsensitive))
321         mod = Qt::ControlModifier;
322 
323     if (enable)
324         m_touchModifiers |= mod;
325     else
326         m_touchModifiers &= ~mod;
327 #endif
328 }
329 
touchStart()330 void EventSender::touchStart()
331 {
332 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
333     if (!m_touchActive) {
334         sendTouchEvent(QEvent::TouchBegin);
335         m_touchActive = true;
336     } else
337         sendTouchEvent(QEvent::TouchUpdate);
338 #endif
339 }
340 
touchMove()341 void EventSender::touchMove()
342 {
343 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
344     sendTouchEvent(QEvent::TouchUpdate);
345 #endif
346 }
347 
touchEnd()348 void EventSender::touchEnd()
349 {
350 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
351     for (int i = 0; i < m_touchPoints.count(); ++i)
352         if (m_touchPoints[i].state() != Qt::TouchPointReleased) {
353             sendTouchEvent(QEvent::TouchUpdate);
354             return;
355         }
356     sendTouchEvent(QEvent::TouchEnd);
357     m_touchActive = false;
358 #endif
359 }
360 
clearTouchPoints()361 void EventSender::clearTouchPoints()
362 {
363 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
364     m_touchPoints.clear();
365     m_touchModifiers = Qt::KeyboardModifiers();
366     m_touchActive = false;
367 #endif
368 }
369 
releaseTouchPoint(int index)370 void EventSender::releaseTouchPoint(int index)
371 {
372 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
373     if (index < 0 || index >= m_touchPoints.count())
374         return;
375 
376     m_touchPoints[index].setState(Qt::TouchPointReleased);
377 #endif
378 }
379 
sendTouchEvent(QEvent::Type type)380 void EventSender::sendTouchEvent(QEvent::Type type)
381 {
382 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
383     QTouchEvent event(type, QTouchEvent::TouchScreen, m_touchModifiers);
384     event.setTouchPoints(m_touchPoints);
385     QApplication::sendEvent(m_page, &event);
386     QList<QTouchEvent::TouchPoint>::Iterator it = m_touchPoints.begin();
387     while (it != m_touchPoints.end()) {
388         if (it->state() == Qt::TouchPointReleased)
389             it = m_touchPoints.erase(it);
390         else {
391             it->setState(Qt::TouchPointStationary);
392             ++it;
393         }
394     }
395 #endif
396 }
397 
zoomPageIn()398 void EventSender::zoomPageIn()
399 {
400     QWebFrame* frame = m_page->mainFrame();
401     if (frame)
402         frame->setZoomFactor(frame->zoomFactor() * ZOOM_STEP);
403 }
404 
zoomPageOut()405 void EventSender::zoomPageOut()
406 {
407     QWebFrame* frame = m_page->mainFrame();
408     if (frame)
409         frame->setZoomFactor(frame->zoomFactor() / ZOOM_STEP);
410 }
411 
frameUnderMouse() const412 QWebFrame* EventSender::frameUnderMouse() const
413 {
414     QWebFrame* frame = m_page->mainFrame();
415 
416 redo:
417     QList<QWebFrame*> children = frame->childFrames();
418     for (int i = 0; i < children.size(); ++i) {
419         if (children.at(i)->geometry().contains(m_mousePos)) {
420             frame = children.at(i);
421             goto redo;
422         }
423     }
424     if (frame->geometry().contains(m_mousePos))
425         return frame;
426     return 0;
427 }
428 
sendOrQueueEvent(QEvent * event)429 void EventSender::sendOrQueueEvent(QEvent* event)
430 {
431     // Mouse move events are queued if
432     // 1. A previous event was queued.
433     // 2. A delay was set-up by leapForward().
434     // 3. A call to mouseMoveTo while the mouse button is pressed could initiate a drag operation, and that does not return until mouseUp is processed.
435     // To be safe and avoid a deadlock, this event is queued.
436     if (endOfQueue == startOfQueue && !eventQueue[endOfQueue].m_delay && (!(m_mouseButtonPressed && (m_eventLoop && event->type() == QEvent::MouseButtonRelease)))) {
437         QApplication::sendEvent(m_page->view(), event);
438         delete event;
439         return;
440     }
441     eventQueue[endOfQueue++].m_event = event;
442     eventQueue[endOfQueue].m_delay = 0;
443     replaySavedEvents(event->type() != QEvent::MouseMove);
444 }
445 
replaySavedEvents(bool flush)446 void EventSender::replaySavedEvents(bool flush)
447 {
448     if (startOfQueue < endOfQueue) {
449         // First send all the events that are ready to be sent
450         while (!eventQueue[startOfQueue].m_delay && startOfQueue < endOfQueue) {
451             QEvent* ev = eventQueue[startOfQueue++].m_event;
452             QApplication::postEvent(m_page->view(), ev); // ev deleted by the system
453         }
454         if (startOfQueue == endOfQueue) {
455             // Reset the queue
456             startOfQueue = 0;
457             endOfQueue = 0;
458         } else {
459             QTest::qWait(eventQueue[startOfQueue].m_delay);
460             eventQueue[startOfQueue].m_delay = 0;
461         }
462     }
463     if (!flush)
464         return;
465 
466     // Send a marker event, it will tell us when it is safe to exit the new event loop
467     QEvent* drtEvent = new QEvent((QEvent::Type)DRT_MESSAGE_DONE);
468     QApplication::postEvent(m_page->view(), drtEvent);
469 
470     // Start an event loop for async handling of Drag & Drop
471     m_eventLoop = new QEventLoop;
472     m_eventLoop->exec();
473     delete m_eventLoop;
474     m_eventLoop = 0;
475 }
476 
eventFilter(QObject * watched,QEvent * event)477 bool EventSender::eventFilter(QObject* watched, QEvent* event)
478 {
479     if (watched != m_page->view())
480         return false;
481     switch (event->type()) {
482     case QEvent::Leave:
483         return true;
484     case QEvent::MouseButtonPress:
485         m_mouseButtonPressed = true;
486         break;
487     case QEvent::MouseMove:
488         if (m_mouseButtonPressed)
489             m_drag = true;
490         break;
491     case QEvent::MouseButtonRelease:
492         m_mouseButtonPressed = false;
493         m_drag = false;
494         break;
495     case DRT_MESSAGE_DONE:
496         m_eventLoop->exit();
497         return true;
498     }
499     return false;
500 }
501