1/*
2 * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include "QtWebPageEventHandler.h"
23
24#include "NativeWebKeyboardEvent.h"
25#include "NativeWebMouseEvent.h"
26#include "NativeWebWheelEvent.h"
27#include "PageViewportControllerClientQt.h"
28#include "WebPageProxy.h"
29#include "qquickwebpage_p.h"
30#include "qquickwebview_p.h"
31#include <QCursor>
32#include <QDrag>
33#include <QGuiApplication>
34#include <QInputEvent>
35#include <QInputMethod>
36#include <QMimeData>
37#include <QMouseEvent>
38#include <QQuickWindow>
39#include <QStyleHints>
40#include <QTextFormat>
41#include <QTouchEvent>
42#include <QTransform>
43#include <WebCore/DragData.h>
44#include <WebCore/Editor.h>
45
46using namespace WebCore;
47
48namespace WebKit {
49
50static inline Qt::DropAction dragOperationToDropAction(unsigned dragOperation)
51{
52    Qt::DropAction result = Qt::IgnoreAction;
53    if (dragOperation & DragOperationCopy)
54        result = Qt::CopyAction;
55    else if (dragOperation & DragOperationMove)
56        result = Qt::MoveAction;
57    else if (dragOperation & DragOperationGeneric)
58        result = Qt::MoveAction;
59    else if (dragOperation & DragOperationLink)
60        result = Qt::LinkAction;
61    return result;
62}
63
64static inline Qt::DropActions dragOperationToDropActions(unsigned dragOperations)
65{
66    Qt::DropActions result = Qt::IgnoreAction;
67    if (dragOperations & DragOperationCopy)
68        result |= Qt::CopyAction;
69    if (dragOperations & DragOperationMove)
70        result |= Qt::MoveAction;
71    if (dragOperations & DragOperationGeneric)
72        result |= Qt::MoveAction;
73    if (dragOperations & DragOperationLink)
74        result |= Qt::LinkAction;
75    return result;
76}
77
78static inline WebCore::DragOperation dropActionToDragOperation(Qt::DropActions actions)
79{
80    unsigned result = 0;
81    if (actions & Qt::CopyAction)
82        result |= DragOperationCopy;
83    if (actions & Qt::MoveAction)
84        result |= (DragOperationMove | DragOperationGeneric);
85    if (actions & Qt::LinkAction)
86        result |= DragOperationLink;
87    if (result == (DragOperationCopy | DragOperationMove | DragOperationGeneric | DragOperationLink))
88        result = DragOperationEvery;
89    return (DragOperation)result;
90}
91
92QtWebPageEventHandler::QtWebPageEventHandler(WKPageRef pageRef, QQuickWebPage* qmlWebPage, QQuickWebView* qmlWebView)
93    : m_webPageProxy(toImpl(pageRef))
94    , m_viewportController(0)
95    , m_panGestureRecognizer(this)
96    , m_pinchGestureRecognizer(this)
97    , m_tapGestureRecognizer(this)
98    , m_webPage(qmlWebPage)
99    , m_webView(qmlWebView)
100    , m_previousClickButton(Qt::NoButton)
101    , m_clickCount(0)
102    , m_postponeTextInputStateChanged(false)
103    , m_isTapHighlightActive(false)
104    , m_isMouseButtonPressed(false)
105{
106    connect(qApp->inputMethod(), SIGNAL(visibleChanged()), this, SLOT(inputPanelVisibleChanged()));
107}
108
109QtWebPageEventHandler::~QtWebPageEventHandler()
110{
111    disconnect(qApp->inputMethod(), SIGNAL(visibleChanged()), this, SLOT(inputPanelVisibleChanged()));
112}
113
114void QtWebPageEventHandler::handleMouseMoveEvent(QMouseEvent* ev)
115{
116    // For some reason mouse press results in mouse hover (which is
117    // converted to mouse move for WebKit). We ignore these hover
118    // events by comparing lastPos with newPos.
119    // NOTE: lastPos from the event always comes empty, so we work
120    // around that here.
121    static QPointF lastPos = QPointF();
122    QTransform fromItemTransform = m_webPage->transformFromItem();
123    QPointF webPagePoint = fromItemTransform.map(ev->localPos());
124    ev->accept();
125    if (lastPos == webPagePoint)
126        return;
127    lastPos = webPagePoint;
128
129    m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, /*eventClickCount*/ 0));
130}
131
132void QtWebPageEventHandler::handleMousePressEvent(QMouseEvent* ev)
133{
134    QTransform fromItemTransform = m_webPage->transformFromItem();
135    QPointF webPagePoint = fromItemTransform.map(ev->localPos());
136
137    if (m_clickTimer.isActive()
138        && m_previousClickButton == ev->button()
139        && (webPagePoint - m_lastClick).manhattanLength() < qApp->styleHints()->startDragDistance()) {
140        m_clickCount++;
141    } else {
142        m_clickCount = 1;
143        m_previousClickButton = ev->button();
144    }
145
146    ev->accept();
147    m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, m_clickCount));
148
149    m_lastClick = webPagePoint;
150    m_clickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), this);
151}
152
153void QtWebPageEventHandler::handleMouseReleaseEvent(QMouseEvent* ev)
154{
155    ev->accept();
156    QTransform fromItemTransform = m_webPage->transformFromItem();
157    m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, /*eventClickCount*/ 0));
158}
159
160void QtWebPageEventHandler::handleWheelEvent(QWheelEvent* ev)
161{
162    QTransform fromItemTransform = m_webPage->transformFromItem();
163    m_webPageProxy->handleWheelEvent(NativeWebWheelEvent(ev, fromItemTransform));
164}
165
166void QtWebPageEventHandler::handleHoverLeaveEvent(QHoverEvent* ev)
167{
168    // To get the correct behavior of mouseout, we need to turn the Leave event of our webview into a mouse move
169    // to a very far region.
170    QHoverEvent fakeEvent(QEvent::HoverMove, QPoint(INT_MIN, INT_MIN), ev->oldPosF());
171    fakeEvent.setTimestamp(ev->timestamp());
172    // This will apply the transform on the event.
173    handleHoverMoveEvent(&fakeEvent);
174}
175
176void QtWebPageEventHandler::handleHoverMoveEvent(QHoverEvent* ev)
177{
178    QMouseEvent me(QEvent::MouseMove, ev->posF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
179    me.setAccepted(ev->isAccepted());
180    me.setTimestamp(ev->timestamp());
181    // This will apply the transform on the event.
182    handleMouseMoveEvent(&me);
183}
184
185void QtWebPageEventHandler::handleDragEnterEvent(QDragEnterEvent* ev)
186{
187    m_webPageProxy->resetDragOperation();
188    QTransform fromItemTransform = m_webPage->transformFromItem();
189    // FIXME: Should not use QCursor::pos()
190    DragData dragData(ev->mimeData(), fromItemTransform.map(ev->pos()), QCursor::pos(), dropActionToDragOperation(ev->possibleActions()));
191    m_webPageProxy->dragEntered(&dragData);
192    ev->acceptProposedAction();
193}
194
195void QtWebPageEventHandler::handleDragLeaveEvent(QDragLeaveEvent* ev)
196{
197    bool accepted = ev->isAccepted();
198
199    // FIXME: Should not use QCursor::pos()
200    DragData dragData(0, IntPoint(), QCursor::pos(), DragOperationNone);
201    m_webPageProxy->dragExited(&dragData);
202    m_webPageProxy->resetDragOperation();
203
204    ev->setAccepted(accepted);
205}
206
207void QtWebPageEventHandler::handleDragMoveEvent(QDragMoveEvent* ev)
208{
209    bool accepted = ev->isAccepted();
210
211    QTransform fromItemTransform = m_webPage->transformFromItem();
212    // FIXME: Should not use QCursor::pos()
213    DragData dragData(ev->mimeData(), fromItemTransform.map(ev->pos()), QCursor::pos(), dropActionToDragOperation(ev->possibleActions()));
214    m_webPageProxy->dragUpdated(&dragData);
215    ev->setDropAction(dragOperationToDropAction(m_webPageProxy->dragSession().operation));
216    if (m_webPageProxy->dragSession().operation != DragOperationNone)
217        ev->accept();
218
219    ev->setAccepted(accepted);
220}
221
222void QtWebPageEventHandler::handleDropEvent(QDropEvent* ev)
223{
224    bool accepted = ev->isAccepted();
225    QTransform fromItemTransform = m_webPage->transformFromItem();
226    // FIXME: Should not use QCursor::pos()
227    DragData dragData(ev->mimeData(), fromItemTransform.map(ev->pos()), QCursor::pos(), dropActionToDragOperation(ev->possibleActions()));
228    SandboxExtension::Handle handle;
229    SandboxExtension::HandleArray sandboxExtensionForUpload;
230    m_webPageProxy->performDrag(&dragData, String(), handle, sandboxExtensionForUpload);
231    ev->setDropAction(dragOperationToDropAction(m_webPageProxy->dragSession().operation));
232    ev->accept();
233
234    ev->setAccepted(accepted);
235}
236
237void QtWebPageEventHandler::activateTapHighlight(const QTouchEvent::TouchPoint& point)
238{
239#if ENABLE(TOUCH_EVENTS)
240    ASSERT(!point.pos().toPoint().isNull());
241    ASSERT(!m_isTapHighlightActive);
242    m_isTapHighlightActive = true;
243    QTransform fromItemTransform = m_webPage->transformFromItem();
244    m_webPageProxy->handlePotentialActivation(IntPoint(fromItemTransform.map(point.pos()).toPoint()), IntSize(point.rect().size().toSize()));
245#else
246    Q_UNUSED(point);
247#endif
248}
249
250void QtWebPageEventHandler::deactivateTapHighlight()
251{
252#if ENABLE(TOUCH_EVENTS)
253    if (!m_isTapHighlightActive)
254        return;
255
256    // An empty point deactivates the highlighting.
257    m_webPageProxy->handlePotentialActivation(IntPoint(), IntSize());
258    m_isTapHighlightActive = false;
259#endif
260}
261
262void QtWebPageEventHandler::handleSingleTapEvent(const QTouchEvent::TouchPoint& point)
263{
264    deactivateTapHighlight();
265    m_postponeTextInputStateChanged = true;
266
267    QTransform fromItemTransform = m_webPage->transformFromItem();
268    WebGestureEvent gesture(WebEvent::GestureSingleTap, fromItemTransform.map(point.pos()).toPoint(), point.screenPos().toPoint(), WebEvent::Modifiers(0), 0, IntSize(point.rect().size().toSize()), FloatPoint(0, 0));
269    m_webPageProxy->handleGestureEvent(gesture);
270}
271
272void QtWebPageEventHandler::handleDoubleTapEvent(const QTouchEvent::TouchPoint& point)
273{
274    if (!m_webView->isInteractive())
275        return;
276
277    deactivateTapHighlight();
278    QTransform fromItemTransform = m_webPage->transformFromItem();
279    m_webPageProxy->findZoomableAreaForPoint(fromItemTransform.map(point.pos()).toPoint(), IntSize(point.rect().size().toSize()));
280}
281
282void QtWebPageEventHandler::timerEvent(QTimerEvent* ev)
283{
284    int timerId = ev->timerId();
285    if (timerId == m_clickTimer.timerId())
286        m_clickTimer.stop();
287    else
288        QObject::timerEvent(ev);
289}
290
291void QtWebPageEventHandler::handleKeyPressEvent(QKeyEvent* ev)
292{
293    m_webPageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(ev));
294}
295
296void QtWebPageEventHandler::handleKeyReleaseEvent(QKeyEvent* ev)
297{
298    m_webPageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(ev));
299}
300
301void QtWebPageEventHandler::handleFocusInEvent(QFocusEvent*)
302{
303    m_webPageProxy->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
304}
305
306void QtWebPageEventHandler::handleFocusLost()
307{
308    m_webPageProxy->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
309}
310
311void QtWebPageEventHandler::setViewportController(PageViewportControllerClientQt* controller)
312{
313    m_viewportController = controller;
314}
315
316void QtWebPageEventHandler::handleInputMethodEvent(QInputMethodEvent* ev)
317{
318    QString commit = ev->commitString();
319    QString composition = ev->preeditString();
320
321    int replacementStart = ev->replacementStart();
322    int replacementLength = ev->replacementLength();
323
324    // NOTE: We might want to handle events of one char as special
325    // and resend them as key events to make web site completion work.
326
327    int cursorPositionWithinComposition = 0;
328
329    Vector<CompositionUnderline> underlines;
330
331    for (int i = 0; i < ev->attributes().size(); ++i) {
332        const QInputMethodEvent::Attribute& attr = ev->attributes().at(i);
333        switch (attr.type) {
334        case QInputMethodEvent::TextFormat: {
335            if (composition.isEmpty())
336                break;
337
338            QTextCharFormat textCharFormat = attr.value.value<QTextFormat>().toCharFormat();
339            QColor qcolor = textCharFormat.underlineColor();
340            Color color = makeRGBA(qcolor.red(), qcolor.green(), qcolor.blue(), qcolor.alpha());
341            int start = qMin(attr.start, (attr.start + attr.length));
342            int end = qMax(attr.start, (attr.start + attr.length));
343            underlines.append(CompositionUnderline(start, end, color, false));
344            break;
345        }
346        case QInputMethodEvent::Cursor:
347            if (attr.length)
348                cursorPositionWithinComposition = attr.start;
349            break;
350        // Selection is handled further down.
351        default: break;
352        }
353    }
354
355    if (composition.isEmpty()) {
356        int selectionStart = -1;
357        int selectionLength = 0;
358        for (int i = 0; i < ev->attributes().size(); ++i) {
359            const QInputMethodEvent::Attribute& attr = ev->attributes().at(i);
360            if (attr.type == QInputMethodEvent::Selection) {
361                selectionStart = attr.start;
362                selectionLength = attr.length;
363
364                ASSERT(selectionStart >= 0);
365                ASSERT(selectionLength >= 0);
366                break;
367            }
368        }
369
370        m_webPageProxy->confirmComposition(commit, selectionStart, selectionLength);
371    } else {
372        ASSERT(cursorPositionWithinComposition >= 0);
373        ASSERT(replacementStart >= 0);
374
375        m_webPageProxy->setComposition(composition, underlines,
376            cursorPositionWithinComposition, cursorPositionWithinComposition,
377            replacementStart, replacementLength);
378    }
379
380    ev->accept();
381}
382
383void QtWebPageEventHandler::handleTouchEvent(QTouchEvent* event)
384{
385#if ENABLE(TOUCH_EVENTS)
386    QTransform fromItemTransform = m_webPage->transformFromItem();
387    m_webPageProxy->handleTouchEvent(NativeWebTouchEvent(event, fromItemTransform));
388    event->accept();
389#else
390    ASSERT_NOT_REACHED();
391    event->ignore();
392#endif
393}
394
395void QtWebPageEventHandler::resetGestureRecognizers()
396{
397    m_panGestureRecognizer.cancel();
398    m_pinchGestureRecognizer.cancel();
399    m_tapGestureRecognizer.cancel();
400}
401
402static void setInputPanelVisible(bool visible)
403{
404    if (qApp->inputMethod()->isVisible() == visible)
405        return;
406
407    qApp->inputMethod()->setVisible(visible);
408}
409
410void QtWebPageEventHandler::inputPanelVisibleChanged()
411{
412    if (!m_viewportController)
413        return;
414
415    // We only respond to the input panel becoming visible.
416    if (!m_webView->hasActiveFocus() || !qApp->inputMethod()->isVisible())
417        return;
418
419    const EditorState& editor = m_webPageProxy->editorState();
420    if (editor.isContentEditable)
421        m_viewportController->focusEditableArea(QRectF(editor.cursorRect), QRectF(editor.editorRect));
422}
423
424void QtWebPageEventHandler::updateTextInputState()
425{
426    if (m_postponeTextInputStateChanged)
427        return;
428
429    const EditorState& editor = m_webPageProxy->editorState();
430
431    m_webView->setFlag(QQuickItem::ItemAcceptsInputMethod, editor.isContentEditable);
432
433    if (!m_webView->hasActiveFocus())
434        return;
435
436    qApp->inputMethod()->update(Qt::ImQueryInput | Qt::ImEnabled | Qt::ImHints);
437
438    setInputPanelVisible(editor.isContentEditable);
439}
440
441void QtWebPageEventHandler::handleWillSetInputMethodState()
442{
443    if (qApp->inputMethod()->isVisible())
444        qApp->inputMethod()->commit();
445}
446
447void QtWebPageEventHandler::doneWithGestureEvent(const WebGestureEvent& event, bool wasEventHandled)
448{
449    if (event.type() != WebEvent::GestureSingleTap)
450        return;
451
452    m_postponeTextInputStateChanged = false;
453
454    if (!wasEventHandled || !m_webView->hasActiveFocus())
455        return;
456
457    updateTextInputState();
458}
459
460void QtWebPageEventHandler::handleInputEvent(const QInputEvent* event)
461{
462    if (m_viewportController) {
463        switch (event->type()) {
464        case QEvent::MouseButtonPress:
465        case QEvent::TouchBegin:
466            m_viewportController->touchBegin();
467
468            // The page viewport controller might still be animating kinetic scrolling or a scale animation
469            // such as double-tap to zoom or the bounce back effect. A touch stops the kinetic scrolling
470            // where as it does not stop the scale animation.
471            // The gesture recognizer stops the kinetic scrolling animation if needed.
472            break;
473        case QEvent::MouseMove:
474        case QEvent::TouchUpdate:
475            // The scale animation can only be interrupted by a pinch gesture, which will then take over.
476            if (m_viewportController->scaleAnimationActive() && m_pinchGestureRecognizer.isRecognized())
477                m_viewportController->interruptScaleAnimation();
478            break;
479        case QEvent::MouseButtonRelease:
480        case QEvent::TouchEnd:
481            m_viewportController->touchEnd();
482            break;
483        default:
484            break;
485        }
486
487        // If the scale animation is active we don't pass the event to the recognizers. In the future
488        // we would want to queue the event here and repost then when the animation ends.
489        if (m_viewportController->scaleAnimationActive())
490            return;
491    }
492
493    bool isMouseEvent = false;
494
495    switch (event->type()) {
496    case QEvent::MouseButtonPress:
497        isMouseEvent = true;
498        m_isMouseButtonPressed = true;
499        break;
500    case QEvent::MouseMove:
501        if (!m_isMouseButtonPressed)
502            return;
503        isMouseEvent = true;
504        break;
505    case QEvent::MouseButtonRelease:
506        isMouseEvent = true;
507        m_isMouseButtonPressed = false;
508        break;
509    case QEvent::MouseButtonDblClick:
510        return;
511    default:
512        break;
513    }
514
515    QList<QTouchEvent::TouchPoint> activeTouchPoints;
516    QTouchEvent::TouchPoint currentTouchPoint;
517    qint64 eventTimestampMillis = event->timestamp();
518    int touchPointCount = 0;
519
520    if (!isMouseEvent) {
521        const QTouchEvent* touchEvent = static_cast<const QTouchEvent*>(event);
522        const QList<QTouchEvent::TouchPoint>& touchPoints = touchEvent->touchPoints();
523        currentTouchPoint = touchPoints.first();
524        touchPointCount = touchPoints.size();
525        activeTouchPoints.reserve(touchPointCount);
526
527        for (int i = 0; i < touchPointCount; ++i) {
528            if (touchPoints[i].state() != Qt::TouchPointReleased)
529                activeTouchPoints << touchPoints[i];
530        }
531    } else {
532        const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
533        touchPointCount = 1;
534
535        // Make a distinction between mouse events on the basis of pressed buttons.
536        currentTouchPoint.setId(mouseEvent->buttons());
537        currentTouchPoint.setScreenPos(mouseEvent->screenPos());
538        // For tap gesture hit testing the float touch rect is translated to
539        // an int rect representing the radius of the touch point (size/2),
540        // thus the touch rect has to have a size of at least 2.
541        currentTouchPoint.setRect(QRectF(mouseEvent->localPos(), QSizeF(2, 2)));
542
543        if (m_isMouseButtonPressed)
544            activeTouchPoints << currentTouchPoint;
545    }
546
547    const int activeTouchPointCount = activeTouchPoints.size();
548
549    if (!activeTouchPointCount) {
550        if (touchPointCount == 1) {
551            // No active touch points, one finger released.
552            if (m_panGestureRecognizer.isRecognized())
553                m_panGestureRecognizer.finish(currentTouchPoint, eventTimestampMillis);
554            else {
555                // The events did not result in a pan gesture.
556                m_panGestureRecognizer.cancel();
557                m_tapGestureRecognizer.finish(currentTouchPoint);
558            }
559
560        } else
561            m_pinchGestureRecognizer.finish();
562
563        // Early return since this was a touch-end event.
564        return;
565    } else if (activeTouchPointCount == 1) {
566        // If the pinch gesture recognizer was previously in active state the content might
567        // be out of valid zoom boundaries, thus we need to finish the pinch gesture here.
568        // This will resume the content to valid zoom levels before the pan gesture is started.
569        m_pinchGestureRecognizer.finish();
570        m_panGestureRecognizer.update(activeTouchPoints.first(), eventTimestampMillis);
571    } else if (activeTouchPointCount == 2) {
572        m_panGestureRecognizer.cancel();
573        m_pinchGestureRecognizer.update(activeTouchPoints.first(), activeTouchPoints.last());
574    }
575
576    if (m_panGestureRecognizer.isRecognized() || m_pinchGestureRecognizer.isRecognized() || m_webView->isMoving())
577        m_tapGestureRecognizer.cancel();
578    else if (touchPointCount == 1)
579        m_tapGestureRecognizer.update(currentTouchPoint);
580
581}
582
583#if ENABLE(TOUCH_EVENTS)
584void QtWebPageEventHandler::doneWithTouchEvent(const NativeWebTouchEvent& event, bool wasEventHandled)
585{
586    if (wasEventHandled || event.type() == WebEvent::TouchCancel) {
587        m_panGestureRecognizer.cancel();
588        m_pinchGestureRecognizer.cancel();
589        if (event.type() != WebEvent::TouchMove)
590            m_tapGestureRecognizer.cancel();
591        return;
592    }
593
594    const QTouchEvent* ev = event.nativeEvent();
595
596    handleInputEvent(ev);
597}
598#endif
599
600void QtWebPageEventHandler::didFindZoomableArea(const IntPoint& target, const IntRect& area)
601{
602    if (!m_viewportController)
603        return;
604
605    // FIXME: As the find method might not respond immediately during load etc,
606    // we should ignore all but the latest request.
607    m_viewportController->zoomToAreaGestureEnded(QPointF(target), QRectF(area));
608}
609
610void QtWebPageEventHandler::startDrag(const WebCore::DragData& dragData, PassRefPtr<ShareableBitmap> dragImage)
611{
612    QImage dragQImage;
613    if (dragImage)
614        dragQImage = dragImage->createQImage();
615    else if (dragData.platformData() && dragData.platformData()->hasImage())
616        dragQImage = qvariant_cast<QImage>(dragData.platformData()->imageData());
617
618    DragOperation dragOperationMask = dragData.draggingSourceOperationMask();
619    QMimeData* mimeData = const_cast<QMimeData*>(dragData.platformData());
620    Qt::DropActions supportedDropActions = dragOperationToDropActions(dragOperationMask);
621
622    QPoint clientPosition;
623    QPoint globalPosition;
624    Qt::DropAction actualDropAction = Qt::IgnoreAction;
625
626    if (QWindow* window = m_webPage->window()) {
627        QDrag* drag = new QDrag(window);
628        drag->setPixmap(QPixmap::fromImage(dragQImage));
629        drag->setMimeData(mimeData);
630        actualDropAction = drag->exec(supportedDropActions);
631        globalPosition = QCursor::pos();
632        clientPosition = window->mapFromGlobal(globalPosition);
633    }
634
635    m_webPageProxy->dragEnded(clientPosition, globalPosition, dropActionToDragOperation(actualDropAction));
636}
637
638} // namespace WebKit
639
640#include "moc_QtWebPageEventHandler.cpp"
641
642