QtWebPageEventHandler.cpp [plain text]
#include "config.h"
#include "QtWebPageEventHandler.h"
#include "NativeWebKeyboardEvent.h"
#include "NativeWebMouseEvent.h"
#include "NativeWebWheelEvent.h"
#include "PageViewportControllerClientQt.h"
#include "WebPageProxy.h"
#include "qquickwebpage_p.h"
#include "qquickwebview_p.h"
#include <QCursor>
#include <QDrag>
#include <QGuiApplication>
#include <QInputEvent>
#include <QInputMethod>
#include <QMimeData>
#include <QMouseEvent>
#include <QQuickWindow>
#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_viewportController(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)
, m_isTapHighlightActive(false)
, m_isMouseButtonPressed(false)
{
connect(qApp->inputMethod(), SIGNAL(visibleChanged()), this, SLOT(inputPanelVisibleChanged()));
}
QtWebPageEventHandler::~QtWebPageEventHandler()
{
disconnect(qApp->inputMethod(), 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());
ev->accept();
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();
}
ev->accept();
m_webPageProxy->handleMouseEvent(NativeWebMouseEvent(ev, fromItemTransform, m_clickCount));
m_lastClick = webPagePoint;
m_clickTimer.start(qApp->styleHints()->mouseDoubleClickInterval(), this);
}
void QtWebPageEventHandler::handleMouseReleaseEvent(QMouseEvent* ev)
{
ev->accept();
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));
}
void QtWebPageEventHandler::handleHoverLeaveEvent(QHoverEvent* ev)
{
QHoverEvent fakeEvent(QEvent::HoverMove, QPoint(INT_MIN, INT_MIN), ev->oldPosF());
fakeEvent.setTimestamp(ev->timestamp());
handleHoverMoveEvent(&fakeEvent);
}
void QtWebPageEventHandler::handleHoverMoveEvent(QHoverEvent* ev)
{
QMouseEvent me(QEvent::MouseMove, 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::activateTapHighlight(const QTouchEvent::TouchPoint& point)
{
#if ENABLE(TOUCH_EVENTS)
ASSERT(!point.pos().toPoint().isNull());
ASSERT(!m_isTapHighlightActive);
m_isTapHighlightActive = true;
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::deactivateTapHighlight()
{
#if ENABLE(TOUCH_EVENTS)
if (!m_isTapHighlightActive)
return;
m_webPageProxy->handlePotentialActivation(IntPoint(), IntSize());
m_isTapHighlightActive = false;
#endif
}
void QtWebPageEventHandler::handleSingleTapEvent(const QTouchEvent::TouchPoint& point)
{
deactivateTapHighlight();
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)
{
if (!m_webView->isInteractive())
return;
deactivateTapHighlight();
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::handleFocusLost()
{
m_webPageProxy->viewStateDidChange(WebPageProxy::ViewIsFocused | WebPageProxy::ViewWindowIsActive);
}
void QtWebPageEventHandler::setViewportController(PageViewportControllerClientQt* controller)
{
m_viewportController = controller;
}
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->inputMethod()->isVisible() == visible)
return;
qApp->inputMethod()->setVisible(visible);
}
void QtWebPageEventHandler::inputPanelVisibleChanged()
{
if (!m_viewportController)
return;
if (!m_webView->hasActiveFocus() || !qApp->inputMethod()->isVisible())
return;
const EditorState& editor = m_webPageProxy->editorState();
if (editor.isContentEditable)
m_viewportController->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->inputMethod()->update(Qt::ImQueryInput | Qt::ImEnabled | Qt::ImHints);
setInputPanelVisible(editor.isContentEditable);
}
void QtWebPageEventHandler::handleWillSetInputMethodState()
{
if (qApp->inputMethod()->isVisible())
qApp->inputMethod()->commit();
}
void QtWebPageEventHandler::doneWithGestureEvent(const WebGestureEvent& event, bool wasEventHandled)
{
if (event.type() != WebEvent::GestureSingleTap)
return;
m_postponeTextInputStateChanged = false;
if (!wasEventHandled || !m_webView->hasActiveFocus())
return;
updateTextInputState();
}
void QtWebPageEventHandler::handleInputEvent(const QInputEvent* event)
{
if (m_viewportController) {
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::TouchBegin:
m_viewportController->touchBegin();
break;
case QEvent::MouseMove:
case QEvent::TouchUpdate:
if (m_viewportController->scaleAnimationActive() && m_pinchGestureRecognizer.isRecognized())
m_viewportController->interruptScaleAnimation();
break;
case QEvent::MouseButtonRelease:
case QEvent::TouchEnd:
m_viewportController->touchEnd();
break;
default:
break;
}
if (m_viewportController->scaleAnimationActive())
return;
}
bool isMouseEvent = false;
switch (event->type()) {
case QEvent::MouseButtonPress:
isMouseEvent = true;
m_isMouseButtonPressed = true;
break;
case QEvent::MouseMove:
if (!m_isMouseButtonPressed)
return;
isMouseEvent = true;
break;
case QEvent::MouseButtonRelease:
isMouseEvent = true;
m_isMouseButtonPressed = false;
break;
case QEvent::MouseButtonDblClick:
return;
default:
break;
}
QList<QTouchEvent::TouchPoint> activeTouchPoints;
QTouchEvent::TouchPoint currentTouchPoint;
qint64 eventTimestampMillis = event->timestamp();
int touchPointCount = 0;
if (!isMouseEvent) {
const QTouchEvent* touchEvent = static_cast<const QTouchEvent*>(event);
const QList<QTouchEvent::TouchPoint>& touchPoints = touchEvent->touchPoints();
currentTouchPoint = touchPoints.first();
touchPointCount = touchPoints.size();
activeTouchPoints.reserve(touchPointCount);
for (int i = 0; i < touchPointCount; ++i) {
if (touchPoints[i].state() != Qt::TouchPointReleased)
activeTouchPoints << touchPoints[i];
}
} else {
const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
touchPointCount = 1;
currentTouchPoint.setId(mouseEvent->buttons());
currentTouchPoint.setScreenPos(mouseEvent->screenPos());
currentTouchPoint.setRect(QRectF(mouseEvent->localPos(), QSizeF(2, 2)));
if (m_isMouseButtonPressed)
activeTouchPoints << currentTouchPoint;
}
const int activeTouchPointCount = activeTouchPoints.size();
if (!activeTouchPointCount) {
if (touchPointCount == 1) {
if (m_panGestureRecognizer.isRecognized())
m_panGestureRecognizer.finish(currentTouchPoint, eventTimestampMillis);
else {
m_panGestureRecognizer.cancel();
m_tapGestureRecognizer.finish(currentTouchPoint);
}
} 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(currentTouchPoint);
}
#if ENABLE(TOUCH_EVENTS)
void QtWebPageEventHandler::doneWithTouchEvent(const NativeWebTouchEvent& event, bool wasEventHandled)
{
if (wasEventHandled || event.type() == WebEvent::TouchCancel) {
m_panGestureRecognizer.cancel();
m_pinchGestureRecognizer.cancel();
if (event.type() != WebEvent::TouchMove)
m_tapGestureRecognizer.cancel();
return;
}
const QTouchEvent* ev = event.nativeEvent();
handleInputEvent(ev);
}
#endif
void QtWebPageEventHandler::didFindZoomableArea(const IntPoint& target, const IntRect& area)
{
if (!m_viewportController)
return;
m_viewportController->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->window()) {
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"