Index: Source/core/page/EventHandler.cpp |
diff --git a/Source/core/page/EventHandler.cpp b/Source/core/page/EventHandler.cpp |
index 6cfb5ac4da40857cf04b2ad674cc0e7b2891759a..5eb6095f7b2de272627965e1a81d366ee8ba918a 100644 |
--- a/Source/core/page/EventHandler.cpp |
+++ b/Source/core/page/EventHandler.cpp |
@@ -45,6 +45,7 @@ |
#include "core/events/EventPath.h" |
#include "core/events/KeyboardEvent.h" |
#include "core/events/MouseEvent.h" |
+#include "core/events/PointerEvent.h" |
#include "core/events/TextEvent.h" |
#include "core/events/TouchEvent.h" |
#include "core/events/WheelEvent.h" |
@@ -316,6 +317,7 @@ void EventHandler::clear() |
m_scrollbarHandlingScrollGesture = nullptr; |
m_maxMouseMovedDuration = 0; |
m_touchPressed = false; |
+ m_pointerIdManager.clear(); |
m_mouseDownMayStartSelect = false; |
m_mouseDownMayStartDrag = false; |
m_lastShowPressTimestamp = 0; |
@@ -3777,7 +3779,7 @@ void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setL |
} |
} |
-static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state) |
+static const AtomicString& touchEventNameForTouchPointState(PlatformTouchPoint::State state) |
{ |
switch (state) { |
case PlatformTouchPoint::TouchReleased: |
@@ -3789,7 +3791,26 @@ static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State |
case PlatformTouchPoint::TouchMoved: |
return EventTypeNames::touchmove; |
case PlatformTouchPoint::TouchStationary: |
- // TouchStationary state is not converted to touch events, so fall through to assert. |
+ // Fall through to default |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ return emptyAtom; |
+ } |
+} |
+ |
+static const AtomicString& pointerEventNameForTouchPointState(PlatformTouchPoint::State state) |
+{ |
+ switch (state) { |
+ case PlatformTouchPoint::TouchReleased: |
+ return EventTypeNames::pointerup; |
+ case PlatformTouchPoint::TouchCancelled: |
+ return EventTypeNames::pointercancel; |
+ case PlatformTouchPoint::TouchPressed: |
+ return EventTypeNames::pointerdown; |
+ case PlatformTouchPoint::TouchMoved: |
+ return EventTypeNames::pointermove; |
+ case PlatformTouchPoint::TouchStationary: |
+ // Fall through to default |
default: |
ASSERT_NOT_REACHED(); |
return emptyAtom; |
@@ -3811,17 +3832,75 @@ HitTestResult EventHandler::hitTestResultInFrame(LocalFrame* frame, const Layout |
return result; |
} |
+void EventHandler::handlePointerEventsFromTouchEvent(const PlatformTouchEvent& event, |
+ const Vector<PlatformTouchPoint>& points, Vector<TouchInfo>& touchInfos) |
+{ |
+ const String& PointerTypeTouch("touch"); |
+ |
+ // Iterate through the touch points, sending PointerEvents to the targets as required. |
+ for (unsigned i = 0; i < points.size(); ++i) { |
+ const PlatformTouchPoint& point = points[i]; |
+ TouchInfo& touchInfo = touchInfos[i]; |
+ |
+ unsigned pointerId = point.id(); |
+ const PlatformTouchPoint::State pointState = point.state(); |
+ |
+ if (pointState == PlatformTouchPoint::TouchStationary) |
+ continue; |
+ bool pointerReleasedOrCancelled = |
+ pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled; |
+ const AtomicString& eventName(pointerEventNameForTouchPointState(pointState)); |
+ |
+ if (pointState == PlatformTouchPoint::TouchPressed) |
+ m_pointerIdManager.add(pointerId); |
+ |
+ bool isEnterOrLeave = false; |
+ bool isCancelable = !(isEnterOrLeave || pointState == PlatformTouchPoint::TouchCancelled); |
+ bool isBubbling = !isEnterOrLeave; |
+ |
+ PointerEventInit pointerEventInit; |
+ pointerEventInit.setPointerId(pointerId); |
+ pointerEventInit.setWidth(touchInfo.adjustedRadius.width()); |
+ pointerEventInit.setHeight(touchInfo.adjustedRadius.height()); |
+ pointerEventInit.setPressure(point.force()); |
+ pointerEventInit.setPointerType(PointerTypeTouch); |
+ pointerEventInit.setIsPrimary(m_pointerIdManager.isPrimary(pointerId)); |
+ pointerEventInit.setScreenX(point.screenPos().x()); |
+ pointerEventInit.setScreenY(point.screenPos().y()); |
+ pointerEventInit.setClientX(touchInfo.adjustedPagePoint.x()); |
+ pointerEventInit.setClientY(touchInfo.adjustedPagePoint.y()); |
+ pointerEventInit.setButton(0); |
+ pointerEventInit.setButtons(pointerReleasedOrCancelled ? 0 : 1); |
+ |
+ pointerEventInit.setCtrlKey(event.ctrlKey()); |
+ pointerEventInit.setShiftKey(event.shiftKey()); |
+ pointerEventInit.setAltKey(event.altKey()); |
+ pointerEventInit.setMetaKey(event.metaKey()); |
+ |
+ pointerEventInit.setBubbles(isBubbling); |
+ pointerEventInit.setCancelable(isCancelable); |
+ |
+ RefPtrWillBeRawPtr<PointerEvent> pointerEvent = PointerEvent::create(eventName, pointerEventInit); |
+ touchInfo.touchTarget->toNode()->dispatchPointerEvent(pointerEvent.get()); |
+ touchInfo.consumed = pointerEvent->defaultPrevented() || pointerEvent->defaultHandled(); |
+ |
+ // Remove the released/cancelled id at the end to correctly determine primary id above. |
+ if (pointerReleasedOrCancelled) |
+ m_pointerIdManager.remove(pointerId); |
+ } |
+} |
+ |
bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
{ |
TRACE_EVENT0("blink", "EventHandler::handleTouchEvent"); |
const Vector<PlatformTouchPoint>& points = event.touchPoints(); |
- unsigned i; |
bool freshTouchEvents = true; |
bool allTouchReleased = true; |
- for (i = 0; i < points.size(); ++i) { |
+ for (unsigned i = 0; i < points.size(); ++i) { |
const PlatformTouchPoint& point = points[i]; |
+ |
if (point.state() != PlatformTouchPoint::TouchPressed) |
freshTouchEvents = false; |
if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled) |
@@ -3854,7 +3933,7 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
} |
// First do hit tests for any new touch points. |
- for (i = 0; i < points.size(); ++i) { |
+ for (unsigned i = 0; i < points.size(); ++i) { |
const PlatformTouchPoint& point = points[i]; |
// Touch events implicitly capture to the touched node, and don't change |
@@ -3916,30 +3995,10 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
return false; |
} |
- // Build up the lists to use for the 'touches', 'targetTouches' and |
- // 'changedTouches' attributes in the JS event. See |
- // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
- // lists fit together. |
- |
- // Holds the complete set of touches on the screen. |
- RefPtrWillBeRawPtr<TouchList> touches = TouchList::create(); |
- |
- // A different view on the 'touches' list above, filtered and grouped by |
- // event target. Used for the 'targetTouches' list in the JS event. |
- using TargetTouchesHeapMap = WillBeHeapHashMap<EventTarget*, RefPtrWillBeMember<TouchList>>; |
- TargetTouchesHeapMap touchesByTarget; |
- |
- // Array of touches per state, used to assemble the 'changedTouches' list. |
- using EventTargetSet = WillBeHeapHashSet<RefPtrWillBeMember<EventTarget>>; |
- struct { |
- // The touches corresponding to the particular change state this struct |
- // instance represents. |
- RefPtrWillBeMember<TouchList> m_touches; |
- // Set of targets involved in m_touches. |
- EventTargetSet m_targets; |
- } changedTouches[PlatformTouchPoint::TouchStateEnd]; |
+ // Compute and store the common info used by both PointerEvent and TouchEvent. |
+ Vector<TouchInfo> touchInfos(points.size()); |
- for (i = 0; i < points.size(); ++i) { |
+ for (unsigned i = 0; i < points.size(); ++i) { |
const PlatformTouchPoint& point = points[i]; |
PlatformTouchPoint::State pointState = point.state(); |
RefPtrWillBeRawPtr<EventTarget> touchTarget = nullptr; |
@@ -3988,21 +4047,69 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
// pagePoint should always be in the target element's document coordinates. |
FloatPoint pagePoint = targetFrame->view()->rootFrameToContents(point.pos()); |
- |
float scaleFactor = 1.0f / targetFrame->pageZoomFactor(); |
- FloatPoint adjustedPagePoint = pagePoint.scaledBy(scaleFactor); |
- FloatSize adjustedRadius = point.radius().scaledBy(scaleFactor); |
+ TouchInfo& touchInfo = touchInfos[i]; |
+ touchInfo.touchTarget = touchTarget.get(); |
+ touchInfo.targetFrame = targetFrame; |
+ touchInfo.adjustedPagePoint = pagePoint.scaledBy(scaleFactor); |
+ touchInfo.adjustedRadius = point.radius().scaledBy(scaleFactor); |
+ touchInfo.knownTarget = knownTarget; |
+ touchInfo.consumed = false; |
+ } |
+ |
+ if (RuntimeEnabledFeatures::pointerEventEnabled()) |
+ handlePointerEventsFromTouchEvent(event, points, touchInfos); |
+ |
+ bool swallowedEvent = false; |
+ |
+ // Build up the lists to use for the 'touches', 'targetTouches' and |
+ // 'changedTouches' attributes in the JS event. See |
+ // http://www.w3.org/TR/touch-events/#touchevent-interface for how these |
+ // lists fit together. |
+ |
+ // Holds the complete set of touches on the screen. |
+ RefPtrWillBeRawPtr<TouchList> touches = TouchList::create(); |
+ |
+ // A different view on the 'touches' list above, filtered and grouped by |
+ // event target. Used for the 'targetTouches' list in the JS event. |
+ using TargetTouchesHeapMap = WillBeHeapHashMap<EventTarget*, RefPtrWillBeMember<TouchList>>; |
+ TargetTouchesHeapMap touchesByTarget; |
+ |
+ // Array of touches per state, used to assemble the 'changedTouches' list. |
+ using EventTargetSet = WillBeHeapHashSet<RefPtrWillBeMember<EventTarget>>; |
+ struct { |
+ // The touches corresponding to the particular change state this struct |
+ // instance represents. |
+ RefPtrWillBeMember<TouchList> m_touches; |
+ // Set of targets involved in m_touches. |
+ EventTargetSet m_targets; |
+ } changedTouches[PlatformTouchPoint::TouchStateEnd]; |
+ |
+ for (unsigned i = 0; i < points.size(); ++i) { |
+ const PlatformTouchPoint& point = points[i]; |
+ const TouchInfo& touchInfo = touchInfos[i]; |
+ PlatformTouchPoint::State pointState = point.state(); |
+ |
+ if (touchInfo.consumed) |
+ continue; |
RefPtrWillBeRawPtr<Touch> touch = Touch::create( |
- targetFrame, touchTarget.get(), point.id(), point.screenPos(), adjustedPagePoint, adjustedRadius, point.rotationAngle(), point.force()); |
+ touchInfo.targetFrame, |
+ touchInfo.touchTarget, |
+ point.id(), |
+ point.screenPos(), |
+ touchInfo.adjustedPagePoint, |
+ touchInfo.adjustedRadius, |
+ point.rotationAngle(), |
+ point.force()); |
// Ensure this target's touch list exists, even if it ends up empty, so |
// it can always be passed to TouchEvent::Create below. |
- TargetTouchesHeapMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get()); |
+ TargetTouchesHeapMap::iterator targetTouchesIterator = touchesByTarget.find(touchInfo.touchTarget); |
if (targetTouchesIterator == touchesByTarget.end()) { |
- touchesByTarget.set(touchTarget.get(), TouchList::create()); |
- targetTouchesIterator = touchesByTarget.find(touchTarget.get()); |
+ touchesByTarget.set(touchInfo.touchTarget, TouchList::create()); |
+ targetTouchesIterator = touchesByTarget.find(touchInfo.touchTarget); |
} |
// touches and targetTouches should only contain information about |
@@ -4019,12 +4126,12 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
// never be in the changedTouches list so we do not handle them |
// explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609 |
// for further discussion about the TouchStationary state. |
- if (pointState != PlatformTouchPoint::TouchStationary && knownTarget) { |
+ if (pointState != PlatformTouchPoint::TouchStationary && touchInfo.knownTarget) { |
ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); |
if (!changedTouches[pointState].m_touches) |
changedTouches[pointState].m_touches = TouchList::create(); |
changedTouches[pointState].m_touches->append(touch); |
- changedTouches[pointState].m_targets.add(touchTarget); |
+ changedTouches[pointState].m_targets.add(touchInfo.touchTarget); |
} |
} |
if (allTouchReleased) { |
@@ -4032,20 +4139,19 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
m_touchSequenceUserGestureToken.clear(); |
} |
- // Now iterate the changedTouches list and m_targets within it, sending |
- // events to the targets as required. |
- bool swallowedEvent = false; |
+ // Now iterate through the changedTouches list and m_targets within it, sending |
+ // TouchEvents to the targets as required. |
for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) { |
if (!changedTouches[state].m_touches) |
continue; |
- const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state))); |
+ const AtomicString& eventName(touchEventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state))); |
const EventTargetSet& targetsForState = changedTouches[state].m_targets; |
for (const RefPtrWillBeMember<EventTarget>& eventTarget : targetsForState) { |
EventTarget* touchEventTarget = eventTarget.get(); |
RefPtrWillBeRawPtr<TouchEvent> touchEvent = TouchEvent::create( |
touches.get(), touchesByTarget.get(touchEventTarget), changedTouches[state].m_touches.get(), |
- stateName, touchEventTarget->toNode()->document().domWindow(), |
+ eventName, touchEventTarget->toNode()->document().domWindow(), |
event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), event.cancelable(), event.causesScrollingIfUncanceled(), event.timestamp()); |
touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get()); |
swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled(); |