QtWebPageEventHandler.cpp [plain text]
#include "config.h"
#include "QtWebPageEventHandler.h"
#include "NativeWebKeyboardEvent.h"
#include "NativeWebMouseEvent.h"
#include "NativeWebWheelEvent.h"
#include "QtViewportInteractionEngine.h"
#include "WebPageProxy.h"
#include "qquickwebpage_p.h"
#include "qquickwebview_p.h"
#include <QCursor>
#include <QDrag>
#include <QGuiApplication>
#include <QInputPanel>
#include <QMimeData>
#include <QtQuick/QQuickCanvas>
#include <QStyleHints>
#include <QTextFormat>
#include <QTouchEvent>
#include <QTransform>
#include <WebCore/DragData.h>
#include <WebCore/Editor.h>
using namespace WebCore;
namespace WebKit {
static inline Qt::DropAction dragOperationToDropAction(unsigned dragOperation)
{
Qt::DropAction result = Qt::IgnoreAction;
if (dragOperation & DragOperationCopy)
result = Qt::CopyAction;
else if (dragOperation & DragOperationMove)
result = Qt::MoveAction;
else if (dragOperation & DragOperationGeneric)
result = Qt::MoveAction;
else if (dragOperation & DragOperationLink)
result = Qt::LinkAction;
return result;
}
static inline Qt::DropActions dragOperationToDropActions(unsigned dragOperations)
{
Qt::DropActions result = Qt::IgnoreAction;
if (dragOperations & DragOperationCopy)
result |= Qt::CopyAction;
if (dragOperations & DragOperationMove)
result |= Qt::MoveAction;
if (dragOperations & DragOperationGeneric)
result |= Qt::MoveAction;
if (dragOperations & DragOperationLink)
result |= Qt::LinkAction;
return result;
}
static inline WebCore::DragOperation dropActionToDragOperation(Qt::DropActions actions)
{
unsigned result = 0;
if (actions & Qt::CopyAction)
result |= DragOperationCopy;
if (actions & Qt::MoveAction)
result |= (DragOperationMove | DragOperationGeneric);
if (actions & Qt::LinkAction)
result |= DragOperationLink;
if (result == (DragOperationCopy | DragOperationMove | DragOperationGeneric | DragOperationLink))
result = DragOperationEvery;
return (DragOperation)result;
}
QtWebPageEventHandler::QtWebPageEventHandler(WKPageRef pageRef, QQuickWebPage* qmlWebPage, QQuickWebView* qmlWebView)
: m_webPageProxy(toImpl(pageRef))
, m_interactionEngine(0)
, m_panGestureRecognizer(this)
, m_pinchGestureRecognizer(this)
, m_tapGestureRecognizer(this)
, m_webPage(qmlWebPage)
, m_webView(qmlWebView)
, m_previousClickButton(Qt::NoButton)
, m_clickCount(0)
, m_postponeTextInputStateChanged(false)
{
connect(qApp->inputPanel(), SIGNAL(visibleChanged()), this, SLOT(inputPanelVisibleChanged()));
}
QtWebPageEventHandler::~QtWebPageEventHandler()
{
disconnect(qApp->inputPanel(), SIGNAL(visibleChanged()), this, SLOT(inputPanelVisibleChanged()));
}
void QtWebPageEventHandler::handleMouseMoveEvent(QMouseEvent* ev)
{
static QPointF lastPos = QPointF();
QTransform fromItemTransform = m_webPage->transformFromItem();
QPointF webPagePoint = fromItemTransform.map(ev->localPos());
if (lastPos == webPagePoint)
return;
lastPos = webPagePoint;
m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, 0));
}
void QtWebPageEventHandler::handleMousePressEvent(QMouseEvent* ev)
{
QTransform fromItemTransform = m_webPage->transformFromItem();
QPointF webPagePoint = fromItemTransform.map(ev->localPos());
if (m_clickTimer.isActive()
&& m_previousClickButton == ev->button()
&& (webPagePoint - m_lastClick).manhattanLength() < qApp->styleHints()->startDragDistance()) {
m_clickCount++;
} else {
m_clickCount = 1;
m_previousClickButton = ev->button();
}
m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, m_clickCount));
m_lastClick = webPagePoint;
m_clickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), this);
}
void QtWebPageEventHandler::handleMouseReleaseEvent(QMouseEvent* ev)
{
QTransform fromItemTransform = m_webPage->transformFromItem();
m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, 0));
}
void QtWebPageEventHandler::handleWheelEvent(QWheelEvent* ev)
{
QTransform fromItemTransform = m_webPage->transformFromItem();
m_webPageProxy->handleWheelEvent(NativeWebWheelEvent(ev, fromItemTransform));
if (m_interactionEngine)
m_interactionEngine->wheelEvent(ev);
}
void QtWebPageEventHandler::handleHoverLeaveEvent(QHoverEvent* ev)
{
QTransform fromItemTransform = m_webPage->transformFromItem();
QHoverEvent fakeEvent(QEvent::HoverMove, QPoint(INT_MIN, INT_MIN), fromItemTransform.map(ev->oldPosF()));
fakeEvent.setTimestamp(ev->timestamp());
handleHoverMoveEvent(&fakeEvent);
}
void QtWebPageEventHandler::handleHoverMoveEvent(QHoverEvent* ev)
{
QTransform fromItemTransform = m_webPage->transformFromItem();
QMouseEvent me(QEvent::MouseMove, fromItemTransform.map(ev->posF()), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
me.setAccepted(ev->isAccepted());
me.setTimestamp(ev->timestamp());
handleMouseMoveEvent(&me);
}
void QtWebPageEventHandler::handleDragEnterEvent(QDragEnterEvent* ev)
{
m_webPageProxy->resetDragOperation();
QTransform fromItemTransform = m_webPage->transformFromItem();
DragData dragData(ev->mimeData(), fromItemTransform.map(ev->pos()), QCursor::pos(), dropActionToDragOperation(ev->possibleActions()));
m_webPageProxy->dragEntered(&dragData);
ev->acceptProposedAction();
}
void QtWebPageEventHandler::handleDragLeaveEvent(QDragLeaveEvent* ev)
{
bool accepted = ev->isAccepted();
DragData dragData(0, IntPoint(), QCursor::pos(), DragOperationNone);
m_webPageProxy->dragExited(&dragData);
m_webPageProxy->resetDragOperation();
ev->setAccepted(accepted);
}
void QtWebPageEventHandler::handleDragMoveEvent(QDragMoveEvent* ev)
{
bool accepted = ev->isAccepted();
QTransform fromItemTransform = m_webPage->transformFromItem();
DragData dragData(ev->mimeData(), fromItemTransform.map(ev->pos()), QCursor::pos(), dropActionToDragOperation(ev->possibleActions()));
m_webPageProxy->dragUpdated(&dragData);
ev->setDropAction(dragOperationToDropAction(m_webPageProxy->dragSession().operation));
if (m_webPageProxy->dragSession().operation != DragOperationNone)
ev->accept();
ev->setAccepted(accepted);
}
void QtWebPageEventHandler::handleDropEvent(QDropEvent* ev)
{
bool accepted = ev->isAccepted();
QTransform fromItemTransform = m_webPage->transformFromItem();
DragData dragData(ev->mimeData(), fromItemTransform.map(ev->pos()), QCursor::pos(), dropActionToDragOperation(ev->possibleActions()));
SandboxExtension::Handle handle;
SandboxExtension::HandleArray sandboxExtensionForUpload;
m_webPageProxy->performDrag(&dragData, String(), handle, sandboxExtensionForUpload);
ev->setDropAction(dragOperationToDropAction(m_webPageProxy->dragSession().operation));
ev->accept();
ev->setAccepted(accepted);
}
void QtWebPageEventHandler::handlePotentialSingleTapEvent(const QTouchEvent::TouchPoint& point)
{
#if ENABLE(TOUCH_EVENTS)
if (point.pos() == QPointF()) {
m_webPageProxy->handlePotentialActivation(IntPoint(), IntSize());
} else {
QTransform fromItemTransform = m_webPage->transformFromItem();
m_webPageProxy->handlePotentialActivation(IntPoint(fromItemTransform.map(point.pos()).toPoint()), IntSize(point.rect().size().toSize()));
}
#else
Q_UNUSED(point);
#endif
}
void QtWebPageEventHandler::handleSingleTapEvent(const QTouchEvent::TouchPoint& point)
{
m_postponeTextInputStateChanged = true;
QTransform fromItemTransform = m_webPage->transformFromItem();
WebGestureEvent gesture(WebEvent::GestureSingleTap, fromItemTransform.map(point.pos()).toPoint(), point.screenPos().toPoint(), WebEvent::Modifiers(0), 0, IntSize(point.rect().size().toSize()), FloatPoint(0, 0));
m_webPageProxy->handleGestureEvent(gesture);
}
void QtWebPageEventHandler::handleDoubleTapEvent(const QTouchEvent::TouchPoint& point)
{
QTransform fromItemTransform = m_webPage->transformFromItem();
m_webPageProxy->findZoomableAreaForPoint(fromItemTransform.map(point.pos()).toPoint(), IntSize(point.rect().size().toSize()));
}
void QtWebPageEventHandler::timerEvent(QTimerEvent* ev)
{
int timerId = ev->timerId();
if (timerId == m_clickTimer.timerId())
m_clickTimer.stop();
else
QObject::timerEvent(ev);
}
void QtWebPageEventHandler::handleKeyPressEvent(QKeyEvent* ev)
{
m_webPageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(ev));
}
void QtWebPageEventHandler::handleKeyReleaseEvent(QKeyEvent* ev)
{
m_webPageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(ev));
}
void QtWebPageEventHandler::handleFocusInEvent(QFocusEvent*)
{
m_webPageProxy->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
}
void QtWebPageEventHandler::handleFocusOutEvent(QFocusEvent*)
{
m_webPageProxy->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
}
void QtWebPageEventHandler::setViewportInteractionEngine(QtViewportInteractionEngine* engine)
{
m_interactionEngine = engine;
}
void QtWebPageEventHandler::handleInputMethodEvent(QInputMethodEvent* ev)
{
QString commit = ev->commitString();
QString composition = ev->preeditString();
int replacementStart = ev->replacementStart();
int replacementLength = ev->replacementLength();
int cursorPositionWithinComposition = 0;
Vector<CompositionUnderline> underlines;
for (int i = 0; i < ev->attributes().size(); ++i) {
const QInputMethodEvent::Attribute& attr = ev->attributes().at(i);
switch (attr.type) {
case QInputMethodEvent::TextFormat: {
if (composition.isEmpty())
break;
QTextCharFormat textCharFormat = attr.value.value<QTextFormat>().toCharFormat();
QColor qcolor = textCharFormat.underlineColor();
Color color = makeRGBA(qcolor.red(), qcolor.green(), qcolor.blue(), qcolor.alpha());
int start = qMin(attr.start, (attr.start + attr.length));
int end = qMax(attr.start, (attr.start + attr.length));
underlines.append(CompositionUnderline(start, end, color, false));
break;
}
case QInputMethodEvent::Cursor:
if (attr.length)
cursorPositionWithinComposition = attr.start;
break;
default: break;
}
}
if (composition.isEmpty()) {
int selectionStart = -1;
int selectionLength = 0;
for (int i = 0; i < ev->attributes().size(); ++i) {
const QInputMethodEvent::Attribute& attr = ev->attributes().at(i);
if (attr.type == QInputMethodEvent::Selection) {
selectionStart = attr.start;
selectionLength = attr.length;
ASSERT(selectionStart >= 0);
ASSERT(selectionLength >= 0);
break;
}
}
m_webPageProxy->confirmComposition(commit, selectionStart, selectionLength);
} else {
ASSERT(cursorPositionWithinComposition >= 0);
ASSERT(replacementStart >= 0);
m_webPageProxy->setComposition(composition, underlines,
cursorPositionWithinComposition, cursorPositionWithinComposition,
replacementStart, replacementLength);
}
ev->accept();
}
void QtWebPageEventHandler::handleTouchEvent(QTouchEvent* event)
{
#if ENABLE(TOUCH_EVENTS)
QTransform fromItemTransform = m_webPage->transformFromItem();
m_webPageProxy->handleTouchEvent(NativeWebTouchEvent(event, fromItemTransform));
event->accept();
#else
ASSERT_NOT_REACHED();
event->ignore();
#endif
}
void QtWebPageEventHandler::resetGestureRecognizers()
{
m_panGestureRecognizer.cancel();
m_pinchGestureRecognizer.cancel();
m_tapGestureRecognizer.cancel();
}
static void setInputPanelVisible(bool visible)
{
if (qApp->inputPanel()->visible() == visible)
return;
qApp->inputPanel()->setVisible(visible);
}
void QtWebPageEventHandler::inputPanelVisibleChanged()
{
if (!m_interactionEngine)
return;
if (!m_webView->hasActiveFocus() || !qApp->inputPanel()->visible())
return;
const EditorState& editor = m_webPageProxy->editorState();
if (editor.isContentEditable)
m_interactionEngine->focusEditableArea(QRectF(editor.cursorRect), QRectF(editor.editorRect));
}
void QtWebPageEventHandler::updateTextInputState()
{
if (m_postponeTextInputStateChanged)
return;
const EditorState& editor = m_webPageProxy->editorState();
m_webView->setFlag(QQuickItem::ItemAcceptsInputMethod, editor.isContentEditable);
if (!m_webView->hasActiveFocus())
return;
qApp->inputPanel()->update(Qt::ImQueryInput | Qt::ImEnabled);
setInputPanelVisible(editor.isContentEditable);
}
void QtWebPageEventHandler::doneWithGestureEvent(const WebGestureEvent& event, bool wasEventHandled)
{
if (event.type() != WebEvent::GestureSingleTap)
return;
m_postponeTextInputStateChanged = false;
if (!wasEventHandled || !m_webView->hasActiveFocus())
return;
updateTextInputState();
}
#if ENABLE(TOUCH_EVENTS)
void QtWebPageEventHandler::doneWithTouchEvent(const NativeWebTouchEvent& event, bool wasEventHandled)
{
if (!m_interactionEngine)
return;
if (wasEventHandled || event.type() == WebEvent::TouchCancel) {
resetGestureRecognizers();
return;
}
const QTouchEvent* ev = event.nativeEvent();
switch (ev->type()) {
case QEvent::TouchBegin:
ASSERT(!m_interactionEngine->panGestureActive());
ASSERT(!m_interactionEngine->pinchGestureActive());
m_interactionEngine->touchBegin();
break;
case QEvent::TouchUpdate:
if (m_interactionEngine->scaleAnimationActive() && m_pinchGestureRecognizer.isRecognized())
m_interactionEngine->interruptScaleAnimation();
break;
case QEvent::TouchEnd:
m_interactionEngine->touchEnd();
break;
default:
break;
}
if (m_interactionEngine->scaleAnimationActive())
return;
const QList<QTouchEvent::TouchPoint>& touchPoints = ev->touchPoints();
const int touchPointCount = touchPoints.size();
qint64 eventTimestampMillis = ev->timestamp();
QList<QTouchEvent::TouchPoint> activeTouchPoints;
activeTouchPoints.reserve(touchPointCount);
for (int i = 0; i < touchPointCount; ++i) {
if (touchPoints[i].state() != Qt::TouchPointReleased)
activeTouchPoints << touchPoints[i];
}
const int activeTouchPointCount = activeTouchPoints.size();
if (!activeTouchPointCount) {
if (touchPointCount == 1) {
if (!m_panGestureRecognizer.isRecognized())
m_tapGestureRecognizer.update(ev->type(), touchPoints.first());
m_panGestureRecognizer.finish(touchPoints.first(), eventTimestampMillis);
} else
m_pinchGestureRecognizer.finish();
return;
} else if (activeTouchPointCount == 1) {
m_pinchGestureRecognizer.finish();
m_panGestureRecognizer.update(activeTouchPoints.first(), eventTimestampMillis);
} else if (activeTouchPointCount == 2) {
m_panGestureRecognizer.cancel();
m_pinchGestureRecognizer.update(activeTouchPoints.first(), activeTouchPoints.last());
}
if (m_panGestureRecognizer.isRecognized() || m_pinchGestureRecognizer.isRecognized() || m_webView->isMoving())
m_tapGestureRecognizer.cancel();
else if (touchPointCount == 1)
m_tapGestureRecognizer.update(ev->type(), touchPoints.first());
}
#endif
void QtWebPageEventHandler::didFindZoomableArea(const IntPoint& target, const IntRect& area)
{
if (!m_interactionEngine)
return;
m_interactionEngine->zoomToAreaGestureEnded(QPointF(target), QRectF(area));
}
void QtWebPageEventHandler::startDrag(const WebCore::DragData& dragData, PassRefPtr<ShareableBitmap> dragImage)
{
QImage dragQImage;
if (dragImage)
dragQImage = dragImage->createQImage();
else if (dragData.platformData() && dragData.platformData()->hasImage())
dragQImage = qvariant_cast<QImage>(dragData.platformData()->imageData());
DragOperation dragOperationMask = dragData.draggingSourceOperationMask();
QMimeData* mimeData = const_cast<QMimeData*>(dragData.platformData());
Qt::DropActions supportedDropActions = dragOperationToDropActions(dragOperationMask);
QPoint clientPosition;
QPoint globalPosition;
Qt::DropAction actualDropAction = Qt::IgnoreAction;
if (QWindow* window = m_webPage->canvas()) {
QDrag* drag = new QDrag(window);
drag->setPixmap(QPixmap::fromImage(dragQImage));
drag->setMimeData(mimeData);
actualDropAction = drag->exec(supportedDropActions);
globalPosition = QCursor::pos();
clientPosition = window->mapFromGlobal(globalPosition);
}
m_webPageProxy->dragEnded(clientPosition, globalPosition, dropActionToDragOperation(actualDropAction));
}
}
#include "moc_QtWebPageEventHandler.cpp"