PointerEvents should not require touch event listeners to be registered
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Nov 2018 09:09:34 +0000 (09:09 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Nov 2018 09:09:34 +0000 (09:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191333
<rdar://problem/45857523>

Reviewed by Dean Jackson.

Source/WebCore:

Tests: pointerevents/ios/pointer-events-dispatch-on-touch.html
       pointerevents/ios/pointer-events-prevent-default.html

* dom/EventNames.h:
(WebCore::EventNames::isTouchEventType const):
(WebCore::EventNames::touchAndPointerEventNames const):
(WebCore::EventNames::touchEventNames const): Deleted.
* dom/Node.cpp:
(WebCore::Node::moveNodeToNewDocument):

Source/WebKit:

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::updateTouchEventTracking):
(WebKit::WebPageProxy::touchEventTrackingType const):
(WebKit::WebPageProxy::handleTouchEventSynchronously):
(WebKit::WebPageProxy::handleTouchEventAsynchronously):
(WebKit::WebPageProxy::handleTouchEvent):
(WebKit::WebPageProxy::resetState):
* UIProcess/WebPageProxy.h:

LayoutTests:

Add two iOS tests that check that we dispatch pointer events when only those events are registered
and that we correctly account for calls to preventDefault(). To support pointer events test, we add
a new utility where we will be adding user interaction functions that should allow these tests to be
submitted to the WPT repository provided browsers implement their own versions of the UIController.

* TestExpectations:
* pointerevents/ios/pointer-events-dispatch-on-touch-expected.txt: Added.
* pointerevents/ios/pointer-events-dispatch-on-touch.html: Added.
* pointerevents/ios/pointer-events-prevent-default-expected.txt: Added.
* pointerevents/ios/pointer-events-prevent-default.html: Added.
* pointerevents/utils.js: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/pointer-events-prevent-default-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/pointer-events-prevent-default.html [new file with mode: 0644]
LayoutTests/pointerevents/utils.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/EventNames.h
Source/WebCore/dom/Node.cpp
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h

index 1ea555c..8c59982 100644 (file)
@@ -1,3 +1,23 @@
+2018-11-16  Antoine Quint  <graouts@apple.com>
+
+        PointerEvents should not require touch event listeners to be registered
+        https://bugs.webkit.org/show_bug.cgi?id=191333
+        <rdar://problem/45857523>
+
+        Reviewed by Dean Jackson.
+
+        Add two iOS tests that check that we dispatch pointer events when only those events are registered
+        and that we correctly account for calls to preventDefault(). To support pointer events test, we add
+        a new utility where we will be adding user interaction functions that should allow these tests to be
+        submitted to the WPT repository provided browsers implement their own versions of the UIController.
+
+        * TestExpectations:
+        * pointerevents/ios/pointer-events-dispatch-on-touch-expected.txt: Added.
+        * pointerevents/ios/pointer-events-dispatch-on-touch.html: Added.
+        * pointerevents/ios/pointer-events-prevent-default-expected.txt: Added.
+        * pointerevents/ios/pointer-events-prevent-default.html: Added.
+        * pointerevents/utils.js: Added.
+
 2018-11-15  Zalan Bujtas  <zalan@apple.com>
 
         [iOS] Do not get stuck in indeterminate content observation state.
index 95a33f7..c7f89a3 100644 (file)
@@ -49,6 +49,7 @@ http/tests/gzip-content-encoding [ Skip ]
 http/tests/cookies/same-site [ Skip ]
 system-preview [ Skip ]
 editing/images [ Skip ]
+pointerevents/ios [ Skip ]
 
 # window.showModalDialog is only tested in DumpRenderTree on Mac.
 editing/execCommand/show-modal-dialog-during-execCommand.html [ Skip ]
diff --git a/LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch-expected.txt b/LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch-expected.txt
new file mode 100644 (file)
index 0000000..82128b9
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Pointer events get dispatched in response to a touch. 
+
diff --git a/LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch.html b/LayoutTests/pointerevents/ios/pointer-events-dispatch-on-touch.html
new file mode 100644 (file)
index 0000000..e11971d
--- /dev/null
@@ -0,0 +1,28 @@
+<!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((target, test) => {
+    target.addEventListener("pointerdown", event => {
+        assert_true(event instanceof PointerEvent);
+        assert_equals(event.type, "pointerdown");
+        assert_equals(event.clientX, 50);
+        assert_equals(event.clientY, 50);
+        test.done();
+    });
+    ui.beginTouches({ x: 50, y: 50 });
+}, "Pointer events get dispatched in response to a touch.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/pointer-events-prevent-default-expected.txt b/LayoutTests/pointerevents/ios/pointer-events-prevent-default-expected.txt
new file mode 100644 (file)
index 0000000..aad5cf2
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Scrolling of content can be disabled by calling preventDefault() on a pointer event. 
+
diff --git a/LayoutTests/pointerevents/ios/pointer-events-prevent-default.html b/LayoutTests/pointerevents/ios/pointer-events-prevent-default.html
new file mode 100644 (file)
index 0000000..ef671b5
--- /dev/null
@@ -0,0 +1,30 @@
+<!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((target, test) => {
+    target.style.overflowY = "scroll";
+    target.style.webkitOverflowScrolling = "touch";
+    target.appendChild(document.createElement("div")).setAttribute("style", "width: 100%; height: 200%");
+
+    const initialScrollTop = target.scrollTop;
+    target.addEventListener("pointerdown", event => event.preventDefault());
+    ui.swipe({ x: 100, y: 100 }, { x: 100, y: 50 }).then(() => {
+        assert_equals(target.scrollTop, initialScrollTop);
+        test.done();
+    });
+}, "Scrolling of content can be disabled by calling preventDefault() on a pointer event.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/utils.js b/LayoutTests/pointerevents/utils.js
new file mode 100644 (file)
index 0000000..bbf36ce
--- /dev/null
@@ -0,0 +1,117 @@
+
+function target_test(...args)
+{
+    if (args.length !== 2 && args.length !== 3) {
+        console.error(`target_test expected 2 or 3 arguments but got ${args.length}.`);
+        return;
+    }
+
+    const impliedOptions = args.length == 2;
+
+    let options = impliedOptions ? { } : args[0];
+    let continutation = args[impliedOptions ? 0 : 1];
+    let description = args[impliedOptions ? 1 : 2];
+
+    options.x = options.x || 0;
+    options.y = options.y || 0;
+    options.width = options.width || "100%";
+    options.height = options.height || "100%";
+
+    async_test(test => {
+        const target = document.body.appendChild(document.createElement("div"));
+        target.setAttribute("style", `
+            position: absolute;
+            left: ${options.x};
+            top: ${options.y};
+            width: ${options.width};
+            height: ${options.height};
+        `);
+        test.add_cleanup(() => target.remove());
+
+        continutation(target, test);
+    }, description);
+}
+
+const ui = new (class UIController {
+
+    beginTouches(options)
+    {
+        return this._run(`uiController.touchDownAtPoint(${options.x}, ${options.y}, ${options.numberOfTouches || 1})`);
+    }
+
+    swipe(from, to)
+    {
+        const durationInSeconds = 0.5;
+        return this._run(`uiController.dragFromPointToPoint(${from.x}, ${from.y}, ${to.x}, ${to.y}, ${durationInSeconds})`);
+    }
+
+    pinchOut(options)
+    {
+        options.x = options.x || 0;
+        options.y = options.y || 0;
+
+        const startEvent = {
+            inputType : "hand",
+            timeOffset : 0,
+            touches : [
+                { inputType : "finger",
+                  phase : "moved",
+                  id : 1,
+                  x : options.x,
+                  y : options.y,
+                  pressure : 0
+                },
+                { inputType : "finger",
+                  phase : "moved",
+                  id : 2,
+                  x : (options.x + options.width) / options.scale,
+                  y : (options.y + options.height) / options.scale,
+                  pressure : 0
+                }
+            ]
+        };
+
+        const endEvent = {
+            inputType : "hand",
+            timeOffset : 0.5,
+            touches : [
+                { inputType : "finger",
+                  phase : "moved",
+                  id : 1,
+                  x : options.x,
+                  y : options.y,
+                  pressure : 0
+                },
+                { inputType : "finger",
+                  phase : "moved",
+                  id : 2,
+                  x : options.x + options.width,
+                  y : options.y + options.height,
+                  pressure : 0
+                }
+            ]
+        };
+
+        return this._runEvents({
+            interpolate : "linear",
+            timestep: 0.1,
+            coordinateSpace : "content",
+            startEvent: startEvent,
+            endEvent: endEvent
+        });
+    }
+
+    _runEvents(events)
+    {
+        return this._run(`uiController.sendEventStream('${JSON.stringify({ events: [events] })}')`);
+    }
+
+    _run(command)
+    {
+        return new Promise(resolve => testRunner.runUIScript(`(function() {
+            (function() { ${command} })();
+            uiController.uiScriptComplete();
+        })();`, resolve));
+    }
+
+})();
index 6378519..030cd7a 100644 (file)
@@ -1,3 +1,21 @@
+2018-11-16  Antoine Quint  <graouts@apple.com>
+
+        PointerEvents should not require touch event listeners to be registered
+        https://bugs.webkit.org/show_bug.cgi?id=191333
+        <rdar://problem/45857523>
+
+        Reviewed by Dean Jackson.
+
+        Tests: pointerevents/ios/pointer-events-dispatch-on-touch.html
+               pointerevents/ios/pointer-events-prevent-default.html
+
+        * dom/EventNames.h:
+        (WebCore::EventNames::isTouchEventType const):
+        (WebCore::EventNames::touchAndPointerEventNames const):
+        (WebCore::EventNames::touchEventNames const): Deleted.
+        * dom/Node.cpp:
+        (WebCore::Node::moveNodeToNewDocument):
+
 2018-11-15  Zalan Bujtas  <zalan@apple.com>
 
         [iOS] Do not get stuck in indeterminate content observation state.
index 526e1d7..3f88b75 100644 (file)
@@ -359,7 +359,7 @@ public:
     bool isGamepadEventType(const AtomicString& eventType) const;
 #endif
 
-    std::array<std::reference_wrapper<const AtomicString>, 5> touchEventNames() const;
+    std::array<std::reference_wrapper<const AtomicString>, 9> touchAndPointerEventNames() const;
     std::array<std::reference_wrapper<const AtomicString>, 3> gestureEventNames() const;
 
 private:
@@ -393,7 +393,11 @@ inline bool EventNames::isTouchEventType(const AtomicString& eventType) const
         || eventType == touchmoveEvent
         || eventType == touchendEvent
         || eventType == touchcancelEvent
-        || eventType == touchforcechangeEvent;
+        || eventType == touchforcechangeEvent
+        || eventType == pointerdownEvent
+        || eventType == pointermoveEvent
+        || eventType == pointerupEvent
+        || eventType == pointercancelEvent;
 }
 
 inline bool EventNames::isWheelEventType(const AtomicString& eventType) const
@@ -402,9 +406,9 @@ inline bool EventNames::isWheelEventType(const AtomicString& eventType) const
         || eventType == mousewheelEvent;
 }
 
-inline std::array<std::reference_wrapper<const AtomicString>, 5> EventNames::touchEventNames() const
+inline std::array<std::reference_wrapper<const AtomicString>, 9> EventNames::touchAndPointerEventNames() const
 {
-    return { { touchstartEvent, touchmoveEvent, touchendEvent, touchcancelEvent, touchforcechangeEvent } };
+    return { { touchstartEvent, touchmoveEvent, touchendEvent, touchcancelEvent, touchforcechangeEvent, pointerdownEvent, pointermoveEvent, pointerupEvent, pointercancelEvent } };
 }
 
 inline std::array<std::reference_wrapper<const AtomicString>, 3> EventNames::gestureEventNames() const
index c06c126..de0569d 100644 (file)
@@ -2079,7 +2079,7 @@ void Node::moveNodeToNewDocument(Document& oldDocument, Document& newDocument)
         }
 
         unsigned numTouchEventListeners = 0;
