Home | History | Annotate | Download | only in qt
      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 
     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 
     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 
    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 
    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 
    139 void EventSender::leapForward(int ms)
    140 {
    141     eventQueue[endOfQueue].m_delay = ms;
    142     //qDebug() << "EventSender::leapForward" << ms;
    143 }
    144 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    341 void EventSender::touchMove()
    342 {
    343 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
    344     sendTouchEvent(QEvent::TouchUpdate);
    345 #endif
    346 }
    347 
    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 
    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 
    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 
    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 
    398 void EventSender::zoomPageIn()
    399 {
    400     QWebFrame* frame = m_page->mainFrame();
    401     if (frame)
    402         frame->setZoomFactor(frame->zoomFactor() * ZOOM_STEP);
    403 }
    404 
    405 void EventSender::zoomPageOut()
    406 {
    407     QWebFrame* frame = m_page->mainFrame();
    408     if (frame)
    409         frame->setZoomFactor(frame->zoomFactor() / ZOOM_STEP);
    410 }
    411 
    412 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 
    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 
    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 
    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