| Index: third_party/WebKit/Source/core/input/EventHandler.cpp
|
| diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
|
| index 3e20b960f3321477f0ddd74a9d3aaa41e26d9055..7a9a4b630908f4cab20d4c8c4b32f49406b2e60a 100644
|
| --- a/third_party/WebKit/Source/core/input/EventHandler.cpp
|
| +++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
|
| @@ -58,7 +58,6 @@
|
| #include "core/frame/Settings.h"
|
| #include "core/frame/UseCounter.h"
|
| #include "core/frame/VisualViewport.h"
|
| -#include "core/html/HTMLCanvasElement.h"
|
| #include "core/html/HTMLDialogElement.h"
|
| #include "core/html/HTMLFrameElementBase.h"
|
| #include "core/html/HTMLFrameSetElement.h"
|
| @@ -107,31 +106,6 @@ namespace blink {
|
|
|
| namespace {
|
|
|
| -bool hasTouchHandlers(const EventHandlerRegistry& registry)
|
| -{
|
| - return registry.hasEventHandlers(EventHandlerRegistry::TouchEventBlocking)
|
| - || registry.hasEventHandlers(EventHandlerRegistry::TouchEventPassive);
|
| -}
|
| -
|
| -const AtomicString& touchEventNameForTouchPointState(PlatformTouchPoint::TouchState state)
|
| -{
|
| - switch (state) {
|
| - case PlatformTouchPoint::TouchReleased:
|
| - return EventTypeNames::touchend;
|
| - case PlatformTouchPoint::TouchCancelled:
|
| - return EventTypeNames::touchcancel;
|
| - case PlatformTouchPoint::TouchPressed:
|
| - return EventTypeNames::touchstart;
|
| - case PlatformTouchPoint::TouchMoved:
|
| - return EventTypeNames::touchmove;
|
| - case PlatformTouchPoint::TouchStationary:
|
| - // Fall through to default
|
| - default:
|
| - ASSERT_NOT_REACHED();
|
| - return emptyAtom;
|
| - }
|
| -}
|
| -
|
| // Convert |event->deltaMode()| to scroll granularity and output as |granularity|.
|
| bool wheelGranularityToScrollGranularity(const WheelEvent* event, ScrollGranularity* granularity)
|
| {
|
| @@ -269,7 +243,7 @@ EventHandler::EventHandler(LocalFrame* frame)
|
| , m_accumulatedRootOverscroll(FloatSize())
|
| , m_mousePositionIsUnknown(true)
|
| , m_mouseDownTimestamp(0)
|
| - , m_touchPressed(false)
|
| + , m_pointerEventManager(frame)
|
| , m_scrollGestureHandlingNode(nullptr)
|
| , m_lastGestureScrollOverWidget(false)
|
| , m_longTapShouldInvokeContextMenu(false)
|
| @@ -297,9 +271,6 @@ DEFINE_TRACE(EventHandler)
|
| visitor->trace(m_dragTarget);
|
| visitor->trace(m_frameSetBeingResized);
|
| visitor->trace(m_scrollbarHandlingScrollGesture);
|
| - visitor->trace(m_targetForTouchID);
|
| - visitor->trace(m_regionForTouchID);
|
| - visitor->trace(m_touchSequenceDocument);
|
| visitor->trace(m_scrollGestureHandlingNode);
|
| visitor->trace(m_previousGestureScrolledNode);
|
| visitor->trace(m_lastDeferredTapElement);
|
| @@ -336,13 +307,9 @@ void EventHandler::clear()
|
| m_mousePressed = false;
|
| m_capturesDragging = false;
|
| m_capturingMouseEventsNode = nullptr;
|
| - m_targetForTouchID.clear();
|
| - m_touchSequenceDocument.clear();
|
| - m_touchSequenceUserGestureToken.clear();
|
| clearGestureScrollState();
|
| m_lastGestureScrollOverWidget = false;
|
| m_scrollbarHandlingScrollGesture = nullptr;
|
| - m_touchPressed = false;
|
| m_pointerEventManager.clear();
|
| m_mouseDownMayStartDrag = false;
|
| m_lastShowPressTimestamp = 0;
|
| @@ -1222,7 +1189,7 @@ WebInputEventResult EventHandler::handleMouseMoveOrLeaveEvent(const PlatformMous
|
| }
|
|
|
| // Treat any mouse move events as readonly if the user is currently touching the screen.
|
| - if (m_touchPressed)
|
| + if (m_pointerEventManager.getTouchEventManager().isAnyTouchActive())
|
| hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
|
| HitTestRequest request(hitType);
|
| MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(mouseEvent, HitTestResult(request, LayoutPoint()));
|
| @@ -3572,359 +3539,11 @@ HitTestResult EventHandler::hitTestResultInFrame(LocalFrame* frame, const Layout
|
| return result;
|
| }
|
|
|
| -void EventHandler::dispatchPointerEvents(const PlatformTouchEvent& event,
|
| - HeapVector<TouchInfo>& touchInfos)
|
| -{
|
| - if (!RuntimeEnabledFeatures::pointerEventEnabled())
|
| - return;
|
| -
|
| - // 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& touchPoint = touchInfo.point;
|
| -
|
| -
|
| - if (touchPoint.state() == PlatformTouchPoint::TouchStationary
|
| - || !touchInfo.knownTarget)
|
| - continue;
|
| -
|
| - WebInputEventResult result =
|
| - m_pointerEventManager.sendTouchPointerEvent(
|
| - touchInfo.touchTarget, touchPoint, event.getModifiers(),
|
| - touchInfo.adjustedRadius.width(), touchInfo.adjustedRadius.height(),
|
| - touchInfo.adjustedPagePoint.x(), touchInfo.adjustedPagePoint.y());
|
| - touchInfo.consumed = result != WebInputEventResult::NotHandled;
|
| - }
|
| -}
|
| -
|
| -namespace {
|
| -
|
| -// Defining this class type local to dispatchTouchEvents() and annotating
|
| -// it with STACK_ALLOCATED(), runs into MSVC(VS 2013)'s C4822 warning
|
| -// that the local class doesn't provide a local definition for 'operator new'.
|
| -// Which it intentionally doesn't and shouldn't.
|
| -//
|
| -// Work around such toolchain bugginess by lifting out the type, thereby
|
| -// taking it out of C4822's reach.
|
| -class ChangedTouches final {
|
| - STACK_ALLOCATED();
|
| -public:
|
| - // The touches corresponding to the particular change state this struct
|
| - // instance represents.
|
| - Member<TouchList> m_touches;
|
| -
|
| - using EventTargetSet = HeapHashSet<Member<EventTarget>>;
|
| - // Set of targets involved in m_touches.
|
| - EventTargetSet m_targets;
|
| -};
|
| -
|
| -} // namespace
|
| -
|
| -WebInputEventResult EventHandler::dispatchTouchEvents(const PlatformTouchEvent& event,
|
| - HeapVector<TouchInfo>& touchInfos, bool freshTouchEvents, bool allTouchReleased)
|
| -{
|
| - // 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.
|
| - 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 = HeapHashMap<EventTarget*, Member<TouchList>>;
|
| - TargetTouchesHeapMap touchesByTarget;
|
| -
|
| - // Array of touches per state, used to assemble the 'changedTouches' list.
|
| - ChangedTouches changedTouches[PlatformTouchPoint::TouchStateEnd];
|
| -
|
| - for (unsigned i = 0; i < touchInfos.size(); ++i) {
|
| - const TouchInfo& touchInfo = touchInfos[i];
|
| - const PlatformTouchPoint& point = touchInfo.point;
|
| - PlatformTouchPoint::TouchState pointState = point.state();
|
| -
|
| - if (touchInfo.consumed)
|
| - continue;
|
| -
|
| - Touch* touch = Touch::create(
|
| - touchInfo.targetFrame.get(),
|
| - touchInfo.touchTarget.get(),
|
| - point.id(),
|
| - point.screenPos(),
|
| - touchInfo.adjustedPagePoint,
|
| - touchInfo.adjustedRadius,
|
| - point.rotationAngle(),
|
| - point.force(),
|
| - touchInfo.region);
|
| -
|
| - // 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.get());
|
| - if (targetTouchesIterator == touchesByTarget.end()) {
|
| - touchesByTarget.set(touchInfo.touchTarget.get(), TouchList::create());
|
| - targetTouchesIterator = touchesByTarget.find(touchInfo.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 && 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();
|
| - }
|
| -
|
| - WebInputEventResult eventResult = WebInputEventResult::NotHandled;
|
| -
|
| - // 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::TouchState>(state)));
|
| - for (const auto& eventTarget : changedTouches[state].m_targets) {
|
| - EventTarget* touchEventTarget = eventTarget;
|
| - TouchEvent* touchEvent = TouchEvent::create(
|
| - touches, touchesByTarget.get(touchEventTarget), changedTouches[state].m_touches.get(),
|
| - eventName, touchEventTarget->toNode()->document().domWindow(),
|
| - event.getModifiers(), event.cancelable(), event.causesScrollingIfUncanceled(), event.timestamp());
|
| -
|
| - eventResult = mergeEventResult(eventResult, toWebInputEventResult(touchEventTarget->dispatchEvent(touchEvent)));
|
| - }
|
| - }
|
| -
|
| - return eventResult;
|
| -}
|
| -
|
| WebInputEventResult EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
|
| {
|
| TRACE_EVENT0("blink", "EventHandler::handleTouchEvent");
|
|
|
| - if (event.type() == PlatformEvent::TouchScrollStarted) {
|
| - m_pointerEventManager.blockTouchPointers();
|
| - return WebInputEventResult::HandledSystem;
|
| - }
|
| -
|
| - const Vector<PlatformTouchPoint>& points = event.touchPoints();
|
| -
|
| - bool newTouchSequence = true;
|
| - bool allTouchReleased = true;
|
| - for (unsigned i = 0; i < points.size(); ++i) {
|
| - const PlatformTouchPoint& point = points[i];
|
| -
|
| - if (point.state() != PlatformTouchPoint::TouchPressed)
|
| - newTouchSequence = false;
|
| - if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled)
|
| - allTouchReleased = false;
|
| - }
|
| - if (newTouchSequence) {
|
| - // Ideally we'd ASSERT !m_touchSequenceDocument here since we should
|
| - // have cleared the active document when we saw the last release. But we
|
| - // have some tests that violate this, ClusterFuzz could trigger it, and
|
| - // there may be cases where the browser doesn't reliably release all
|
| - // touches. http://crbug.com/345372 tracks this.
|
| - m_touchSequenceDocument.clear();
|
| - m_touchSequenceUserGestureToken.clear();
|
| - }
|
| -
|
| - ASSERT(m_frame->view());
|
| - if (m_touchSequenceDocument && (!m_touchSequenceDocument->frame() || !m_touchSequenceDocument->frame()->view())) {
|
| - // If the active touch document has no frame or view, it's probably being destroyed
|
| - // so we can't dispatch events.
|
| - return WebInputEventResult::NotHandled;
|
| - }
|
| -
|
| - // First do hit tests for any new touch points.
|
| - 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
|
| - // active/hover states themselves (Gesture events do). So we only need
|
| - // to hit-test on touchstart, and it can be read-only.
|
| - if (point.state() == PlatformTouchPoint::TouchPressed) {
|
| - HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent | HitTestRequest::ReadOnly | HitTestRequest::Active;
|
| - LayoutPoint pagePoint = roundedLayoutPoint(m_frame->view()->rootFrameToContents(point.pos()));
|
| - HitTestResult result;
|
| - if (!m_touchSequenceDocument) {
|
| - result = hitTestResultAtPoint(pagePoint, hitType);
|
| - } else if (m_touchSequenceDocument->frame()) {
|
| - LayoutPoint framePoint = roundedLayoutPoint(m_touchSequenceDocument->frame()->view()->rootFrameToContents(point.pos()));
|
| - result = hitTestResultInFrame(m_touchSequenceDocument->frame(), framePoint, hitType);
|
| - } else {
|
| - continue;
|
| - }
|
| -
|
| - Node* node = result.innerNode();
|
| - if (!node)
|
| - continue;
|
| -
|
| - if (isHTMLCanvasElement(node)) {
|
| - std::pair<Element*, String> regionInfo = toHTMLCanvasElement(node)->getControlAndIdIfHitRegionExists(result.pointInInnerNodeFrame());
|
| - if (regionInfo.first)
|
| - node = regionInfo.first;
|
| - m_regionForTouchID.set(point.id(), regionInfo.second);
|
| - }
|
| -
|
| - // Touch events should not go to text nodes
|
| - if (node->isTextNode())
|
| - node = FlatTreeTraversal::parent(*node);
|
| -
|
| - if (!m_touchSequenceDocument) {
|
| - // Keep track of which document should receive all touch events
|
| - // in the active sequence. This must be a single document to
|
| - // ensure we don't leak Nodes between documents.
|
| - m_touchSequenceDocument = &(result.innerNode()->document());
|
| - ASSERT(m_touchSequenceDocument->frame()->view());
|
| - }
|
| -
|
| - // Ideally we'd ASSERT(!m_targetForTouchID.contains(point.id())
|
| - // since we shouldn't get a touchstart for a touch that's already
|
| - // down. However EventSender allows this to be violated and there's
|
| - // some tests that take advantage of it. There may also be edge
|
| - // cases in the browser where this happens.
|
| - // See http://crbug.com/345372.
|
| - m_targetForTouchID.set(point.id(), node);
|
| -
|
| - TouchAction effectiveTouchAction = TouchActionUtil::computeEffectiveTouchAction(*node);
|
| - if (effectiveTouchAction != TouchActionAuto)
|
| - m_frame->page()->chromeClient().setTouchAction(effectiveTouchAction);
|
| - }
|
| - }
|
| -
|
| - m_touchPressed = !allTouchReleased;
|
| -
|
| - // If there's no document receiving touch events, or no handlers on the
|
| - // document set to receive the events, then we can skip all the rest of
|
| - // this work.
|
| - if (!m_touchSequenceDocument || !m_touchSequenceDocument->frameHost() || !hasTouchHandlers(m_touchSequenceDocument->frameHost()->eventHandlerRegistry()) || !m_touchSequenceDocument->frame()) {
|
| - if (allTouchReleased) {
|
| - m_touchSequenceDocument.clear();
|
| - m_touchSequenceUserGestureToken.clear();
|
| - }
|
| - return WebInputEventResult::NotHandled;
|
| - }
|
| -
|
| - // Whether a touch should be considered a "user gesture" or not is a tricky question.
|
| - // https://docs.google.com/document/d/1oF1T3O7_E4t1PYHV6gyCwHxOi3ystm0eSL5xZu7nvOg/edit#
|
| - // TODO(rbyers): Disable user gesture in some cases but retain logging for now (crbug.com/582140).
|
| - OwnPtr<UserGestureIndicator> gestureIndicator;
|
| - if (event.touchPoints().size() == 1
|
| - && event.touchPoints()[0].state() == PlatformTouchPoint::TouchReleased
|
| - && !event.causesScrollingIfUncanceled()) {
|
| - // This is a touchend corresponding to a tap, definitely a user gesture. So don't supply
|
| - // a UserGestureUtilizedCallback.
|
| - gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessingUserGesture));
|
| - } else {
|
| - // This is some other touch event that perhaps shouldn't be considered a user gesture. So
|
| - // use a UserGestureUtilizedCallback to get metrics / deprecation warnings.
|
| - if (m_touchSequenceUserGestureToken)
|
| - gestureIndicator = adoptPtr(new UserGestureIndicator(m_touchSequenceUserGestureToken.release(), &m_touchSequenceDocument->frame()->eventHandler()));
|
| - else
|
| - gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessingUserGesture, &m_touchSequenceDocument->frame()->eventHandler()));
|
| - m_touchSequenceUserGestureToken = UserGestureIndicator::currentToken();
|
| - }
|
| -
|
| - // Compute and store the common info used by both PointerEvent and TouchEvent.
|
| - HeapVector<TouchInfo> touchInfos(points.size());
|
| -
|
| - for (unsigned i = 0; i < points.size(); ++i) {
|
| - const PlatformTouchPoint& point = points[i];
|
| - PlatformTouchPoint::TouchState pointState = point.state();
|
| - EventTarget* touchTarget = nullptr;
|
| - String regionID;
|
| -
|
| - if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) {
|
| - // The target should be the original target for this touch, so get
|
| - // it from the hashmap. As it's a release or cancel we also remove
|
| - // it from the map.
|
| - touchTarget = m_targetForTouchID.take(point.id());
|
| - regionID = m_regionForTouchID.take(point.id());
|
| - } else {
|
| - // No hittest is performed on move or stationary, since the target
|
| - // is not allowed to change anyway.
|
| - touchTarget = m_targetForTouchID.get(point.id());
|
| - regionID = m_regionForTouchID.get(point.id());
|
| - }
|
| -
|
| - LocalFrame* targetFrame = nullptr;
|
| - bool knownTarget = false;
|
| - if (touchTarget) {
|
| - Document& doc = touchTarget->toNode()->document();
|
| - // If the target node has moved to a new document while it was being touched,
|
| - // we can't send events to the new document because that could leak nodes
|
| - // from one document to another. See http://crbug.com/394339.
|
| - if (&doc == m_touchSequenceDocument.get()) {
|
| - targetFrame = doc.frame();
|
| - knownTarget = true;
|
| - }
|
| - }
|
| - if (!knownTarget) {
|
| - // If we don't have a target registered for the point it means we've
|
| - // missed our opportunity to do a hit test for it (due to some
|
| - // optimization that prevented blink from ever seeing the
|
| - // touchstart), or that the touch started outside the active touch
|
| - // sequence document. We should still include the touch in the
|
| - // Touches list reported to the application (eg. so it can
|
| - // differentiate between a one and two finger gesture), but we won't
|
| - // actually dispatch any events for it. Set the target to the
|
| - // Document so that there's some valid node here. Perhaps this
|
| - // should really be LocalDOMWindow, but in all other cases the target of
|
| - // a Touch is a Node so using the window could be a breaking change.
|
| - // Since we know there was no handler invoked, the specific target
|
| - // should be completely irrelevant to the application.
|
| - touchTarget = m_touchSequenceDocument;
|
| - targetFrame = m_touchSequenceDocument->frame();
|
| - }
|
| - ASSERT(targetFrame);
|
| -
|
| - // pagePoint should always be in the target element's document coordinates.
|
| - FloatPoint pagePoint = targetFrame->view()->rootFrameToContents(point.pos());
|
| - float scaleFactor = 1.0f / targetFrame->pageZoomFactor();
|
| -
|
| - TouchInfo& touchInfo = touchInfos[i];
|
| - touchInfo.point = point;
|
| - touchInfo.touchTarget = touchTarget;
|
| - touchInfo.targetFrame = targetFrame;
|
| - touchInfo.adjustedPagePoint = pagePoint.scaledBy(scaleFactor);
|
| - touchInfo.adjustedRadius = point.radius().scaledBy(scaleFactor);
|
| - touchInfo.knownTarget = knownTarget;
|
| - touchInfo.consumed = false;
|
| - touchInfo.region = regionID;
|
| - }
|
| -
|
| - if (newTouchSequence)
|
| - m_pointerEventManager.unblockTouchPointers();
|
| - dispatchPointerEvents(event, touchInfos);
|
| - // Note that the disposition of any pointer events affects only the generation of touch
|
| - // events. If all pointer events were handled (and hence no touch events were fired), that
|
| - // is still equivalent to the touch events going unhandled because pointer event handler
|
| - // don't block scroll gesture generation.
|
| -
|
| - // TODO(crbug.com/507408): If PE handlers always call preventDefault, we won't see TEs until after
|
| - // scrolling starts because the scrolling would suppress upcoming PEs. This sudden "break" in TE
|
| - // suppression can make the visible TEs inconsistent (e.g. touchmove without a touchstart).
|
| -
|
| - return dispatchTouchEvents(event, touchInfos, newTouchSequence, allTouchReleased);
|
| + return m_pointerEventManager.handleTouchEvents(event);
|
| }
|
|
|
| void EventHandler::userGestureUtilized()
|
|
|