-        for (auto& name : eventNames().touchEventNames())
+        for (auto& name : eventNames().touchAndPointerEventNames())
             numTouchEventListeners += eventListeners(name).size();
 
         for (unsigned i = 0; i < numTouchEventListeners; ++i) {
index e70e869..2cb48f3 100644 (file)
@@ -1,3 +1,20 @@
+2018-11-16  Antoine Quint  <graouts@apple.com>
+
+        PointerEvents should not require touch event listeners to be registered
+        https://bugs.webkit.org/show_bug.cgi?id=191333
+        <rdar://problem/45857523>
+
+        Reviewed by Dean Jackson.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::updateTouchEventTracking):
+        (WebKit::WebPageProxy::touchEventTrackingType const):
+        (WebKit::WebPageProxy::handleTouchEventSynchronously):
+        (WebKit::WebPageProxy::handleTouchEventAsynchronously):
+        (WebKit::WebPageProxy::handleTouchEvent):
+        (WebKit::WebPageProxy::resetState):
+        * UIProcess/WebPageProxy.h:
+
 2018-11-15  Myles C. Maxfield  <mmaxfield@apple.com>
 
         WKPreferencesSetFontSmoothingLevel doesn't actually do anything
index 053dd11..d1d1432 100644 (file)
@@ -2380,17 +2380,20 @@ void WebPageProxy::updateTouchEventTracking(const WebTouchEvent& touchStartEvent
 
             trackingType = mergeTrackingTypes(trackingType, trackingTypeForLocation);
         };
