WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "EventSenderQt.h"
+
+#include <QGraphicsSceneMouseEvent>
+#include <QtTest/QtTest>
+
+#define KEYCODE_DEL         127
+#define KEYCODE_BACKSPACE   8
+#define KEYCODE_LEFTARROW   0xf702
+#define KEYCODE_RIGHTARROW  0xf703
+#define KEYCODE_UPARROW     0xf700
+#define KEYCODE_DOWNARROW   0xf701
+
+// Ports like Gtk and Windows expose a different approach for their zooming
+// API if compared to Qt: they have specific methods for zooming in and out,
+// as well as a settable zoom factor, while Qt has only a 'setZoomValue' method.
+// Hence Qt DRT adopts a fixed zoom-factor (1.2) for compatibility.
+#define ZOOM_STEP           1.2
+
+#define DRT_MESSAGE_DONE (QEvent::User + 1)
+
+struct DRTEventQueue {
+    QEvent* m_event;
+    int m_delay;
+};
+
+static DRTEventQueue eventQueue[1024];
+static unsigned endOfQueue;
+static unsigned startOfQueue;
+
+EventSender::EventSender(QWebPage* parent)
+    : QObject(parent)
+{
+    m_page = parent;
+    m_mouseButtonPressed = false;
+    m_drag = false;
+    memset(eventQueue, 0, sizeof(eventQueue));
+    endOfQueue = 0;
+    startOfQueue = 0;
+    m_eventLoop = 0;
+    m_currentButton = 0;
+    resetClickCount();
+    m_page->view()->installEventFilter(this);
+    // So that we can match Scrollbar::pixelsPerLineStep() in WheelEventQt.cpp and
+    // pass fast/events/platform-wheelevent-in-scrolling-div.html
+    QApplication::setWheelScrollLines(2);
+}
+
+void EventSender::mouseDown(int button)
+{
+    Qt::MouseButton mouseButton;
+    switch (button) {
+    case 0:
+        mouseButton = Qt::LeftButton;
+        break;
+    case 1:
+        mouseButton = Qt::MidButton;
+        break;
+    case 2:
+        mouseButton = Qt::RightButton;
+        break;
+    case 3:
+        // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
+        mouseButton = Qt::MidButton;
+        break;
+    default:
+        mouseButton = Qt::LeftButton;
+        break;
+    }
+
+    // only consider a click to count, an event originated by the
+    // same previous button and at the same position.
+    if (m_currentButton == button
+        && m_mousePos == m_clickPos
+        && m_clickTimer.isActive())
+        m_clickCount++;
+    else
+        m_clickCount = 1;
+
+    m_currentButton = button;
+    m_clickPos = m_mousePos;
+    m_mouseButtons |= mouseButton;
+
+//     qDebug() << "EventSender::mouseDown" << frame;
+    QEvent* event;
+    if (isGraphicsBased()) {
+        event = createGraphicsSceneMouseEvent((m_clickCount == 2) ?
+                    QEvent::GraphicsSceneMouseDoubleClick : QEvent::GraphicsSceneMousePress,
+                    m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
+    } else {
+        event = new QMouseEvent((m_clickCount == 2) ? QEvent::MouseButtonDblClick :
+                    QEvent::MouseButtonPress, m_mousePos, m_mousePos,
+                    mouseButton, m_mouseButtons, Qt::NoModifier);
+    }
+
+    sendOrQueueEvent(event);
+
+    m_clickTimer.start(QApplication::doubleClickInterval(), this);
+}
+
+void EventSender::mouseUp(int button)
+{
+    Qt::MouseButton mouseButton;
+    switch (button) {
+    case 0:
+        mouseButton = Qt::LeftButton;
+        break;
+    case 1:
+        mouseButton = Qt::MidButton;
+        break;
+    case 2:
+        mouseButton = Qt::RightButton;
+        break;
+    case 3:
+        // fast/events/mouse-click-events expects the 4th button to be treated as the middle button
+        mouseButton = Qt::MidButton;
+        break;
+    default:
+        mouseButton = Qt::LeftButton;
+        break;
+    }
+
+    m_mouseButtons &= ~mouseButton;
+
+//     qDebug() << "EventSender::mouseUp" << frame;
+    QEvent* event;
+    if (isGraphicsBased()) {
+        event = createGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseRelease,
+                    m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
+    } else {
+        event = new QMouseEvent(QEvent::MouseButtonRelease,
+                    m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier);
+    }
+
+    sendOrQueueEvent(event);
+}
+
+void EventSender::mouseMoveTo(int x, int y)
+{
+//     qDebug() << "EventSender::mouseMoveTo" << x << y;
+    m_mousePos = QPoint(x, y);
+
+    QEvent* event;
+    if (isGraphicsBased()) {
+        event = createGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseMove,
+                    m_mousePos, m_mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier);
+    } else {
+        event = new QMouseEvent(QEvent::MouseMove,
+                    m_mousePos, m_mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier);
+    }
+
+    sendOrQueueEvent(event);
+}
+
+#ifndef QT_NO_WHEELEVENT
+void EventSender::mouseScrollBy(int x, int y)
+{
+    continuousMouseScrollBy((x*120), (y*120));
+}
+
+void EventSender::continuousMouseScrollBy(int x, int y)
+{
+    // continuousMouseScrollBy() mimics devices that send fine-grained scroll events where the 'delta' specified is not the usual
+    // multiple of 120. See http://doc.qt.nokia.com/4.6/qwheelevent.html#delta for a good explanation of this.
+    if (x) {
+        QEvent* event;
+        if (isGraphicsBased()) {
+            event = createGraphicsSceneWheelEvent(QEvent::GraphicsSceneWheel,
+                        m_mousePos, m_mousePos, x, Qt::NoModifier, Qt::Horizontal);
+        } else
+            event = new QWheelEvent(m_mousePos, m_mousePos, x, m_mouseButtons, Qt::NoModifier, Qt::Horizontal);
+
+        sendOrQueueEvent(event);
+    }
+    if (y) {
+        QEvent* event;
+        if (isGraphicsBased()) {
+            event = createGraphicsSceneWheelEvent(QEvent::GraphicsSceneWheel,
+                        m_mousePos, m_mousePos, y, Qt::NoModifier, Qt::Vertical);
+        } else
+            event = new QWheelEvent(m_mousePos, m_mousePos, y, m_mouseButtons, Qt::NoModifier, Qt::Vertical);
+
+        sendOrQueueEvent(event);
+    }
+}
+#endif
+
+void EventSender::leapForward(int ms)
+{
+    eventQueue[endOfQueue].m_delay = ms;
+    //qDebug() << "EventSender::leapForward" << ms;
+}
+
+void EventSender::keyDown(const QString& string, const QStringList& modifiers, unsigned int location)
+{
+    QString s = string;
+    Qt::KeyboardModifiers modifs = 0;
+    for (int i = 0; i < modifiers.size(); ++i) {
+        const QString& m = modifiers.at(i);
+        if (m == "ctrlKey")
+            modifs |= Qt::ControlModifier;
+        else if (m == "shiftKey")
+            modifs |= Qt::ShiftModifier;
+        else if (m == "altKey")
+            modifs |= Qt::AltModifier;
+        else if (m == "metaKey")
+            modifs |= Qt::MetaModifier;
+    }
+    if (location == 3)
+        modifs |= Qt::KeypadModifier;
+    int code = 0;
+    if (string.length() == 1) {
+        code = string.unicode()->unicode();
+        //qDebug() << ">>>>>>>>> keyDown" << code << (char)code;
+        // map special keycodes used by the tests to something that works for Qt/X11
+        if (code == '\r') {
+            code = Qt::Key_Return;
+        } else if (code == '\t') {
+            code = Qt::Key_Tab;
+            if (modifs == Qt::ShiftModifier)
+                code = Qt::Key_Backtab;
+            s = QString();
+        } else if (code == KEYCODE_DEL || code == KEYCODE_BACKSPACE) {
+            code = Qt::Key_Backspace;
+            if (modifs == Qt::AltModifier)
+                modifs = Qt::ControlModifier;
+            s = QString();
+        } else if (code == 'o' && modifs == Qt::ControlModifier) {
+            s = QLatin1String("\n");
+            code = '\n';
+            modifs = 0;
+        } else if (code == 'y' && modifs == Qt::ControlModifier) {
+            s = QLatin1String("c");
+            code = 'c';
+        } else if (code == 'k' && modifs == Qt::ControlModifier) {
+            s = QLatin1String("x");
+            code = 'x';
+        } else if (code == 'a' && modifs == Qt::ControlModifier) {
+            s = QString();
+            code = Qt::Key_Home;
+            modifs = 0;
+        } else if (code == KEYCODE_LEFTARROW) {
+            s = QString();
+            code = Qt::Key_Left;
+            if (modifs & Qt::MetaModifier) {
+                code = Qt::Key_Home;
+                modifs &= ~Qt::MetaModifier;
+            }
+        } else if (code == KEYCODE_RIGHTARROW) {
+            s = QString();
+            code = Qt::Key_Right;
+            if (modifs & Qt::MetaModifier) {
+                code = Qt::Key_End;
+                modifs &= ~Qt::MetaModifier;
+            }
+        } else if (code == KEYCODE_UPARROW) {
+            s = QString();
+            code = Qt::Key_Up;
+            if (modifs & Qt::MetaModifier) {
+                code = Qt::Key_PageUp;
+                modifs &= ~Qt::MetaModifier;
+            }
+        } else if (code == KEYCODE_DOWNARROW) {
+            s = QString();
+            code = Qt::Key_Down;
+            if (modifs & Qt::MetaModifier) {
+                code = Qt::Key_PageDown;
+                modifs &= ~Qt::MetaModifier;
+            }
+        } else if (code == 'a' && modifs == Qt::ControlModifier) {
+            s = QString();
+            code = Qt::Key_Home;
+            modifs = 0;
+        } else
+            code = string.unicode()->toUpper().unicode();
+    } else {
+        //qDebug() << ">>>>>>>>> keyDown" << string;
+
+        if (string.startsWith(QLatin1Char('F')) && string.count() <= 3) {
+            s = s.mid(1);
+            int functionKey = s.toInt();
+            Q_ASSERT(functionKey >= 1 && functionKey <= 35);
+            code = Qt::Key_F1 + (functionKey - 1);
+        // map special keycode strings used by the tests to something that works for Qt/X11
+        } else if (string == QLatin1String("leftArrow")) {
+            s = QString();
+            code = Qt::Key_Left;
+        } else if (string == QLatin1String("rightArrow")) {
+            s = QString();
+            code = Qt::Key_Right;
+        } else if (string == QLatin1String("upArrow")) {
+            s = QString();
+            code = Qt::Key_Up;
+        } else if (string == QLatin1String("downArrow")) {
+            s = QString();
+            code = Qt::Key_Down;
+        } else if (string == QLatin1String("pageUp")) {
+            s = QString();
+            code = Qt::Key_PageUp;
+        } else if (string == QLatin1String("pageDown")) {
+            s = QString();
+            code = Qt::Key_PageDown;
+        } else if (string == QLatin1String("home")) {
+            s = QString();
+            code = Qt::Key_Home;
+        } else if (string == QLatin1String("end")) {
+            s = QString();
+            code = Qt::Key_End;
+        } else if (string == QLatin1String("delete")) {
+            s = QString();
+            code = Qt::Key_Delete;
+        }
+    }
+    QKeyEvent event(QEvent::KeyPress, code, modifs, s);
+    sendEvent(m_page, &event);
+    QKeyEvent event2(QEvent::KeyRelease, code, modifs, s);
+    sendEvent(m_page, &event2);
+}
+
+void EventSender::contextClick()
+{
+    QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier);
+    sendEvent(m_page, &event);
+    QMouseEvent event2(QEvent::MouseButtonRelease, m_mousePos, Qt::RightButton, Qt::RightButton, Qt::NoModifier);
+    sendEvent(m_page, &event2);
+
+    if (isGraphicsBased()) {
+        QGraphicsSceneContextMenuEvent ctxEvent(QEvent::GraphicsSceneContextMenu);
+        ctxEvent.setReason(QGraphicsSceneContextMenuEvent::Mouse);
+        ctxEvent.setPos(m_mousePos);
+        WebCore::WebViewGraphicsBased* view = qobject_cast<WebCore::WebViewGraphicsBased*>(m_page->view());
+        if (view)
+            sendEvent(view->graphicsView(), &ctxEvent);
+    } else {
+        QContextMenuEvent ctxEvent(QContextMenuEvent::Mouse, m_mousePos);
+        sendEvent(m_page->view(), &ctxEvent);
+    }
+}
+
+void EventSender::scheduleAsynchronousClick()
+{
+    QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier);
+    postEvent(m_page, event);
+    QMouseEvent* event2 = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, Qt::LeftButton, Qt::RightButton, Qt::NoModifier);
+    postEvent(m_page, event2);
+}
+
+void EventSender::addTouchPoint(int x, int y)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    // Use index to refer to the position in the vector that this touch
+    // is stored. We then create a unique id for the touch that will be
+    // passed into WebCore.
+    int index = m_touchPoints.count();
+    int id = m_touchPoints.isEmpty() ? 0 : m_touchPoints.last().id() + 1;
+    QTouchEvent::TouchPoint point(id);
+    m_touchPoints.append(point);
+    updateTouchPoint(index, x, y);
+    m_touchPoints[index].setState(Qt::TouchPointPressed);
+#endif
+}
+
+void EventSender::updateTouchPoint(int index, int x, int y)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    if (index < 0 || index >= m_touchPoints.count())
+        return;
+
+    QTouchEvent::TouchPoint &p = m_touchPoints[index];
+    p.setPos(QPointF(x, y));
+    p.setState(Qt::TouchPointMoved);
+#endif
+}
+
+void EventSender::setTouchModifier(const QString &modifier, bool enable)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    Qt::KeyboardModifier mod = Qt::NoModifier;
+    if (!modifier.compare(QLatin1String("shift"), Qt::CaseInsensitive))
+        mod = Qt::ShiftModifier;
+    else if (!modifier.compare(QLatin1String("alt"), Qt::CaseInsensitive))
+        mod = Qt::AltModifier;
+    else if (!modifier.compare(QLatin1String("meta"), Qt::CaseInsensitive))
+        mod = Qt::MetaModifier;
+    else if (!modifier.compare(QLatin1String("ctrl"), Qt::CaseInsensitive))
+        mod = Qt::ControlModifier;
+
+    if (enable)
+        m_touchModifiers |= mod;
+    else
+        m_touchModifiers &= ~mod;
+#endif
+}
+
+void EventSender::touchStart()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    if (!m_touchActive) {
+        sendTouchEvent(QEvent::TouchBegin);
+        m_touchActive = true;
+    } else
+        sendTouchEvent(QEvent::TouchUpdate);
+#endif
+}
+
+void EventSender::touchMove()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    sendTouchEvent(QEvent::TouchUpdate);
+#endif
+}
+
+void EventSender::touchEnd()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    for (int i = 0; i < m_touchPoints.count(); ++i)
+        if (m_touchPoints[i].state() != Qt::TouchPointReleased) {
+            sendTouchEvent(QEvent::TouchUpdate);
+            return;
+        }
+    sendTouchEvent(QEvent::TouchEnd);
+    m_touchActive = false;
+#endif
+}
+
+void EventSender::clearTouchPoints()
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    m_touchPoints.clear();
+    m_touchModifiers = Qt::KeyboardModifiers();
+    m_touchActive = false;
+#endif
+}
+
+void EventSender::releaseTouchPoint(int index)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    if (index < 0 || index >= m_touchPoints.count())
+        return;
+
+    m_touchPoints[index].setState(Qt::TouchPointReleased);
+#endif
+}
+
+void EventSender::sendTouchEvent(QEvent::Type type)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    QTouchEvent event(type, QTouchEvent::TouchScreen, m_touchModifiers);
+    event.setTouchPoints(m_touchPoints);
+    sendEvent(m_page, &event);
+    QList<QTouchEvent::TouchPoint>::Iterator it = m_touchPoints.begin();
+    while (it != m_touchPoints.end()) {
+        if (it->state() == Qt::TouchPointReleased)
+            it = m_touchPoints.erase(it);
+        else {
+            it->setState(Qt::TouchPointStationary);
+            ++it;
+        }
+    }
+#endif
+}
+
+void EventSender::zoomPageIn()
+{
+    if (QWebFrame* frame = m_page->mainFrame())
+        frame->setZoomFactor(frame->zoomFactor() * ZOOM_STEP);
+}
+
+void EventSender::zoomPageOut()
+{
+    if (QWebFrame* frame = m_page->mainFrame())
+        frame->setZoomFactor(frame->zoomFactor() / ZOOM_STEP);
+}
+
+void EventSender::textZoomIn()
+{
+    if (QWebFrame* frame = m_page->mainFrame())
+        frame->setTextSizeMultiplier(frame->textSizeMultiplier() * ZOOM_STEP);
+}
+
+void EventSender::textZoomOut()
+{
+    if (QWebFrame* frame = m_page->mainFrame())
+        frame->setTextSizeMultiplier(frame->textSizeMultiplier() / ZOOM_STEP);
+}
+
+QWebFrame* EventSender::frameUnderMouse() const
+{
+    QWebFrame* frame = m_page->mainFrame();
+
+redo:
+    QList<QWebFrame*> children = frame->childFrames();
+    for (int i = 0; i < children.size(); ++i) {
+        if (children.at(i)->geometry().contains(m_mousePos)) {
+            frame = children.at(i);
+            goto redo;
+        }
+    }
+    if (frame->geometry().contains(m_mousePos))
+        return frame;
+    return 0;
+}
+
+void EventSender::sendOrQueueEvent(QEvent* event)
+{
+    // Mouse move events are queued if 
+    // 1. A previous event was queued.
+    // 2. A delay was set-up by leapForward().
+    // 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. 
+    // To be safe and avoid a deadlock, this event is queued.
+    if (endOfQueue == startOfQueue && !eventQueue[endOfQueue].m_delay && (!(m_mouseButtonPressed && (m_eventLoop && event->type() == QEvent::MouseButtonRelease)))) {
+        sendEvent(m_page->view(), event);
+        delete event;
+        return;
+    }
+    eventQueue[endOfQueue++].m_event = event;
+    eventQueue[endOfQueue].m_delay = 0;
+    replaySavedEvents(event->type() != QEvent::MouseMove);
+}
+
+void EventSender::replaySavedEvents(bool flush)
+{
+    if (startOfQueue < endOfQueue) {
+        // First send all the events that are ready to be sent
+        while (!eventQueue[startOfQueue].m_delay && startOfQueue < endOfQueue) {
+            QEvent* ev = eventQueue[startOfQueue++].m_event;
+            postEvent(m_page->view(), ev);
+        }
+        if (startOfQueue == endOfQueue) {
+            // Reset the queue
+            startOfQueue = 0;
+            endOfQueue = 0;
+        } else {
+            QTest::qWait(eventQueue[startOfQueue].m_delay);
+            eventQueue[startOfQueue].m_delay = 0;
+        }
+    }
+    if (!flush)
+        return;
+
+    // Send a marker event, it will tell us when it is safe to exit the new event loop
+    QEvent* drtEvent = new QEvent((QEvent::Type)DRT_MESSAGE_DONE);
+    QApplication::postEvent(m_page->view(), drtEvent);
+
+    // Start an event loop for async handling of Drag & Drop
+    m_eventLoop = new QEventLoop;
+    m_eventLoop->exec();
+    delete m_eventLoop;
+    m_eventLoop = 0;
+}
+
+bool EventSender::eventFilter(QObject* watched, QEvent* event)
+{
+    if (watched != m_page->view())
+        return false;
+    switch (event->type()) {
+    case QEvent::Leave:
+        return true;
+    case QEvent::MouseButtonPress:
+    case QEvent::GraphicsSceneMousePress:
+        m_mouseButtonPressed = true;
+        break;
+    case QEvent::MouseMove:
+    case QEvent::GraphicsSceneMouseMove:
+        if (m_mouseButtonPressed)
+            m_drag = true;
+        break;
+    case QEvent::MouseButtonRelease:
+    case QEvent::GraphicsSceneMouseRelease:
+        m_mouseButtonPressed = false;
+        m_drag = false;
+        break;
+    case DRT_MESSAGE_DONE:
+        m_eventLoop->exit();
+        return true;
+    }
+    return false;
+}
+
+void EventSender::timerEvent(QTimerEvent* ev)
+{
+    m_clickTimer.stop();
+}
+
+QGraphicsSceneMouseEvent* EventSender::createGraphicsSceneMouseEvent(QEvent::Type type, const QPoint& pos, const QPoint& screenPos, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
+{
+    QGraphicsSceneMouseEvent* event;
+    event = new QGraphicsSceneMouseEvent(type);
+    event->setPos(pos);
+    event->setScreenPos(screenPos);
+    event->setButton(button);
+    event->setButtons(buttons);
+    event->setModifiers(modifiers);
+
+    return event;
+}
+
+QGraphicsSceneWheelEvent* EventSender::createGraphicsSceneWheelEvent(QEvent::Type type, const QPoint& pos, const QPoint& screenPos, int delta, Qt::KeyboardModifiers modifiers, Qt::Orientation orientation)
+{
+    QGraphicsSceneWheelEvent* event;
+    event = new QGraphicsSceneWheelEvent(type);
+    event->setPos(pos);
+    event->setScreenPos(screenPos);
+    event->setDelta(delta);
+    event->setModifiers(modifiers);
+    event->setOrientation(orientation);
+
+    return event;
+}
+
+void EventSender::sendEvent(QObject* receiver, QEvent* event)
+{
+    if (WebCore::WebViewGraphicsBased* view = qobject_cast<WebCore::WebViewGraphicsBased*>(receiver))
+        view->scene()->sendEvent(view->graphicsView(), event);
+    else
+        QApplication::sendEvent(receiver, event);
+}
+
+void EventSender::postEvent(QObject* receiver, QEvent* event)
+{
+    // QGraphicsScene does not have a postEvent method, so send the event in this case
+    // and delete it after that.
+    if (WebCore::WebViewGraphicsBased* view = qobject_cast<WebCore::WebViewGraphicsBased*>(receiver)) {
+        view->scene()->sendEvent(view->graphicsView(), event);
+        delete event;
+    } else
+        QApplication::postEvent(receiver, event); // event deleted by the system
+}