Commit 4362021b authored by apavlov@chromium.org's avatar apavlov@chromium.org

Implement touch event emulation in the WebCore layer

https://bugs.webkit.org/show_bug.cgi?id=77105

Reviewed by Ryosuke Niwa.

Source/WebCore:

This change essentially maps mouse events into single-touch events in the following way:
- mousedown -> touchstart
- mouseup -> touchend
- mousemove -> touchmove (between mousedown and mouseup).

Test: fast/events/touch/emulate-touch-events.html

* page/EventHandler.cpp:
(SyntheticTouchPoint): A synthetic touch point built from PlatformMouseEvent.
(WebCore::SyntheticTouchPoint::SyntheticTouchPoint):
(SyntheticSingleTouchEvent): A synthetic touch point event built from PlatformMouseEvent.
(WebCore::SyntheticSingleTouchEvent::SyntheticSingleTouchEvent):
(WebCore::EventHandler::handleMouseReleaseEvent): Invoke maybeDispatchSyntheticTouchEvent() and bail out if necessary.
(WebCore::EventHandler::handleMousePressEvent): Invoke maybeDispatchSyntheticTouchEvent() and bail out if necessary.
(WebCore::EventHandler::mouseMoved): Invoke maybeDispatchSyntheticTouchEvent() and bail out if necessary.
(WebCore::EventHandler::dispatchSyntheticTouchEventIfEnabled): Dispatch a synthetic touch event if necessary.
* page/EventHandler.h: Added new method.
* page/Settings.cpp:
(WebCore::Settings::Settings): Added m_touchEventEmulationEnabled initializer.
* page/Settings.h: Added m_touchEventEmulationEnabled, getter, and setter.
(WebCore::Settings::setTouchEventEmulationEnabled): Added.
(WebCore::Settings::isTouchEventEmulationEnabled): Added.
* platform/PlatformTouchPoint.h:
(WebCore::PlatformTouchPoint::PlatformTouchPoint): Unconditionally compile the parameterless ctor.
* testing/InternalSettings.cpp:
(WebCore::InternalSettings::setTouchEventEmulationEnabled): Added for testing.
* testing/InternalSettings.h: Added setTouchEventEmulationEnabled() for testing.
* testing/InternalSettings.idl: Added setTouchEventEmulationEnabled() for testing.

LayoutTests:

