| Index: third_party/WebKit/Source/core/input/PointerEventManager.cpp
|
| diff --git a/third_party/WebKit/Source/core/input/PointerEventManager.cpp b/third_party/WebKit/Source/core/input/PointerEventManager.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ad351b8762fa6d0ed2a5435d08209cbfa221736e
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/input/PointerEventManager.cpp
|
| @@ -0,0 +1,359 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "core/input/PointerEventManager.h"
|
| +
|
| +#include "core/dom/shadow/FlatTreeTraversal.h"
|
| +#include "core/events/MouseEvent.h"
|
| +#include "core/input/EventHandler.h"
|
| +
|
| +namespace blink {
|
| +
|
| +namespace {
|
| +
|
| +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;
|
| + }
|
| +}
|
| +
|
| +const AtomicString& pointerEventNameForMouseEventName(const AtomicString& mouseEventName)
|
| +{
|
| +#define RETURN_CORRESPONDING_PE_NAME(eventSuffix) \
|
| + if (mouseEventName == EventTypeNames::mouse##eventSuffix) {\
|
| + return EventTypeNames::pointer##eventSuffix;\
|
| + }
|
| +
|
| + RETURN_CORRESPONDING_PE_NAME(down);
|
| + RETURN_CORRESPONDING_PE_NAME(enter);
|
| + RETURN_CORRESPONDING_PE_NAME(leave);
|
| + RETURN_CORRESPONDING_PE_NAME(move);
|
| + RETURN_CORRESPONDING_PE_NAME(out);
|
| + RETURN_CORRESPONDING_PE_NAME(over);
|
| + RETURN_CORRESPONDING_PE_NAME(up);
|
| +
|
| +#undef RETURN_CORRESPONDING_PE_NAME
|
| +
|
| + ASSERT_NOT_REACHED();
|
| + return emptyAtom;
|
| +}
|
| +
|
| +bool isInDocument(PassRefPtrWillBeRawPtr<EventTarget> n)
|
| +{
|
| + return n && n->toNode() && n->toNode()->inDocument();
|
| +}
|
| +
|
| +WebInputEventResult dispatchPointerEvent(
|
| + PassRefPtrWillBeRawPtr<EventTarget> target,
|
| + PassRefPtrWillBeRawPtr<PointerEvent> pointerevent,
|
| + bool checkForListener = false)
|
| +{
|
| + if (!RuntimeEnabledFeatures::pointerEventEnabled())
|
| + return WebInputEventResult::NotHandled;
|
| + if (!checkForListener || target->hasEventListeners(pointerevent->type())) {
|
| + bool dispatchResult = target->dispatchEvent(pointerevent);
|
| + return EventHandler::eventToEventResult(pointerevent, dispatchResult);
|
| + }
|
| + return WebInputEventResult::NotHandled;
|
| +}
|
| +
|
| +WebInputEventResult dispatchMouseEvent(
|
| + PassRefPtrWillBeRawPtr<EventTarget> target,
|
| + const AtomicString& mouseEventType,
|
| + const PlatformMouseEvent& mouseEvent,
|
| + PassRefPtrWillBeRawPtr<EventTarget> relatedTarget,
|
| + int detail = 0,
|
| + bool checkForListener = false)
|
| +{
|
| + if (target->toNode()
|
| + && (!checkForListener || target->hasEventListeners(mouseEventType))) {
|
| + RefPtrWillBeRawPtr<Node> targetNode = target->toNode();
|
| + RefPtrWillBeRawPtr<MouseEvent> event = MouseEvent::create(mouseEventType,
|
| + targetNode->document().domWindow(), mouseEvent, detail,
|
| + relatedTarget ? relatedTarget->toNode() : nullptr);
|
| + bool res = target->dispatchEvent(event);
|
| + return EventHandler::eventToEventResult(event, res);
|
| + }
|
| + return WebInputEventResult::NotHandled;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +PassRefPtrWillBeRawPtr<Node> PointerEventManager::getEffectiveTargetForPointerEvent(
|
| + PassRefPtrWillBeRawPtr<Node> target,
|
| + PassRefPtrWillBeRawPtr<PointerEvent> pointerEvent)
|
| +{
|
| + // TODO(nzolghadr): Add APIs to set the capturing nodes and return the correct node here
|
| + (void) pointerEvent;
|
| + return target;
|
| +}
|
| +
|
| +// Sends node transition events (pointer|mouse)(out|leave|over|enter) to the corresponding targets
|
| +void PointerEventManager::sendNodeTransitionEvents(
|
| + PassRefPtrWillBeRawPtr<Node> exitedNode,
|
| + PassRefPtrWillBeRawPtr<Node> enteredNode,
|
| + const PlatformMouseEvent& mouseEvent,
|
| + PassRefPtrWillBeRawPtr<AbstractView> view)
|
| +{
|
| + // Pointer event type does not matter as it will be overridden in the sendNodeTransitionEvents
|
| + sendNodeTransitionEvents(exitedNode, enteredNode,
|
| + m_pointerEventFactory.create(EventTypeNames::pointerout, mouseEvent, nullptr, view),
|
| + mouseEvent, true);
|
| +}
|
| +
|
| +void PointerEventManager::sendNodeTransitionEvents(
|
| + PassRefPtrWillBeRawPtr<EventTarget> exitedTarget,
|
| + PassRefPtrWillBeRawPtr<EventTarget> enteredTarget,
|
| + PassRefPtrWillBeRawPtr<PointerEvent> pointerEvent,
|
| + const PlatformMouseEvent& mouseEvent, bool sendMouseEvent)
|
| +{
|
| + if (exitedTarget == enteredTarget)
|
| + return;
|
| +
|
| + // Dispatch pointerout/mouseout events
|
| + if (isInDocument(exitedTarget)) {
|
| + dispatchPointerEvent(exitedTarget, m_pointerEventFactory.create(
|
| + pointerEvent, EventTypeNames::pointerout, enteredTarget));
|
| + if (sendMouseEvent) {
|
| + dispatchMouseEvent(exitedTarget,
|
| + EventTypeNames::mouseout, mouseEvent, enteredTarget);
|
| + }
|
| + }
|
| +
|
| + // Create lists of all exited/entered ancestors, locate the common ancestor
|
| + WillBeHeapVector<RefPtrWillBeMember<Node>, 32> exitedAncestors;
|
| + WillBeHeapVector<RefPtrWillBeMember<Node>, 32> enteredAncestors;
|
| + if (isInDocument(exitedTarget)) {
|
| + RefPtrWillBeRawPtr<Node> exitedNode = exitedTarget->toNode();
|
| + exitedNode->updateDistribution();
|
| + for (RefPtrWillBeRawPtr<Node> node = exitedNode; node; node = FlatTreeTraversal::parent(*node))
|
| + exitedAncestors.append(node);
|
| + }
|
| +
|
| + if (isInDocument(enteredTarget)) {
|
| + RefPtrWillBeRawPtr<Node> enteredNode = enteredTarget->toNode();
|
| + enteredNode->updateDistribution();
|
| + for (RefPtrWillBeRawPtr<Node> node = enteredNode; node; node = FlatTreeTraversal::parent(*node))
|
| + enteredAncestors.append(node);
|
| + }
|
| +
|
| + // A note on mouseenter and mouseleave: These are non-bubbling events, and they are dispatched if there
|
| + // is a capturing event handler on an ancestor or a normal event handler on the element itself. This special
|
| + // handling is necessary to avoid O(n^2) capturing event handler checks.
|
| + //
|
| + // Note, however, that this optimization can possibly cause some unanswered/missing/redundant mouseenter or
|
| + // mouseleave events in certain contrived eventhandling scenarios, e.g., when:
|
| + // - the mouseleave handler for a node sets the only capturing-mouseleave-listener in its ancestor, or
|
| + // - DOM mods in any mouseenter/mouseleave handler changes the common ancestor of exited & entered nodes, etc.
|
| + // We think the spec specifies a "frozen" state to avoid such corner cases (check the discussion on "candidate event
|
| + // listeners" at http://www.w3.org/TR/uievents), but our code below preserves one such behavior from past only to
|
| + // match Firefox and IE behavior.
|
| + //
|
| + // TODO(mustaq): Confirm spec conformance, double-check with other browsers.
|
| +
|
| + size_t numExitedAncestors = exitedAncestors.size();
|
| + size_t numEnteredAncestors = enteredAncestors.size();
|
| +
|
| + size_t exitedAncestorIndex = numExitedAncestors;
|
| + size_t enteredAncestorIndex = numEnteredAncestors;
|
| + for (size_t i = 0; i < numExitedAncestors; i++) {
|
| + for (size_t j = 0; j < numEnteredAncestors; j++) {
|
| + if (exitedAncestors[i] == enteredAncestors[j]) {
|
| + exitedAncestorIndex = i;
|
| + enteredAncestorIndex = j;
|
| + break;
|
| + }
|
| + }
|
| + if (exitedAncestorIndex < exitedAncestors.size())
|
| + break;
|
| + }
|
| +
|
| + bool exitedNodeHasCapturingAncestor = false;
|
| + for (size_t j = 0; j < exitedAncestors.size(); j++) {
|
| + if (exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::mouseleave)
|
| + || (RuntimeEnabledFeatures::pointerEventEnabled()
|
| + && exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::pointerleave)))
|
| + exitedNodeHasCapturingAncestor = true;
|
| + }
|
| +
|
| + // Dispatch pointerleave/mouseleave events, in child-to-parent order.
|
| + for (size_t j = 0; j < exitedAncestorIndex; j++) {
|
| + dispatchPointerEvent(exitedAncestors[j].get(),
|
| + m_pointerEventFactory.create(
|
| + pointerEvent, EventTypeNames::pointerleave, enteredTarget),
|
| + !exitedNodeHasCapturingAncestor);
|
| + if (sendMouseEvent) {
|
| + dispatchMouseEvent(exitedAncestors[j].get(),
|
| + EventTypeNames::mouseleave, mouseEvent, enteredTarget,
|
| + 0, !exitedNodeHasCapturingAncestor);
|
| + }
|
| + }
|
| +
|
| + // Dispatch pointerover/mouseover.
|
| + if (isInDocument(enteredTarget)) {
|
| + dispatchPointerEvent(enteredTarget, m_pointerEventFactory.create(
|
| + pointerEvent, EventTypeNames::pointerover, exitedTarget));
|
| + if (sendMouseEvent) {
|
| + dispatchMouseEvent(enteredTarget,
|
| + EventTypeNames::mouseover, mouseEvent, exitedTarget);
|
| + }
|
| + }
|
| +
|
| + // Defer locating capturing pointeenter/mouseenter listener until /after/ dispatching the leave events because
|
| + // the leave handlers might set a capturing enter handler.
|
| + bool enteredNodeHasCapturingAncestor = false;
|
| + for (size_t i = 0; i < enteredAncestors.size(); i++) {
|
| + if (enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::mouseenter)
|
| + || (RuntimeEnabledFeatures::pointerEventEnabled()
|
| + && enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::pointerenter)))
|
| + enteredNodeHasCapturingAncestor = true;
|
| + }
|
| +
|
| + // Dispatch pointerenter/mouseenter events, in parent-to-child order.
|
| + for (size_t i = enteredAncestorIndex; i > 0; i--) {
|
| + dispatchPointerEvent(enteredAncestors[i-1].get(),
|
| + m_pointerEventFactory.create(
|
| + pointerEvent, EventTypeNames::pointerenter, exitedTarget),
|
| + !enteredNodeHasCapturingAncestor);
|
| + if (sendMouseEvent) {
|
| + dispatchMouseEvent(enteredAncestors[i-1].get(),
|
| + EventTypeNames::mouseenter, mouseEvent, exitedTarget,
|
| + 0, !enteredNodeHasCapturingAncestor);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void PointerEventManager::setNodeUnderPointer(
|
| + PassRefPtrWillBeRawPtr<PointerEvent> pointerevent,
|
| + PassRefPtrWillBeRawPtr<EventTarget> target)
|
| +{
|
| + if (m_nodeUnderPointer.contains(pointerevent->pointerId())) {
|
| + sendNodeTransitionEvents(m_nodeUnderPointer.get(
|
| + pointerevent->pointerId()), target, pointerevent);
|
| + if (!target)
|
| + m_nodeUnderPointer.remove(pointerevent->pointerId());
|
| + else
|
| + m_nodeUnderPointer.set(pointerevent->pointerId(), target);
|
| + } else if (target) {
|
| + sendNodeTransitionEvents(nullptr, target, pointerevent);
|
| + m_nodeUnderPointer.add(pointerevent->pointerId(), target);
|
| + }
|
| +}
|
| +
|
| +void PointerEventManager::sendTouchCancelPointerEvent(PassRefPtrWillBeRawPtr<EventTarget> target,
|
| + const PlatformTouchPoint& point)
|
| +{
|
| + RefPtrWillBeRawPtr<PointerEvent> pointerEvent =
|
| + m_pointerEventFactory.createPointerCancel(point);
|
| +
|
| + // TODO(nzolghadr): crbug.com/579553 dealing with implicit touch capturing vs pointer event capturing
|
| + target->dispatchEvent(pointerEvent.get());
|
| +
|
| + m_pointerEventFactory.remove(pointerEvent);
|
| + setNodeUnderPointer(pointerEvent, nullptr);
|
| +}
|
| +
|
| +WebInputEventResult PointerEventManager::sendTouchPointerEvent(
|
| + PassRefPtrWillBeRawPtr<EventTarget> target,
|
| + const PlatformTouchPoint& touchPoint, PlatformEvent::Modifiers modifiers,
|
| + const double width, const double height,
|
| + const double clientX, const double clientY)
|
| +{
|
| + RefPtrWillBeRawPtr<PointerEvent> pointerEvent =
|
| + m_pointerEventFactory.create(
|
| + pointerEventNameForTouchPointState(touchPoint.state()),
|
| + touchPoint, modifiers, width, height, clientX, clientY);
|
| +
|
| + setNodeUnderPointer(pointerEvent, target);
|
| +
|
| + // TODO(nzolghadr): crbug.com/579553 dealing with implicit touch capturing vs pointer event capturing
|
| + WebInputEventResult result = dispatchPointerEvent(target, pointerEvent.get());
|
| +
|
| + if (touchPoint.state() == PlatformTouchPoint::TouchReleased
|
| + || touchPoint.state() == PlatformTouchPoint::TouchCancelled) {
|
| + m_pointerEventFactory.remove(pointerEvent);
|
| + setNodeUnderPointer(pointerEvent, nullptr);
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +WebInputEventResult PointerEventManager::sendMousePointerEvent(
|
| + PassRefPtrWillBeRawPtr<Node> target, const AtomicString& mouseEventType,
|
| + int clickCount, const PlatformMouseEvent& mouseEvent,
|
| + PassRefPtrWillBeRawPtr<Node> relatedTarget,
|
| + PassRefPtrWillBeRawPtr<AbstractView> view)
|
| +{
|
| + AtomicString pointerEventType =
|
| + pointerEventNameForMouseEventName(mouseEventType);
|
| + unsigned short pointerButtonsPressed =
|
| + MouseEvent::platformModifiersToButtons(mouseEvent.modifiers());
|
| +
|
| + // Make sure chorded buttons fire pointermove instead of pointerup/pointerdown.
|
| + if ((pointerEventType == EventTypeNames::pointerdown && (pointerButtonsPressed & ~MouseEvent::buttonToButtons(mouseEvent.button())) != 0)
|
| + || (pointerEventType == EventTypeNames::pointerup && pointerButtonsPressed != 0))
|
| + pointerEventType = EventTypeNames::pointermove;
|
| +
|
| + RefPtrWillBeRawPtr<PointerEvent> pointerEvent =
|
| + m_pointerEventFactory.create(pointerEventType, mouseEvent,
|
| + relatedTarget, view);
|
| +
|
| + RefPtrWillBeRawPtr<Node> effectiveTarget =
|
| + getEffectiveTargetForPointerEvent(target, pointerEvent);
|
| +
|
| + WebInputEventResult result =
|
| + dispatchPointerEvent(effectiveTarget, pointerEvent);
|
| +
|
| + if (result != WebInputEventResult::NotHandled
|
| + && pointerEventType == EventTypeNames::pointerdown)
|
| + m_preventMouseEventForPointerTypeMouse = true;
|
| +
|
| + if (!m_preventMouseEventForPointerTypeMouse) {
|
| + result = EventHandler::mergeEventResult(result,
|
| + dispatchMouseEvent(effectiveTarget, mouseEventType, mouseEvent,
|
| + nullptr, clickCount));
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +PointerEventManager::PointerEventManager()
|
| +{
|
| + clear();
|
| +}
|
| +
|
| +PointerEventManager::~PointerEventManager()
|
| +{
|
| +}
|
| +void PointerEventManager::clearPreventMouseEventForPointerTypeMouse()
|
| +{
|
| + m_preventMouseEventForPointerTypeMouse = false;
|
| +}
|
| +void PointerEventManager::clear()
|
| +{
|
| + m_preventMouseEventForPointerTypeMouse = false;
|
| + m_pointerEventFactory.clear();
|
| + m_nodeUnderPointer.clear();
|
| +}
|
| +
|
| +DEFINE_TRACE(PointerEventManager)
|
| +{
|
| + visitor->trace(m_nodeUnderPointer);
|
| +}
|
| +
|
| +
|
| +} // namespace blink
|
|
|