-        updateTrackingType(m_touchEventTracking.touchForceChangedTracking, names.touchforcechangeEvent);
-        updateTrackingType(m_touchEventTracking.touchStartTracking, names.touchstartEvent);
-        updateTrackingType(m_touchEventTracking.touchMoveTracking, names.touchmoveEvent);
-        updateTrackingType(m_touchEventTracking.touchEndTracking, names.touchendEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchForceChangedTracking, names.touchforcechangeEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, names.touchstartEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchMoveTracking, names.touchmoveEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, names.touchendEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchStartTracking, names.pointerdownEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchMoveTracking, names.pointermoveEvent);
+        updateTrackingType(m_touchAndPointerEventTracking.touchEndTracking, names.pointerupEvent);
     }
 #else
     UNUSED_PARAM(touchStartEvent);
-    m_touchEventTracking.touchForceChangedTracking = TrackingType::Synchronous;
-    m_touchEventTracking.touchStartTracking = TrackingType::Synchronous;
-    m_touchEventTracking.touchMoveTracking = TrackingType::Synchronous;
-    m_touchEventTracking.touchEndTracking = TrackingType::Synchronous;
+    m_touchAndPointerEventTracking.touchForceChangedTracking = TrackingType::Synchronous;
+    m_touchAndPointerEventTracking.touchStartTracking = TrackingType::Synchronous;
+    m_touchAndPointerEventTracking.touchMoveTracking = TrackingType::Synchronous;
+    m_touchAndPointerEventTracking.touchEndTracking = TrackingType::Synchronous;
 #endif // ENABLE(ASYNC_SCROLLING)
 }
 