* fast/events/touch/emulate-touch-events-expected.txt: Added.
* fast/events/touch/emulate-touch-events.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@106642 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 51f54b6b
2012-01-27 Alexander Pavlov <apavlov@chromium.org>
Implement touch event emulation in the WebCore layer
https://bugs.webkit.org/show_bug.cgi?id=77105
Reviewed by Ryosuke Niwa.
* fast/events/touch/emulate-touch-events-expected.txt: Added.
* fast/events/touch/emulate-touch-events.html: Added.
2012-02-03 Adam Barth <abarth@webkit.org>
Group all the security failures together.
This tests single touch event emulation using mouse events.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS lastEvent.type is "touchstart"
PASS lastEvent.touches.length is 1
PASS lastEvent.changedTouches.length is 1
PASS lastEvent.targetTouches.length is 1
PASS lastEvent.pageX is 0
PASS lastEvent.pageY is 0
PASS lastEvent.shiftKey is true
PASS lastEvent.altKey is true
PASS lastEvent.ctrlKey is false
PASS lastEvent.metaKey is false
PASS lastEvent.touches[0].target.id is "touchtarget"
PASS lastEvent.touches[0].pageX is 10
PASS lastEvent.touches[0].pageY is 10
PASS lastEvent.touches[0].clientX is 10
PASS lastEvent.touches[0].clientY is 10
PASS lastEvent.touches[0].identifier is 0
PASS lastEvent.changedTouches[0].pageX is 10
PASS lastEvent.changedTouches[0].pageY is 10
PASS lastEvent.changedTouches[0].clientX is 10
PASS lastEvent.changedTouches[0].clientY is 10
PASS lastEvent.changedTouches[0].identifier is 0
PASS lastEvent.targetTouches[0].pageX is 10
PASS lastEvent.targetTouches[0].pageY is 10
PASS lastEvent.targetTouches[0].clientX is 10
PASS lastEvent.targetTouches[0].clientY is 10
PASS lastEvent.targetTouches[0].identifier is 0
PASS lastEvent.type is "touchmove"
PASS lastEvent.touches.length is 1
PASS lastEvent.changedTouches.length is 1
PASS lastEvent.targetTouches.length is 1
PASS lastEvent.pageX is 0
PASS lastEvent.pageY is 0
PASS lastEvent.touches[0].pageX is 20
PASS lastEvent.touches[0].pageY is 30
PASS lastEvent.touches[0].clientX is 20
PASS lastEvent.touches[0].clientY is 30
PASS lastEvent.touches[0].identifier is 0
PASS lastEvent.type is "touchend"
PASS lastEvent.touches.length is 0
PASS lastEvent.changedTouches.length is 1
PASS lastEvent.targetTouches.length is 0
PASS lastEvent.pageX is 0
PASS lastEvent.pageY is 0
PASS lastEvent.changedTouches[0].pageX is 20
PASS lastEvent.changedTouches[0].pageY is 30
PASS lastEvent.changedTouches[0].clientX is 20
PASS lastEvent.changedTouches[0].clientY is 30
PASS lastEvent.changedTouches[0].identifier is 0
PASS lastEvent.shiftKey is false
PASS lastEvent.altKey is true
PASS lastEvent.ctrlKey is true
PASS lastEvent.metaKey is false
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<body>
<script src="../../js/resources/js-test-pre.js"></script>
<div id="touchtarget" style="width: 100px; height: 100px; background-color: blue"></div>
<p id="description"></p>
<div id="console"></div>
<script>
var div = document.getElementById("touchtarget");
var lastEvent = null;
var touchEventsReceived = 0;
var EXPECTED_TOUCH_EVENTS_TOTAL = 3;
function touchEventCallback() {
if (window.eventSender) {
lastEvent = event;
verifyTouch(touchEventsReceived++);
} else
debug(event.type);
if (window.layoutTestController && touchEventsReceived == EXPECTED_TOUCH_EVENTS_TOTAL)
finishJSTest();
}
div.addEventListener("touchstart", touchEventCallback, false);
div.addEventListener("touchmove", touchEventCallback, false);
div.addEventListener("touchend", touchEventCallback, false);
function verifyTouchEvent(type, totalTouchCount, changedTouchCount, targetTouchCount)
{
shouldBeEqualToString("lastEvent.type", type);
shouldBe("lastEvent.touches.length", totalTouchCount.toString());
shouldBe("lastEvent.changedTouches.length", changedTouchCount.toString());
shouldBe("lastEvent.targetTouches.length", targetTouchCount.toString());
shouldBe("lastEvent.pageX", "0");
shouldBe("lastEvent.pageY", "0");
}
function verifyTouchPoint(list, point, x, y, id)
{
shouldBe("lastEvent." + list + "[" + point + "].pageX", x.toString());
shouldBe("lastEvent." + list + "[" + point + "].pageY", y.toString());
shouldBe("lastEvent." + list + "[" + point + "].clientX", x.toString());
shouldBe("lastEvent." + list + "[" + point + "].clientY", y.toString());
shouldBe("lastEvent." + list + "[" + point + "].identifier", id.toString());
}
function verifyTouch(which) {
switch (which) {
case 0:
verifyTouchEvent("touchstart", 1, 1, 1);
shouldBe("lastEvent.shiftKey", "true");
shouldBe("lastEvent.altKey", "true");
shouldBe("lastEvent.ctrlKey", "false");
shouldBe("lastEvent.metaKey", "false");
shouldBeEqualToString("lastEvent.touches[0].target.id", "touchtarget");
verifyTouchPoint("touches", 0, 10, 10, 0);
verifyTouchPoint("changedTouches", 0, 10, 10, 0);
verifyTouchPoint("targetTouches", 0, 10, 10, 0);
break;
case 1:
verifyTouchEvent("touchmove", 1, 1, 1);
verifyTouchPoint("touches", 0, 20, 30, 0);
break;
case 2:
verifyTouchEvent("touchend", 0, 1, 0);
verifyTouchPoint("changedTouches", 0, 20, 30, 0);
shouldBe("lastEvent.shiftKey", "false");
shouldBe("lastEvent.altKey", "true");
shouldBe("lastEvent.ctrlKey", "true");
shouldBe("lastEvent.metaKey", "false");
break;
default:
testFailed("Wrong number of touch events! (" + which + ")");
}
}
function mouseEventSequence()
{
eventSender.mouseMoveTo(10, 10);
eventSender.mouseDown(0, ["shiftKey", "altKey"]);
eventSender.mouseMoveTo(20, 30);
eventSender.mouseUp(0, ["altKey", "ctrlKey"]);
}
if (window.eventSender && window.internals && window.internals.settings) {
description("This tests single touch event emulation using mouse events.");
window.eventSender.dragMode = false;
window.jsTestIsAsync = true;
window.internals.settings.setTouchEventEmulationEnabled(true);
mouseEventSequence();
} else
debug("This test requires DumpRenderTree. Tap on the blue rect to log.");
</script>
<script src="../../js/resources/js-test-post.js"></script>
</body>
</html>
2012-01-27 Alexander Pavlov <apavlov@chromium.org>
Implement touch event emulation in the WebCore layer
https://bugs.webkit.org/show_bug.cgi?id=77105
Reviewed by Ryosuke Niwa.
This change essentially maps mouse events into single-touch events in the following way:
- mousedown -> touchstart
- mouseup -> touchend
- mousemove -> touchmove (between mousedown and mouseup).
Test: fast/events/touch/emulate-touch-events.html
* page/EventHandler.cpp:
(SyntheticTouchPoint): A synthetic touch point built from PlatformMouseEvent.
(WebCore::SyntheticTouchPoint::SyntheticTouchPoint):
(SyntheticSingleTouchEvent): A synthetic touch point event built from PlatformMouseEvent.
(WebCore::SyntheticSingleTouchEvent::SyntheticSingleTouchEvent):
(WebCore::EventHandler::handleMouseReleaseEvent): Invoke maybeDispatchSyntheticTouchEvent() and bail out if necessary.
(WebCore::EventHandler::handleMousePressEvent): Invoke maybeDispatchSyntheticTouchEvent() and bail out if necessary.
(WebCore::EventHandler::mouseMoved): Invoke maybeDispatchSyntheticTouchEvent() and bail out if necessary.
(WebCore::EventHandler::dispatchSyntheticTouchEventIfEnabled): Dispatch a synthetic touch event if necessary.
* page/EventHandler.h: Added new method.
* page/Settings.cpp:
(WebCore::Settings::Settings): Added m_touchEventEmulationEnabled initializer.
* page/Settings.h: Added m_touchEventEmulationEnabled, getter, and setter.
(WebCore::Settings::setTouchEventEmulationEnabled): Added.
(WebCore::Settings::isTouchEventEmulationEnabled): Added.
* platform/PlatformTouchPoint.h:
(WebCore::PlatformTouchPoint::PlatformTouchPoint): Unconditionally compile the parameterless ctor.
* testing/InternalSettings.cpp:
(WebCore::InternalSettings::setTouchEventEmulationEnabled): Added for testing.
* testing/InternalSettings.h: Added setTouchEventEmulationEnabled() for testing.
* testing/InternalSettings.idl: Added setTouchEventEmulationEnabled() for testing.
2012-02-03 Kentaro Hara <haraken@chromium.org>
Remove [NoCPPCustom] IDL
......@@ -60,6 +60,7 @@
#include "MouseEvent.h"
#include "MouseEventWithHitTestResults.h"
#include "Page.h"
#include "PlatformEvent.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformWheelEvent.h"
#include "PluginDocument.h"
......@@ -142,6 +143,73 @@ private:
Cursor m_cursor;
};
#if ENABLE(TOUCH_EVENTS)
class SyntheticTouchPoint : public PlatformTouchPoint {
public:
// The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
{
const static int idDefaultValue = 0;
const static int radiusYDefaultValue = 1;
const static int radiusXDefaultValue = 1;
const static float rotationAngleDefaultValue = 0.0f;
const static float forceDefaultValue = 1.0f;
m_id = idDefaultValue; // There is only one active TouchPoint.
m_screenPos = event.globalPosition();
m_pos = event.position();
m_radiusY = radiusYDefaultValue;
m_radiusX = radiusXDefaultValue;
m_rotationAngle = rotationAngleDefaultValue;
m_force = forceDefaultValue;
PlatformEvent::Type type = event.type();
ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);
switch (type) {
case PlatformEvent::MouseMoved:
m_state = TouchMoved;
break;
case PlatformEvent::MousePressed:
m_state = TouchPressed;
break;
case PlatformEvent::MouseReleased:
m_state = TouchReleased;
break;
default:
ASSERT_NOT_REACHED();
break;
}
}
};
class SyntheticSingleTouchEvent : public PlatformTouchEvent {
public:
explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
{
switch (event.type()) {
case PlatformEvent::MouseMoved:
m_type = TouchMove;
break;
case PlatformEvent::MousePressed:
m_type = TouchStart;
break;
case PlatformEvent::MouseReleased:
m_type = TouchEnd;
break;
default:
ASSERT_NOT_REACHED();
m_type = NoType;
break;
}
m_timestamp = event.timestamp();
m_modifiers = event.modifiers();
m_touchPoints.append(SyntheticTouchPoint(event));
}
};
#endif
static inline bool scrollNode(float delta, WheelEvent::Granularity granularity, ScrollDirection positiveDirection, ScrollDirection negativeDirection, Node* node, Node** stopNode)
{
if (!delta)
......@@ -739,6 +807,12 @@ bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event)
bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
{
#if ENABLE(TOUCH_EVENTS)
bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(event.event());
if (defaultPrevented)
return true;
#endif
if (m_autoscrollInProgress)
stopAutoscrollTimer();
......@@ -1375,6 +1449,12 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent)
{
RefPtr<FrameView> protector(m_frame->view());
#if ENABLE(TOUCH_EVENTS)
bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
if (defaultPrevented)
return true;
#endif
UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
// FIXME (bug 68185): this call should be made at another abstraction layer
......@@ -1561,6 +1641,13 @@ bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
{
RefPtr<FrameView> protector(m_frame->view());
#if ENABLE(TOUCH_EVENTS)
// FIXME: this should be moved elsewhere to also be able to dispatch touchcancel events.
bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(event);
if (defaultPrevented)
return true;
#endif
HitTestResult hoveredNode = HitTestResult(LayoutPoint());
bool result = handleMouseMoveEvent(event, &hoveredNode);
......@@ -3362,7 +3449,21 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
return defaultPrevented;
}
bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& event)
{
if (!m_frame->document()->settings()->isTouchEventEmulationEnabled())
return false;
PlatformEvent::Type eventType = event.type();
if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased)
return false;
if (eventType == PlatformEvent::MouseMoved && !m_touchPressed)
return false;
SyntheticSingleTouchEvent touchEvent(event);
return handleTouchEvent(touchEvent);
}
#endif
......
......@@ -264,6 +264,10 @@ private:
void fakeMouseMoveEventTimerFired(Timer<EventHandler>*);
void cancelFakeMouseMoveEvent();
#if ENABLE(TOUCH_EVENTS)
bool dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent&);
#endif
void invalidateClick();
Node* nodeUnderMouse() const;
......
......@@ -240,6 +240,9 @@ Settings::Settings(Page* page)
, m_scrollingCoordinatorEnabled(false)
#endif
, m_notificationsEnabled(true)
#if ENABLE(TOUCH_EVENTS)
, m_touchEventEmulationEnabled(false)
#endif
, m_loadsImagesAutomaticallyTimer(this, &Settings::loadsImagesAutomaticallyTimerFired)
{
// A Frame may not have been created yet, so we initialize the AtomicString
......
......@@ -523,6 +523,11 @@ namespace WebCore {
void setNotificationsEnabled(bool enabled) { m_notificationsEnabled = enabled; }
bool notificationsEnabled() const { return m_notificationsEnabled; }
#if ENABLE(TOUCH_EVENTS)
void setTouchEventEmulationEnabled(bool enabled) { m_touchEventEmulationEnabled = enabled; }
bool isTouchEventEmulationEnabled() const { return m_touchEventEmulationEnabled; }
#endif
private:
Settings(Page*);
......@@ -669,6 +674,10 @@ namespace WebCore {
bool m_notificationsEnabled : 1;
#if ENABLE(TOUCH_EVENTS)
bool m_touchEventEmulationEnabled : 1;
#endif
Timer<Settings> m_loadsImagesAutomaticallyTimer;
void loadsImagesAutomaticallyTimerFired(Timer<Settings>*);
......
......@@ -44,9 +44,11 @@ public:
TouchStateEnd // Placeholder: must remain the last item.
};
// This is necessary for us to be able to build synthetic events.
PlatformTouchPoint() { };
#if PLATFORM(QT)
PlatformTouchPoint(const QTouchEvent::TouchPoint&);
PlatformTouchPoint() {};
#elif PLATFORM(EFL)
PlatformTouchPoint(unsigned id, const IntPoint& windowPos, State);
#endif
......
......@@ -271,4 +271,15 @@ void InternalSettings::setPerTileDrawingEnabled(bool enabled, ExceptionCode& ec)
settings()->setPerTileDrawingEnabled(enabled);
}
void InternalSettings::setTouchEventEmulationEnabled(bool enabled, ExceptionCode& ec)
{
#if ENABLE(TOUCH_EVENTS)
InternalSettingsGuardForSettings();
settings()->setTouchEventEmulationEnabled(enabled);
#else
UNUSED_PARAM(enabled);
UNUSED_PARAM(ec);
#endif
}
}
......@@ -64,6 +64,7 @@ public:
float pageScaleFactor(ExceptionCode&);
void setPageScaleFactor(float scaleFactor, int x, int y, ExceptionCode&);
void setPerTileDrawingEnabled(bool enabled, ExceptionCode&);
void setTouchEventEmulationEnabled(bool enabled, ExceptionCode&);
private:
InternalSettings(Frame*, InternalSettings* old);
......
......@@ -44,6 +44,7 @@ module window {
boolean unifiedTextCheckingEnabled() raises (DOMException);
float pageScaleFactor() raises(DOMException);
void setPageScaleFactor(in float scaleFactor, in long x, in long y) raises(DOMException);
void setTouchEventEmulationEnabled(in boolean enabled) raises(DOMException);
};
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment