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 <QGraphicsSceneMouseEvent>
33#include <QtTest/QtTest>
34
35#define KEYCODE_DEL         127
36#define KEYCODE_BACKSPACE   8
37#define KEYCODE_LEFTARROW   0xf702
38#define KEYCODE_RIGHTARROW  0xf703
39#define KEYCODE_UPARROW     0xf700
40#define KEYCODE_DOWNARROW   0xf701
41
42// Ports like Gtk and Windows expose a different approach for their zooming
43// API if compared to Qt: they have specific methods for zooming in and out,
44// as well as a settable zoom factor, while Qt has only a 'setZoomValue' method.
45// Hence Qt DRT adopts a fixed zoom-factor (1.2) for compatibility.
46#define ZOOM_STEP           1.2
47
48#define DRT_MESSAGE_DONE (QEvent::User + 1)
49
50struct DRTEventQueue {
51    QEvent* m_event;
52    int m_delay;
53};
54
55static DRTEventQueue eventQueue[1024];
56static unsigned endOfQueue;
57static unsigned startOfQueue;
58
59EventSender::EventSender(QWebPage* parent)
60    : QObject(parent)
61{
62    m_page = parent;
63    m_mouseButtonPressed = false;
64    m_drag = false;
65    memset(eventQueue, 0, sizeof(eventQueue));
66    endOfQueue = 0;
67    startOfQueue = 0;
68    m_eventLoop = 0;
69    m_currentButton = 0;
70    resetClickCount();
71    m_page->view()->installEventFilter(this);
72    // So that we can match Scrollbar::pixelsPerLineStep() in WheelEventQt.cpp and
73    // pass fast/events/platform-wheelevent-in-scrolling-div.html
74    QApplication::setWheelScrollLines(2);
75}
76
77void EventSender::mouseDown(int button)
78{
79    Qt::MouseButton mouseButton;
80    switch (button) {
81    case 0:
82        mouseButton = Qt::LeftButton;
83        break;
84    case 1:
85        mouseButton = Qt::MidButton;
86        break;
87    case 2:
88        mouseButton = Qt::RightButton;
89        break;
90    case 3:
91        // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
92        mouseButton = Qt::MidButton;
93        break;
94    default:
95        mouseButton = Qt::LeftButton;
96        break;
97    }
98
99    // only consider a click to count, an event originated by the
100    // same previous button and at the same position.
101    if (m_currentButton == button
102        && m_mousePos == m_clickPos
103        && m_clickTimer.isActive())
104        m_clickCount++;
105    else
106        m_clickCount = 1;
107
108    m_currentButton = button;
109    m_clickPos = m_mousePos;
110    m_mouseButtons |= mouseButton;
111
112//     qDebug() << "EventSender::mouseDown" << frame;
113    QEvent* event;
114    if (isGraphicsBased()) {
115        event = createGraphicsSceneMouseEvent((m_clickCount == 2) ?
116                    QEvent::GraphicsSceneMouseDoubleClick : QEvent::GraphicsSceneMousePress,
117                    m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
118    } else {
119        event = new QMouseEvent((m_clickCount == 2) ? QEvent::MouseButtonDblClick :
120                    QEvent::MouseButtonPress, m_mousePos, m_mousePos,
121                    mouseButton, m_mouseButtons, Qt::NoModifier);
122    }
123
124    sendOrQueueEvent(event);
125
126    m_clickTimer.start(QApplication::doubleClickInterval(), this);
127}
128
129void EventSender::mouseUp(int button)
130{
131    Qt::MouseButton mouseButton;
132    switch (button) {
133    case 0:
134        mouseButton = Qt::LeftButton;
135        break;
136    case 1:
137        mouseButton = Qt::MidButton;
138        break;
139    case 2:
140        mouseButton = Qt::RightButton;
141        break;
142    case 3:
143        // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
144        mouseButton = Qt::MidButton;
145        break;
146    default:
147        mouseButton = Qt::LeftButton;
148        break;
149    }
150
151    m_mouseButtons &= ~mouseButton;
152
153//     qDebug() << "EventSender::mouseUp" << frame;
154    QEvent* event;
155    if (isGraphicsBased()) {
156        event = createGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseRelease,
157                    m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
158    } else {
159        event = new QMouseEvent(QEvent::MouseButtonRelease,
160                    m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
161    }
162
163    sendOrQueueEvent(event);
164}
165
166void EventSender::mouseMoveTo(int x, int y)
167{
168//     qDebug() << "EventSender::mouseMoveTo" << x << y;
169    m_mousePos = QPoint(x, y);
170
171    QEvent* event;
172    if (isGraphicsBased()) {
173        event = createGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseMove,
174                    m_mousePos, m_mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier);
175    } else {
176        event = new QMouseEvent(QEvent::MouseMove,
177                    m_mousePos, m_mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier);
178    }
179
180    sendOrQueueEvent(event);
181}
182
183#ifndef QT_NO_WHEELEVENT
184void EventSender::mouseScrollBy(int x, int y)
185{
186    continuousMouseScrollBy((x*120), (y*120));
187}
188
189void EventSender::continuousMouseScrollBy(int x, int y)
190{
191    // continuousMouseScrollBy() mimics devices that send fine-grained scroll events where the 'delta' specified is not the usual
192    // multiple of 120. See http://doc.qt.nokia.com/4.6/qwheelevent.html#delta for a good explanation of this.
193    if (x) {
194        QEvent* event;
195        if (isGraphicsBased()) {
196            event = createGraphicsSceneWheelEvent(QEvent::GraphicsSceneWheel,
197                        m_mousePos, m_mousePos, x, Qt::NoModifier, Qt::Horizontal);
198        } else
199            event = new QWheelEvent(m_mousePos, m_mousePos, x, m_mouseButtons, Qt::NoModifier, Qt::Horizontal);
200
201        sendOrQueueEvent(event);
202    }
203    if (y) {
204        QEvent* event;
205        if (isGraphicsBased()) {
206            event = createGraphicsSceneWheelEvent(QEvent::GraphicsSceneWheel,
207                        m_mousePos, m_mousePos, y, Qt::NoModifier, Qt::Vertical);
208        } else
209            event = new QWheelEvent(m_mousePos, m_mousePos, y, m_mouseButtons, Qt::NoModifier, Qt::Vertical);
210
211        sendOrQueueEvent(event);
212    }
213}
214#endif
215
216void EventSender::leapForward(int ms)
217{
218    eventQueue[endOfQueue].m_delay = ms;
219    //qDebug() << "EventSender::leapForward" << ms;
220}
221
222void EventSender::keyDown(const QString& string, const QStringList& modifiers, unsigned int location)
223{
224    QString s = string;
225    Qt::KeyboardModifiers modifs = 0;
226    for (int i = 0; i < modifiers.size(); ++i) {
227        const QString& m = modifiers.at(i);
228        if (m == "ctrlKey")
229            modifs |= Qt::ControlModifier;
230        else if (m == "shiftKey")
231            modifs |= Qt::ShiftModifier;
232        else if (m == "altKey")
233            modifs |= Qt::AltModifier;
234        else if (m == "metaKey")
235            modifs |= Qt::MetaModifier;
236    }
237    if (location == 3)
238        modifs |= Qt::KeypadModifier;
239    int code = 0;
240    if (string.length() == 1) {
241        code = string.unicode()->unicode();
242        //qDebug() << ">>>>>>>>> keyDown" << code << (char)code;
243        // map special keycodes used by the tests to something that works for Qt/X11
244        if (code == '\r') {
245            code = Qt::Key_Return;
246        } else if (code == '\t') {
247            code = Qt::Key_Tab;
248            if (modifs == Qt::ShiftModifier)
249                code = Qt::Key_Backtab;
250            s = QString();
251        } else if (code == KEYCODE_DEL || code == KEYCODE_BACKSPACE) {
252            code = Qt::Key_Backspace;
253            if (modifs == Qt::AltModifier)
254                modifs = Qt::ControlModifier;
255            s = QString();
256        } else if (code == 'o' && modifs == Qt::ControlModifier) {
257            // Mimic the emacs ctrl-o binding on Mac by inserting a paragraph
258            // separator and then putting the cursor back to its original
259            // position. Allows us to pass emacs-ctrl-o.html
260            s = QLatin1String("\n");
261            code = '\n';
262            modifs = 0;
263            QKeyEvent event(QEvent::KeyPress, code, modifs, s);
264            sendEvent(m_page, &event);
265            QKeyEvent event2(QEvent::KeyRelease, code, modifs, s);
266            sendEvent(m_page, &event2);
267            s = QString();
268            code = Qt::Key_Left;
269        } else if (code == 'y' && modifs == Qt::ControlModifier) {
270            s = QLatin1String("c");
271            code = 'c';
272        } else if (code == 'k' && modifs == Qt::ControlModifier) {
273            s = QLatin1String("x");
274            code = 'x';
275        } else if (code == 'a' && modifs == Qt::ControlModifier) {
276            s = QString();
277            code = Qt::Key_Home;
278            modifs = 0;
279        } else if (code == KEYCODE_LEFTARROW) {
280            s = QString();
281            code = Qt::Key_Left;
282            if (modifs & Qt::MetaModifier) {
283                code = Qt::Key_Home;
284                modifs &= ~Qt::MetaModifier;
285            }
286        } else if (code == KEYCODE_RIGHTARROW) {
287            s = QString();
288            code = Qt::Key_Right;
289            if (modifs & Qt::MetaModifier) {
290                code = Qt::Key_End;
291                modifs &= ~Qt::MetaModifier;
292            }
293        } else if (code == KEYCODE_UPARROW) {
294            s = QString();
295            code = Qt::Key_Up;
296            if (modifs & Qt::MetaModifier) {
297                code = Qt::Key_PageUp;
298                modifs &= ~Qt::MetaModifier;
299            }
300        } else if (code == KEYCODE_DOWNARROW) {
301            s = QString();
302            code = Qt::Key_Down;
303            if (modifs & Qt::MetaModifier) {
304                code = Qt::Key_PageDown;
305                modifs &= ~Qt::MetaModifier;
306            }
307        } else if (code == 'a' && modifs == Qt::ControlModifier) {
308            s = QString();
309            code = Qt::Key_Home;
310            modifs = 0;
311        } else
312            code = string.unicode()->toUpper().unicode();
313    } else {
314        //qDebug() << ">>>>>>>>> keyDown" << string;
315
316        if (string.startsWith(QLatin1Char('F')) && string.count() <= 3) {
317            s = s.mid(1);
318            int functionKey = s.toInt();
319            Q_ASSERT(functionKey >= 1 && functionKey <= 35);
320            code = Qt::Key_F1 + (functionKey - 1);
321        // map special keycode strings used by the tests to something that works for Qt/X11
322        } else if (string == QLatin1String("leftArrow")) {
323            s = QString();
324            code = Qt::Key_Left;
325        } else if (string == QLatin1String("rightArrow")) {
326            s = QString();
327            code = Qt::Key_Right;
328        } else if (string == QLatin1String("upArrow")) {
329            s = QString();
330            code = Qt::Key_Up;
331        } else if (string == QLatin1String("downArrow")) {
332            s = QString();
333            code = Qt::Key_Down;
334        } else if (string == QLatin1String("pageUp")) {
335            s = QString();
336            code = Qt::Key_PageUp;
337        } else if (string == QLatin1String("pageDown")) {
338            s = QString();
339            code = Qt::Key_PageDown;
340        } else if (string == QLatin1String("home")) {
341            s = QString();
342            code = Qt::Key_Home;
343        } else if (string == QLatin1String("end")) {
344            s = QString();
345            code = Qt::Key_End;
346        } else if (string == QLatin1String("insert")) {
347            s = QString();
348            code = Qt::Key_Insert;
349        } else if (string == QLatin1String("delete")) {
350            s = QString();
351            code = Qt::Key_Delete;
352        } else if (string == QLatin1String("printScreen")) {
353            s = QString();
354            code = Qt::Key_Print;
355        } else if (string == QLatin1String("menu")) {
356            s = QString();
357            code = Qt::Key_Menu;
358        }
359    }
360    QKeyEvent event(QEvent::KeyPress, code, modifs, s);
361    sendEvent(m_page, &event);
362    QKeyEvent event2(QEvent::KeyRelease, code, modifs, s);
363    sendEvent(m_page, &event2);
364}
365
366QStringList EventSender::contextClick()
367{
368    QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier);
369    sendEvent(m_page, &event);
370    QMouseEvent event2(QEvent::MouseButtonRelease, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier);
371    sendEvent(m_page, &event2);
372
373    if (isGraphicsBased()) {
374        QGraphicsSceneContextMenuEvent ctxEvent(QEvent::GraphicsSceneContextMenu);
375        ctxEvent.setReason(QGraphicsSceneContextMenuEvent::Mouse);
376        ctxEvent.setPos(m_mousePos);
377        WebCore::WebViewGraphicsBased* view = qobject_cast<WebCore::WebViewGraphicsBased*>(m_page->view());
378        if (view)
379            sendEvent(view->graphicsView(), &ctxEvent);
380    } else {
381        QContextMenuEvent ctxEvent(QContextMenuEvent::Mouse, m_mousePos);
382        sendEvent(m_page->view(), &ctxEvent);
383    }
384    return DumpRenderTreeSupportQt::contextMenu(m_page);
385}
386
387void EventSender::scheduleAsynchronousClick()
388{
389    QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier);
390    postEvent(m_page, event);
391    QMouseEvent* event2 = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier);
392    postEvent(m_page, event2);
393}
394
395void EventSender::addTouchPoint(int x, int y)
396{
397    // Use index to refer to the position in the vector that this touch
398    // is stored. We then create a unique id for the touch that will be
399    // passed into WebCore.
400    int index = m_touchPoints.count();
401    int id = m_touchPoints.isEmpty() ? 0 : m_touchPoints.last().id() + 1;
402    QTouchEvent::TouchPoint point(id);
403    m_touchPoints.append(point);
404    updateTouchPoint(index, x, y);
405    m_touchPoints[index].setState(Qt::TouchPointPressed);
406}
407
408void EventSender::updateTouchPoint(int index, int x, int y)
409{
410    if (index < 0 || index >= m_touchPoints.count())
411        return;
412
413    QTouchEvent::TouchPoint &p = m_touchPoints[index];
414    p.setPos(QPointF(x, y));
415    p.setState(Qt::TouchPointMoved);
416}
417
418void EventSender::setTouchModifier(const QString &modifier, bool enable)
419{
420    Qt::KeyboardModifier mod = Qt::NoModifier;
421    if (!modifier.compare(QLatin1String("shift"), Qt::CaseInsensitive))
422        mod = Qt::ShiftModifier;
423    else if (!modifier.compare(QLatin1String("alt"), Qt::CaseInsensitive))
424        mod = Qt::AltModifier;
425    else if (!modifier.compare(QLatin1String("meta"), Qt::CaseInsensitive))
426        mod = Qt::MetaModifier;
427    else if (!modifier.compare(QLatin1String("ctrl"), Qt::CaseInsensitive))
428        mod = Qt::ControlModifier;
429
430    if (enable)
431        m_touchModifiers |= mod;
432    else
433        m_touchModifiers &= ~mod;
434}
435
436void EventSender::touchStart()
437{
438    if (!m_touchActive) {
439        sendTouchEvent(QEvent::TouchBegin);
440        m_touchActive = true;
441    } else
442        sendTouchEvent(QEvent::TouchUpdate);
443}
444
445void EventSender::touchMove()
446{
447    sendTouchEvent(QEvent::TouchUpdate);
448}
449
450void EventSender::touchEnd()
451{
452    for (int i = 0; i < m_touchPoints.count(); ++i)
453        if (m_touchPoints[i].state() != Qt::TouchPointReleased) {
454            sendTouchEvent(QEvent::TouchUpdate);
455            return;
456        }
457    sendTouchEvent(QEvent::TouchEnd);
458    m_touchActive = false;
459}
460
461void EventSender::clearTouchPoints()
462{
463    m_touchPoints.clear();
464    m_touchModifiers = Qt::KeyboardModifiers();
465    m_touchActive = false;
466}
467
468void EventSender::releaseTouchPoint(int index)
469{
470    if (index < 0 || index >= m_touchPoints.count())
471        return;
472
473    m_touchPoints[index].setState(Qt::TouchPointReleased);
474}
475
476void EventSender::sendTouchEvent(QEvent::Type type)
477{
478    QTouchEvent event(type, QTouchEvent::TouchScreen, m_touchModifiers);
479    event.setTouchPoints(m_touchPoints);
480    sendEvent(m_page, &event);
481    QList<QTouchEvent::TouchPoint>::Iterator it = m_touchPoints.begin();
482    while (it != m_touchPoints.end()) {
483        if (it->state() == Qt::TouchPointReleased)
484            it = m_touchPoints.erase(it);
485        else {
486            it->setState(Qt::TouchPointStationary);
487            ++it;
488        }
489    }
490}
491
492void EventSender::zoomPageIn()
493{
494    if (QWebFrame* frame = m_page->mainFrame())
495        frame->setZoomFactor(frame->zoomFactor() * ZOOM_STEP);
496}
497
498void EventSender::zoomPageOut()
499{
500    if (QWebFrame* frame = m_page->mainFrame())
501        frame->setZoomFactor(frame->zoomFactor() / ZOOM_STEP);
502}
503
504void EventSender::textZoomIn()
505{
506    if (QWebFrame* frame = m_page->mainFrame())
507        frame->setTextSizeMultiplier(frame->textSizeMultiplier() * ZOOM_STEP);
508}
509
510void EventSender::textZoomOut()
511{
512    if (QWebFrame* frame = m_page->mainFrame())
513        frame->setTextSizeMultiplier(frame->textSizeMultiplier() / ZOOM_STEP);
514}
515
516QWebFrame* EventSender::frameUnderMouse() const
517{
518    QWebFrame* frame = m_page->mainFrame();
519
520redo:
521    QList<QWebFrame*> children = frame->childFrames();
522    for (int i = 0; i < children.size(); ++i) {
523        if (children.at(i)->geometry().contains(m_mousePos)) {
524            frame = children.at(i);
525            goto redo;
526        }
527    }
528    if (frame->geometry().contains(m_mousePos))
529        return frame;
530    return 0;
531}
532
533void EventSender::sendOrQueueEvent(QEvent* event)
534{
535    // Mouse move events are queued if
536    // 1. A previous event was queued.
537    // 2. A delay was set-up by leapForward().
538    // 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.
539    // To be safe and avoid a deadlock, this event is queued.
540    if (endOfQueue == startOfQueue && !eventQueue[endOfQueue].m_delay && (!(m_mouseButtonPressed && (m_eventLoop && event->type() == QEvent::MouseButtonRelease)))) {
541        sendEvent(m_page->view(), event);
542        delete event;
543        return;
544    }
545    eventQueue[endOfQueue++].m_event = event;
546    eventQueue[endOfQueue].m_delay = 0;
547    replaySavedEvents(event->type() != QEvent::MouseMove);
548}
549
550void EventSender::replaySavedEvents(bool flush)
551{
552    if (startOfQueue < endOfQueue) {
553        // First send all the events that are ready to be sent
554        while (!eventQueue[startOfQueue].m_delay && startOfQueue < endOfQueue) {
555            QEvent* ev = eventQueue[startOfQueue++].m_event;
556            postEvent(m_page->view(), ev);
557        }
558        if (startOfQueue == endOfQueue) {
559            // Reset the queue
560            startOfQueue = 0;
561            endOfQueue = 0;
562        } else {
563            QTest::qWait(eventQueue[startOfQueue].m_delay);
564            eventQueue[startOfQueue].m_delay = 0;
565        }
566    }
567    if (!flush)
568        return;
569
570    // Send a marker event, it will tell us when it is safe to exit the new event loop
571    QEvent* drtEvent = new QEvent((QEvent::Type)DRT_MESSAGE_DONE);
572    QApplication::postEvent(m_page->view(), drtEvent);
573
574    // Start an event loop for async handling of Drag & Drop
575    m_eventLoop = new QEventLoop;
576    m_eventLoop->exec();
577    delete m_eventLoop;
578    m_eventLoop = 0;
579}
580
581bool EventSender::eventFilter(QObject* watched, QEvent* event)
582{
583    if (watched != m_page->view())
584        return false;
585    switch (event->type()) {
586    case QEvent::Leave:
587        return true;
588    case QEvent::MouseButtonPress:
589    case QEvent::GraphicsSceneMousePress:
590        m_mouseButtonPressed = true;
591        break;
592    case QEvent::MouseMove:
593    case QEvent::GraphicsSceneMouseMove:
594        if (m_mouseButtonPressed)
595            m_drag = true;
596        break;
597    case QEvent::MouseButtonRelease:
598    case QEvent::GraphicsSceneMouseRelease:
599        m_mouseButtonPressed = false;
600        m_drag = false;
601        break;
602    case DRT_MESSAGE_DONE:
603        m_eventLoop->exit();
604        return true;
605    }
606    return false;
607}
608
609void EventSender::timerEvent(QTimerEvent* ev)
610{
611    m_clickTimer.stop();
612}
613
614QGraphicsSceneMouseEvent* EventSender::createGraphicsSceneMouseEvent(QEvent::Type type, const QPoint& pos, const QPoint& screenPos, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
615{
616    QGraphicsSceneMouseEvent* event;
617    event = new QGraphicsSceneMouseEvent(type);
618    event->setPos(pos);
619    event->setScreenPos(screenPos);
620    event->setButton(button);
621    event->setButtons(buttons);
622    event->setModifiers(modifiers);
623
624    return event;
625}
626
627QGraphicsSceneWheelEvent* EventSender::createGraphicsSceneWheelEvent(QEvent::Type type, const QPoint& pos, const QPoint& screenPos, int delta, Qt::KeyboardModifiers modifiers, Qt::Orientation orientation)
628{
629    QGraphicsSceneWheelEvent* event;
630    event = new QGraphicsSceneWheelEvent(type);
631    event->setPos(pos);
632    event->setScreenPos(screenPos);
633    event->setDelta(delta);
634    event->setModifiers(modifiers);
635    event->setOrientation(orientation);
636
637    return event;
638}
639
640void EventSender::sendEvent(QObject* receiver, QEvent* event)
641{
642    if (WebCore::WebViewGraphicsBased* view = qobject_cast<WebCore::WebViewGraphicsBased*>(receiver))
643        view->scene()->sendEvent(view->graphicsView(), event);
644    else
645        QApplication::sendEvent(receiver, event);
646}
647
648void EventSender::postEvent(QObject* receiver, QEvent* event)
649{
650    // QGraphicsScene does not have a postEvent method, so send the event in this case
651    // and delete it after that.
652    if (WebCore::WebViewGraphicsBased* view = qobject_cast<WebCore::WebViewGraphicsBased*>(receiver)) {
653        view->scene()->sendEvent(view->graphicsView(), event);
654        delete event;
655    } else
656        QApplication::postEvent(receiver, event); // event deleted by the system
657}
658