@@ -2405,20 +2408,20 @@ TrackingType WebPageProxy::touchEventTrackingType(const WebTouchEvent& touchStar
     // WebCore should not have to set up its state correctly after some events were dismissed.
     // For example, we don't want to send a TouchMoved without a TouchPressed.
     // We send everything, WebCore updates its internal state and dispatch what is needed to the page.
-    TrackingType globalTrackingType = m_touchEventTracking.isTrackingAnything() ? TrackingType::Asynchronous : TrackingType::NotTracking;
+    TrackingType globalTrackingType = m_touchAndPointerEventTracking.isTrackingAnything() ? TrackingType::Asynchronous : TrackingType::NotTracking;
 
-    globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchForceChangedTracking);
+    globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchForceChangedTracking);
     for (auto& touchPoint : touchStartEvent.touchPoints()) {
         switch (touchPoint.state()) {
         case WebPlatformTouchPoint::TouchReleased:
-            globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchEndTracking);
+            globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchEndTracking);
             break;
         case WebPlatformTouchPoint::TouchPressed:
-            globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchStartTracking);
+            globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchStartTracking);
             break;
         case WebPlatformTouchPoint::TouchMoved:
         case WebPlatformTouchPoint::TouchStationary:
-            globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchEventTracking.touchMoveTracking);
+            globalTrackingType = mergeTrackingTypes(globalTrackingType, m_touchAndPointerEventTracking.touchMoveTracking);
             break;
         case WebPlatformTouchPoint::TouchCancelled:
             globalTrackingType = mergeTrackingTypes(globalTrackingType, TrackingType::Asynchronous);
@@ -2483,7 +2486,7 @@ void WebPageProxy::handleTouchEventSynchronously(NativeWebTouchEvent& event)
     m_process->responsivenessTimer().stop();
 
     if (event.allTouchPointsAreReleased())
-        m_touchEventTracking.reset();
+        m_touchAndPointerEventTracking.reset();
 }
 
 void WebPageProxy::handleTouchEventAsynchronously(const NativeWebTouchEvent& event)
@@ -2498,7 +2501,7 @@ void WebPageProxy::handleTouchEventAsynchronously(const NativeWebTouchEvent& eve
     m_process->send(Messages::EventDispatcher::TouchEvent(m_pageID, event), 0);
 
     if (event.allTouchPointsAreReleased())
-        m_touchEventTracking.reset();
+        m_touchAndPointerEventTracking.reset();
 }
 
 #elif ENABLE(TOUCH_EVENTS)
@@ -2532,7 +2535,7 @@ void WebPageProxy::handleTouchEvent(const NativeWebTouchEvent& event)
     }
 
     if (event.allTouchPointsAreReleased())
-        m_touchEventTracking.reset();
+        m_touchAndPointerEventTracking.reset();
 }
 #endif // ENABLE(TOUCH_EVENTS)
 
@@ -6265,7 +6268,7 @@ void WebPageProxy::resetState(ResetStateReason resetStateReason)
     }
 
 #if ENABLE(TOUCH_EVENTS)
-    m_touchEventTracking.reset();
+    m_touchAndPointerEventTracking.reset();
 #endif
 
 #if ENABLE(GEOLOCATION)
index 8ef7e7b..906cf12 100644 (file)
@@ -2093,7 +2093,7 @@ private:
             touchEndTracking = WebCore::TrackingType::NotTracking;
         }
     };
-    TouchEventTracking m_touchEventTracking;
+    TouchEventTracking m_touchAndPointerEventTracking;
 #endif
 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
     Deque<QueuedTouchEvents> m_touchEventQueue;