[Pointer Events] The pointerenter and pointerleave events target the wrong element...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 May 2019 16:50:37 +0000 (16:50 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 May 2019 16:50:37 +0000 (16:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197881
<rdar://problem/50187657>

Patch by Antoine Quint <graouts@apple.com> on 2019-05-14
Reviewed by Dean Jackson.

Source/WebCore:

Test: pointerevents/ios/enter-leave-target.html

The "pointerenter" and "pointerleave" should target the element on which the event listener was added and not
the element that would otherwise hit test. This matches the behavior of "mouseenter" and "mouseleave" on macOS.

* page/PointerCaptureController.cpp:
(WebCore::PointerCaptureController::dispatchEventForTouchAtIndex):

LayoutTests:

Add a test where we tap an element that is the child of another element where the parent is the element with the "pointerenter"
and "pointerleave" events registered. The test shows that we correctly set the target to the parent element and not the child.

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

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

LayoutTests/ChangeLog
LayoutTests/pointerevents/ios/enter-leave-target-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/enter-leave-target.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/PointerCaptureController.cpp

index ec8613e..452ebf2 100644 (file)
@@ -1,3 +1,17 @@
+2019-05-14  Antoine Quint  <graouts@apple.com>
+
+        [Pointer Events] The pointerenter and pointerleave events target the wrong element on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=197881
+        <rdar://problem/50187657>
+
+        Reviewed by Dean Jackson.
+
+        Add a test where we tap an element that is the child of another element where the parent is the element with the "pointerenter"
+        and "pointerleave" events registered. The test shows that we correctly set the target to the parent element and not the child.
+
+        * pointerevents/ios/enter-leave-target-expected.txt: Added.
+        * pointerevents/ios/enter-leave-target.html: Added.
+
 2019-05-14  Daniel Bates  <dabates@apple.com>
 
         [iOS] Cannot scroll to beginning of document after scrolling to end of document and vice versa via key commands
diff --git a/LayoutTests/pointerevents/ios/enter-leave-target-expected.txt b/LayoutTests/pointerevents/ios/enter-leave-target-expected.txt
new file mode 100644 (file)
index 0000000..890c4e8
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that "pointerenter" and "pointerleave" have the element on which the event listener was added as their target. 
+
diff --git a/LayoutTests/pointerevents/ios/enter-leave-target.html b/LayoutTests/pointerevents/ios/enter-leave-target.html
new file mode 100644 (file)
index 0000000..d39f43f
--- /dev/null
@@ -0,0 +1,44 @@
+<!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) => {
+    // Add a child element covering the same bounds as the target so that it hit tests when tapping.
+    target.appendChild(document.createElement("div")).setAttribute("style", "position: absolute; width: 100%; height: 100%;");
+
+    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=197882
+    // [Pointer Events] Listening to a "pointerover", "pointerenter", "pointerout" or "pointerleave" event alone does not fire the event on iOS
+    target.addEventListener("pointerdown", event => { });
+
+    const entered = new Promise(resolve => {
+        target.addEventListener("pointerenter", event => {
+            assert_equals(event.target, target, `The ${event.type} event target matches the element on which the event listener was added.`);
+            resolve();
+        });
+    });
+
+    const left = new Promise(resolve => {
+        target.addEventListener("pointerleave", event => {
+            assert_equals(event.target, target, `The ${event.type} event target matches the element on which the event listener was added.`);
+            resolve();
+        });
+    });
+
+    const tapped = ui.tap({ x: 100, y: 100 });
+
+    Promise.all([entered, left, tapped]).then(() => test.done());
+}, `Testing that "pointerenter" and "pointerleave" have the element on which the event listener was added as their target.`);
+
+</script>
+</body>
+</html>
\ No newline at end of file
index e76c147..e01a29c 100644 (file)
@@ -1,3 +1,19 @@
+2019-05-14  Antoine Quint  <graouts@apple.com>
+
+        [Pointer Events] The pointerenter and pointerleave events target the wrong element on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=197881
+        <rdar://problem/50187657>
+
+        Reviewed by Dean Jackson.
+
+        Test: pointerevents/ios/enter-leave-target.html
+
+        The "pointerenter" and "pointerleave" should target the element on which the event listener was added and not
+        the element that would otherwise hit test. This matches the behavior of "mouseenter" and "mouseleave" on macOS.
+
+        * page/PointerCaptureController.cpp:
+        (WebCore::PointerCaptureController::dispatchEventForTouchAtIndex):
+
 2019-05-14  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         [CG] Adding support for HEIF-sequence ('public.heics') images
index a0fd087..c945361 100644 (file)
@@ -164,6 +164,26 @@ void PointerCaptureController::dispatchEventForTouchAtIndex(EventTarget& target,
         target.dispatchEvent(PointerEvent::create(type, platformTouchEvent, index, isPrimary, view));
     };
 
+    auto dispatchEnterOrLeaveEvent = [&](const String& type) {
+        if (!is<Element>(&target))
+            return;
+
+        auto* targetElement = &downcast<Element>(target);
+
+        bool hasCapturingListenerInHierarchy = false;
+        for (ContainerNode* curr = targetElement; curr; curr = curr->parentInComposedTree()) {
+            if (curr->hasCapturingEventListeners(type)) {
+                hasCapturingListenerInHierarchy = true;
+                break;
+            }
+        }
+
+        for (Element* element = &downcast<Element>(target); element; element = element->parentElementInComposedTree()) {
+            if (hasCapturingListenerInHierarchy || element->hasEventListeners(type))
+                element->dispatchEvent(PointerEvent::create(type, platformTouchEvent, index, isPrimary, view));
+        }
+    };
+
     auto pointerEvent = PointerEvent::create(platformTouchEvent, index, isPrimary, view);
 
     if (pointerEvent->type() == eventNames().pointerdownEvent) {
@@ -171,7 +191,7 @@ void PointerCaptureController::dispatchEventForTouchAtIndex(EventTarget& target,
         // 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);
+        dispatchEnterOrLeaveEvent(eventNames().pointerenterEvent);
     }
 
     pointerEventWillBeDispatched(pointerEvent, &target);
@@ -183,7 +203,7 @@ void PointerCaptureController::dispatchEventForTouchAtIndex(EventTarget& target,
         // 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);
+        dispatchEnterOrLeaveEvent(eventNames().pointerleaveEvent);
     }
 }
 #endif