Index: Source/core/input/EventHandler.cpp |
diff --git a/Source/core/input/EventHandler.cpp b/Source/core/input/EventHandler.cpp |
index 19698b6bf5a0b587896378adc6a5058273c8515d..c878c50acaee0fc4fa4a323b3ff6a22eec5bdcf5 100644 |
--- a/Source/core/input/EventHandler.cpp |
+++ b/Source/core/input/EventHandler.cpp |
@@ -43,6 +43,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" |
@@ -314,6 +315,7 @@ void EventHandler::clear() |
m_scrollbarHandlingScrollGesture = nullptr; |
m_maxMouseMovedDuration = 0; |
m_touchPressed = false; |
+ m_pointerIdManager.clear(); |
m_mouseDownMayStartDrag = false; |
m_lastShowPressTimestamp = 0; |
m_lastDeferredTapElement = nullptr; |
@@ -3379,7 +3381,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: |
@@ -3391,7 +3393,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; |
@@ -3413,17 +3434,174 @@ HitTestResult EventHandler::hitTestResultInFrame(LocalFrame* frame, const Layout |
return result; |
} |
+void EventHandler::dispatchPointerEventsForTouchEvent(const PlatformTouchEvent& event, Vector<TouchInfo>& touchInfos) |
+{ |
+ const String& PointerTypeStrForTouch("touch"); |
+ |
+ // Iterate through the touch points, sending PointerEvents to the targets as required. |
+ for (unsigned i = 0; i < touchInfos.size(); ++i) { |
+ TouchInfo& touchInfo = touchInfos[i]; |
+ const PlatformTouchPoint& point = touchInfo.point; |
+ const 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(PointerIdManager::PointerTypeTouch, pointerId); |
+ |
+ bool isEnterOrLeave = false; |
+ |
+ PointerEventInit pointerEventInit; |
+ pointerEventInit.setPointerId(pointerId); |
+ pointerEventInit.setWidth(touchInfo.adjustedRadius.width()); |
+ pointerEventInit.setHeight(touchInfo.adjustedRadius.height()); |
+ pointerEventInit.setPressure(point.force()); |
+ pointerEventInit.setPointerType(PointerTypeStrForTouch); |
+ pointerEventInit.setIsPrimary(m_pointerIdManager.isPrimary(PointerIdManager::PointerTypeTouch, 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(!isEnterOrLeave); |
+ pointerEventInit.setCancelable(!isEnterOrLeave && pointState != PlatformTouchPoint::TouchCancelled); |
+ |
+ 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(PointerIdManager::PointerTypeTouch, pointerId); |
+ } |
+} |
+ |
+bool EventHandler::dispatchTouchEvents(const PlatformTouchEvent& event, |
+ Vector<TouchInfo>& touchInfos, bool freshTouchEvents, bool allTouchReleased) |
+{ |
+ 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 < touchInfos.size(); ++i) { |
+ const TouchInfo& touchInfo = touchInfos[i]; |
+ const PlatformTouchPoint& point = touchInfo.point; |
+ PlatformTouchPoint::State pointState = point.state(); |
+ |
+ if (touchInfo.consumed) |
+ continue; |
+ |
+ RefPtrWillBeRawPtr<Touch> touch = Touch::create( |
+ 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(touchInfo.touchTarget); |
+ if (targetTouchesIterator == touchesByTarget.end()) { |
+ touchesByTarget.set(touchInfo.touchTarget, TouchList::create()); |
+ targetTouchesIterator = touchesByTarget.find(touchInfo.touchTarget); |
+ } |
+ |
+ // touches and targetTouches should only contain information about |
+ // touches still on the screen, so if this point is released or |
+ // cancelled it will only appear in the changedTouches list. |
+ if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) { |
+ touches->append(touch); |
+ targetTouchesIterator->value->append(touch); |
+ } |
+ |
+ // Now build up the correct list for changedTouches. |
+ // Note that any touches that are in the TouchStationary state (e.g. if |
+ // the user had several points touched but did not move them all) should |
+ // 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 && 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(touchInfo.touchTarget); |
+ } |
+ } |
+ if (allTouchReleased) { |
+ m_touchSequenceDocument.clear(); |
+ m_touchSequenceUserGestureToken.clear(); |
+ } |
+ |
+ // 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& 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(), |
+ 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(); |
+ } |
+ } |
+ |
+ return swallowedEvent; |
+} |
+ |
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) |
@@ -3456,7 +3634,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 |
@@ -3518,30 +3696,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; |
@@ -3590,71 +3748,28 @@ 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); |
- |
- RefPtrWillBeRawPtr<Touch> touch = Touch::create( |
- targetFrame, touchTarget.get(), point.id(), point.screenPos(), adjustedPagePoint, 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()); |
- if (targetTouchesIterator == touchesByTarget.end()) { |
- touchesByTarget.set(touchTarget.get(), TouchList::create()); |
- targetTouchesIterator = touchesByTarget.find(touchTarget.get()); |
- } |
- |
- // touches and targetTouches should only contain information about |
- // touches still on the screen, so if this point is released or |
- // cancelled it will only appear in the changedTouches list. |
- if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) { |
- touches->append(touch); |
- targetTouchesIterator->value->append(touch); |
- } |
- |
- // Now build up the correct list for changedTouches. |
- // Note that any touches that are in the TouchStationary state (e.g. if |
- // the user had several points touched but did not move them all) should |
- // 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) { |
- 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); |
- } |
+ TouchInfo& touchInfo = touchInfos[i]; |
+ touchInfo.point = point; |
+ 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 (allTouchReleased) { |
- m_touchSequenceDocument.clear(); |
- m_touchSequenceUserGestureToken.clear(); |
- } |
- |
- // Now iterate the changedTouches list and m_targets within it, sending |
- // events to the targets as required. |
- bool swallowedEvent = false; |
- for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) { |
- if (!changedTouches[state].m_touches) |
- continue; |
- const AtomicString& stateName(eventNameForTouchPointState(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(), |
- 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(); |
- } |
+ if (RuntimeEnabledFeatures::pointerEventEnabled()) { |
+ dispatchPointerEventsForTouchEvent(event, touchInfos); |
+ // TODO(mustaq): This needs attention. |
+ // From CL discussion: The disposition of any pointer events affects only the generation of |
+ // touch events. If pointer events were handled (and hence no touch events generated) that is |
+ // still equivalent to the touch events going unhandled because pointer event handler don't |
+ // block scroll gesture generation. |
} |
- return swallowedEvent; |
+ return dispatchTouchEvents(event, touchInfos, freshTouchEvents, allTouchReleased); |
} |
TouchAction EventHandler::intersectTouchAction(TouchAction action1, TouchAction action2) |