[iOS] Dispatch additional events along with pointerdown and pointerup
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Feb 2019 17:52:34 +0000 (17:52 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Feb 2019 17:52:34 +0000 (17:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194776
<rdar://problem/48164284>

Reviewed by Brent Fulgham.

Source/WebCore:

The Pointer Events specification mandates that "pointerover" and "pointerenter" events precede a "pointerdown" event and that "pointerout"
and "pointerleave" events follow a "pointerup" event. We remove the EventHandler::dispatchPointerEventForTouchAtIndex() method and replace
it with a PointerCaptureController::dispatchEventForTouchAtIndex() that can handle the dispatch of such additional events correctly, also
allowing for two PointerCaptureController methods (pointerEventWillBeDispatched and pointerEventWasDispatched) to become private.

Test: pointerevents/ios/over-enter-out-leave.html

* dom/EventNames.h: Add the new "pointerover", "pointerenter", "pointerout" and "pointerleave" event types.
* dom/PointerEvent.h:
* dom/ios/PointerEventIOS.cpp:
(WebCore::PointerEvent::create):
* page/EventHandler.cpp:
(WebCore::EventHandler::dispatchPointerEventForTouchAtIndex): Deleted.
* page/EventHandler.h:
* page/PointerCaptureController.cpp:
(WebCore::PointerCaptureController::dispatchEventForTouchAtIndex): Take the existing code from EventHandler::dispatchPointerEventForTouchAtIndex()
and extend it to dispatch additional events as mandated. Since several events may be dispatched we check whether the dispatch of any of those events
had defaultPrevented() or defaultHanded() return true and return those values as a pair.
(WebCore::PointerCaptureController::pointerEventWasDispatched):
* page/PointerCaptureController.h:

LayoutTests:

Added a new test that checks that "pointerover" and "pointerenter" precede "pointerdown" and that "pointerout" and "pointerleave" follow "pointerup".

* pointerevents/ios/over-enter-out-leave-expected.txt: Added.
* pointerevents/ios/over-enter-out-leave.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@241723 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/pointerevents/ios/over-enter-out-leave-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/over-enter-out-leave.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/EventNames.h
Source/WebCore/dom/PointerEvent.h
Source/WebCore/dom/ios/PointerEventIOS.cpp
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/EventHandler.h
Source/WebCore/page/PointerCaptureController.cpp
Source/WebCore/page/PointerCaptureController.h

index 2c4da96..b6f883e 100644 (file)
@@ -1,3 +1,16 @@
+2019-02-18  Antoine Quint  <graouts@apple.com>
+
+        [iOS] Dispatch additional events along with pointerdown and pointerup
+        https://bugs.webkit.org/show_bug.cgi?id=194776
+        <rdar://problem/48164284>
+
+        Reviewed by Brent Fulgham.
+
+        Added a new test that checks that "pointerover" and "pointerenter" precede "pointerdown" and that "pointerout" and "pointerleave" follow "pointerup".
+
+        * pointerevents/ios/over-enter-out-leave-expected.txt: Added.
+        * pointerevents/ios/over-enter-out-leave.html: Added.
+
 2019-02-18  Sihui Liu  <sihui_liu@apple.com>
 
         IndexedDB: leak IDBDatabase and IDBTransacstion in layout tests
diff --git a/LayoutTests/pointerevents/ios/over-enter-out-leave-expected.txt b/LayoutTests/pointerevents/ios/over-enter-out-leave-expected.txt
new file mode 100644 (file)
index 0000000..6d82f46
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that "pointerover" and "pointerenter" precede "pointerdown" and that "pointerout" and "pointerleave" follow "pointerup". 
+
diff --git a/LayoutTests/pointerevents/ios/over-enter-out-leave.html b/LayoutTests/pointerevents/ios/over-enter-out-leave.html
new file mode 100644 (file)
index 0000000..a1ca1c2
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+</head>
+<body>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../utils.js"></script>
+<script>
+
+'use strict';
+
+target_test({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    const eventTracker = new EventTracker(target, ["pointerover", "pointerenter", "pointerdown", "pointerup", "pointerout", "pointerleave"]);
+
+    const touch = ui.finger();
+    ui.sequence([
+        touch.begin({ x: 100, y: 100 }),
+        touch.end()
+    ]).then(() => {
+        eventTracker.assertMatchesEvents([
+            { type: "pointerover", x: 100, y: 100, isPrimary: true },
+            { type: "pointerenter", x: 100, y: 100, isPrimary: true },
+            { type: "pointerdown", x: 100, y: 100, isPrimary: true },
+            { type: "pointerup", x: 100, y: 100, isPrimary: false },
+            { type: "pointerout", x: 100, y: 100, isPrimary: false },
+            { type: "pointerleave", x: 100, y: 100, isPrimary: false },
+        ]);
+        test.done();
+    });
+}, `Testing that "pointerover" and "pointerenter" precede "pointerdown" and that "pointerout" and "pointerleave" follow "pointerup".`);
+
+</script>
+</body>
+</html>
\ No newline at end of file
index 686a28f..e905b57 100644 (file)
@@ -1,3 +1,32 @@
+2019-02-18  Antoine Quint  <graouts@apple.com>
+
+        [iOS] Dispatch additional events along with pointerdown and pointerup
+        https://bugs.webkit.org/show_bug.cgi?id=194776
+        <rdar://problem/48164284>
+
+        Reviewed by Brent Fulgham.
+
+        The Pointer Events specification mandates that "pointerover" and "pointerenter" events precede a "pointerdown" event and that "pointerout"
+        and "pointerleave" events follow a "pointerup" event. We remove the EventHandler::dispatchPointerEventForTouchAtIndex() method and replace
+        it with a PointerCaptureController::dispatchEventForTouchAtIndex() that can handle the dispatch of such additional events correctly, also
+        allowing for two PointerCaptureController methods (pointerEventWillBeDispatched and pointerEventWasDispatched) to become private.
+
+        Test: pointerevents/ios/over-enter-out-leave.html
+
+        * dom/EventNames.h: Add the new "pointerover", "pointerenter", "pointerout" and "pointerleave" event types.
+        * dom/PointerEvent.h:
+        * dom/ios/PointerEventIOS.cpp:
+        (WebCore::PointerEvent::create):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::dispatchPointerEventForTouchAtIndex): Deleted.
+        * page/EventHandler.h:
+        * page/PointerCaptureController.cpp:
+        (WebCore::PointerCaptureController::dispatchEventForTouchAtIndex): Take the existing code from EventHandler::dispatchPointerEventForTouchAtIndex()
+        and extend it to dispatch additional events as mandated. Since several events may be dispatched we check whether the dispatch of any of those events
+        had defaultPrevented() or defaultHanded() return true and return those values as a pair.
+        (WebCore::PointerCaptureController::pointerEventWasDispatched):
+        * page/PointerCaptureController.h:
+
 2019-02-18  Sihui Liu  <sihui_liu@apple.com>
 
         IndexedDB: leak IDBDatabase and IDBTransacstion in layout tests
index 068014d..f4d7d73 100644 (file)
@@ -206,7 +206,11 @@ namespace WebCore {
     macro(pointerlockerror) \
     macro(pointercancel) \
     macro(pointerdown) \
+    macro(pointerenter) \
+    macro(pointerleave) \
     macro(pointermove) \
+    macro(pointerout) \
+    macro(pointerover) \
     macro(pointerup) \
     macro(popstate) \
     macro(previoustrack) \
index de78093..76d328c 100644 (file)
@@ -83,6 +83,7 @@ public:
 
 #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY)
     static Ref<PointerEvent> create(const PlatformTouchEvent&, unsigned touchIndex, bool isPrimary, Ref<WindowProxy>&&);
+    static Ref<PointerEvent> create(const String& type, const PlatformTouchEvent&, unsigned touchIndex, bool isPrimary, Ref<WindowProxy>&&);
 #endif
 
     static const String& mousePointerType();
index b191d19..6403e51 100644 (file)
@@ -63,6 +63,11 @@ Ref<PointerEvent> PointerEvent::create(const PlatformTouchEvent& event, unsigned
     return adoptRef(*new PointerEvent(pointerEventType(phase), event, phaseIsCancelable(phase), index, isPrimary, WTFMove(view)));
 }
 
+Ref<PointerEvent> PointerEvent::create(const String& type, const PlatformTouchEvent& event, unsigned index, bool isPrimary, Ref<WindowProxy>&& view)
+{
+    return adoptRef(*new PointerEvent(type, event, phaseIsCancelable(event.touchPhaseAtIndex(index)), index, isPrimary, WTFMove(view)));
+}
+
 PointerEvent::PointerEvent(const AtomicString& type, const PlatformTouchEvent& event, IsCancelable isCancelable, unsigned index, bool isPrimary, Ref<WindowProxy>&& view)
     : MouseEvent(type, CanBubble::Yes, isCancelable, IsComposed::Yes, event.timestamp().approximateMonotonicTime(), WTFMove(view), 0, event.touchLocationAtIndex(index), event.touchLocationAtIndex(index), { }, event.modifiers(), 0, 0, nullptr, 0, 0, nullptr, IsSimulated::No, IsTrusted::Yes)
     , m_pointerId(event.touchIdentifierAtIndex(index))
index 2024a76..59fd3e0 100644 (file)
 #include "TouchList.h"
 #endif
 
-#if ENABLE(TOUCH_EVENTS) && ENABLE(POINTER_EVENTS)
-#include "PointerCaptureController.h"
-#include "PointerEvent.h"
-#endif
-
 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
 #include "PlatformTouchEvent.h"
 #endif
@@ -4271,18 +4266,6 @@ bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent
 }
 #endif // ENABLE(TOUCH_EVENTS)
 
-#if ENABLE(TOUCH_EVENTS) && ENABLE(POINTER_EVENTS)
-Ref<PointerEvent> EventHandler::dispatchPointerEventForTouchAtIndex(EventTarget& target, const PlatformTouchEvent& platformTouchEvent, unsigned index, bool isPrimary)
-{
-    auto& pointerCaptureController = m_frame.page()->pointerCaptureController();
-    auto pointerEvent = PointerEvent::create(platformTouchEvent, index, isPrimary, m_frame.windowProxy());
-    pointerCaptureController.pointerEventWillBeDispatched(pointerEvent, &target);
-    target.dispatchEvent(pointerEvent);
-    pointerCaptureController.pointerEventWasDispatched(pointerEvent);
-    return pointerEvent;
-}
-#endif // ENABLE(TOUCH_EVENTS) && ENABLE(POINTER_EVENTS)
-
 void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event)
 {
     m_mousePositionIsUnknown = false;
index 43720db..9ea9e0c 100644 (file)
@@ -81,7 +81,6 @@ class PlatformGestureEvent;
 class PlatformKeyboardEvent;
 class PlatformTouchEvent;
 class PlatformWheelEvent;
-class PointerEvent;
 class RenderBox;
 class RenderElement;
 class RenderLayer;
@@ -320,10 +319,6 @@ public:
     WEBCORE_EXPORT bool handleTouchEvent(const PlatformTouchEvent&);
 #endif
 
-#if ENABLE(TOUCH_EVENTS) && ENABLE(POINTER_EVENTS)
-    Ref<PointerEvent> dispatchPointerEventForTouchAtIndex(EventTarget&, const PlatformTouchEvent&, unsigned, bool isPrimary);
-#endif
-
     bool useHandCursor(Node*, bool isOverLink, bool shiftKey);
     void updateCursor();
 
index edcc3ff..267887e 100644 (file)
@@ -134,6 +134,47 @@ bool PointerCaptureController::hasCancelledPointerEventForIdentifier(PointerID p
     return iterator != m_activePointerIdsToCapturingData.end() && iterator->value.cancelled;
 }
 
+#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY)
+std::pair<bool, bool> PointerCaptureController::dispatchEventForTouchAtIndex(EventTarget& target, const PlatformTouchEvent& platformTouchEvent, unsigned index, bool isPrimary, WindowProxy& view)
+{
+    bool defaultPrevented = false;
+    bool defaultHandled = false;
+
+    auto dispatchEvent = [&](const String& type) {
+        auto event = PointerEvent::create(type, platformTouchEvent, index, isPrimary, view);
+        target.dispatchEvent(event);
+        defaultPrevented |= event->defaultPrevented();
+        defaultHandled |= event->defaultHandled();
+    };
+
+    auto pointerEvent = PointerEvent::create(platformTouchEvent, index, isPrimary, view);
+
+    if (pointerEvent->type() == eventNames().pointerdownEvent) {
+        // https://w3c.github.io/pointerevents/#the-pointerdown-event
+        // For input devices that do not support hover, a user agent MUST also fire a pointer event named pointerover followed by a pointer event named
+        // pointerenter prior to dispatching the pointerdown event.
+        dispatchEvent(eventNames().pointeroverEvent);
+        dispatchEvent(eventNames().pointerenterEvent);
+    }
+
+    pointerEventWillBeDispatched(pointerEvent, &target);
+    target.dispatchEvent(pointerEvent);
+    defaultPrevented |= pointerEvent->defaultPrevented();
+    defaultHandled |= pointerEvent->defaultHandled();
+    pointerEventWasDispatched(pointerEvent);
+
+    if (pointerEvent->type() == eventNames().pointerupEvent) {
+        // https://w3c.github.io/pointerevents/#the-pointerup-event
+        // For input devices that do not support hover, a user agent MUST also fire a pointer event named pointerout followed by a
+        // pointer event named pointerleave after dispatching the pointerup event.
+        dispatchEvent(eventNames().pointeroutEvent);
+        dispatchEvent(eventNames().pointerleaveEvent);
+    }
+
+    return { defaultPrevented, defaultHandled };
+}
+#endif
+
 void PointerCaptureController::pointerEventWillBeDispatched(const PointerEvent& event, EventTarget* target)
 {
     // https://w3c.github.io/pointerevents/#implicit-pointer-capture
@@ -170,7 +211,7 @@ void PointerCaptureController::pointerEventWasDispatched(const PointerEvent& eve
         // Pointer Capture steps to fire lostpointercapture if necessary.
         if (event.type() == eventNames().pointerupEvent)
             capturingData.pendingTargetOverride = nullptr;
-        
+
         // When the pointer capture target override is no longer connected, the pending pointer capture target override and pointer
         // capture target override nodes SHOULD be cleared and also a PointerEvent named lostpointercapture corresponding to the captured
         // pointer SHOULD be fired at the document.
index 5cfb1e9..b6184cb 100644 (file)
@@ -47,10 +47,13 @@ public:
 
     void pointerLockWasApplied();
 
+#if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY)
+    std::pair<bool, bool> dispatchEventForTouchAtIndex(EventTarget&, const PlatformTouchEvent&, unsigned, bool isPrimary, WindowProxy&);
+#endif
+
     void touchEndedOrWasCancelledForIdentifier(PointerID);
     bool hasCancelledPointerEventForIdentifier(PointerID);
-    void pointerEventWillBeDispatched(const PointerEvent&, EventTarget*);
-    void pointerEventWasDispatched(const PointerEvent&);
+    void dispatchEvent(PointerEvent&, EventTarget*);
     WEBCORE_EXPORT void cancelPointer(PointerID, const IntPoint&);
 
 private:
@@ -61,6 +64,8 @@ private:
         bool cancelled { false };
     };
 
+    void pointerEventWillBeDispatched(const PointerEvent&, EventTarget*);
+    void pointerEventWasDispatched(const PointerEvent&);
     void processPendingPointerCapture(const PointerEvent&);
 
     Page& m_page;