Title: [238344] trunk
Revision
238344
Author
grao...@webkit.org
Date
2018-11-17 01:29:52 -0800 (Sat, 17 Nov 2018)

Log Message

[Pointer Events] event.isPrimary doesn't always represent the oldest active touch
https://bugs.webkit.org/show_bug.cgi?id=191752
<rdar://problem/46129270>

Reviewed by Dean Jackson.

Source/WebCore:

Provide isPrimary to the constructor so its value can be determined at the call site.

Test: pointerevents/ios/pointer-events-is-primary.html

* dom/PointerEvent.h:
* dom/ios/PointerEventIOS.cpp:
(WebCore::PointerEvent::create):
(WebCore::PointerEvent::PointerEvent):
(WebCore::m_isPrimary):

LayoutTests:

Add a new test that checks that adding a touch after another existing touch does not make it be
the primary touch, but that removing the first touch makes the second touch become the primary touch.

To do this we add a new ui.sequence() method that allows a series of touch actions to be performed
in a linear sequence. The test author can create a finger and call various actions on it, currently
begin(), move() and end().

When these actions are processed, we compute all "stationary" actions for each part of the sequence
so that we can provide this to the uiController.sendEventStream() function.

Finally, we add a way to track events received by the target and assert that the events that were
received match those that were expected.

* pointerevents/ios/pointer-events-is-primary-expected.txt: Added.
* pointerevents/ios/pointer-events-is-primary.html: Added.
* pointerevents/utils.js:
(prototype.handleEvent):
(prototype.assertMatchesEvents):
(const.ui.new.UIController):
(const.ui.new.UIController.prototype.finger):
(const.ui.new.UIController.prototype.pinchOut):
(const.ui.new.UIController.prototype.sequence):
(const.ui.new.UIController.prototype._runEvents):
(prototype.begin):
(prototype.move):
(prototype.end):
(prototype.stationary):
(prototype._action):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (238343 => 238344)


--- trunk/LayoutTests/ChangeLog	2018-11-17 06:46:55 UTC (rev 238343)
+++ trunk/LayoutTests/ChangeLog	2018-11-17 09:29:52 UTC (rev 238344)
@@ -1,3 +1,40 @@
+2018-11-16  Antoine Quint  <grao...@apple.com>
+
+        [Pointer Events] event.isPrimary doesn't always represent the oldest active touch
+        https://bugs.webkit.org/show_bug.cgi?id=191752
+        <rdar://problem/46129270>
+
+        Reviewed by Dean Jackson.
+
+        Add a new test that checks that adding a touch after another existing touch does not make it be
+        the primary touch, but that removing the first touch makes the second touch become the primary touch.
+
+        To do this we add a new ui.sequence() method that allows a series of touch actions to be performed
+        in a linear sequence. The test author can create a finger and call various actions on it, currently
+        begin(), move() and end().
+
+        When these actions are processed, we compute all "stationary" actions for each part of the sequence
+        so that we can provide this to the uiController.sendEventStream() function.
+
+        Finally, we add a way to track events received by the target and assert that the events that were
+        received match those that were expected.
+
+        * pointerevents/ios/pointer-events-is-primary-expected.txt: Added.
+        * pointerevents/ios/pointer-events-is-primary.html: Added.
+        * pointerevents/utils.js:
+        (prototype.handleEvent):
+        (prototype.assertMatchesEvents):
+        (const.ui.new.UIController):
+        (const.ui.new.UIController.prototype.finger):
+        (const.ui.new.UIController.prototype.pinchOut):
+        (const.ui.new.UIController.prototype.sequence):
+        (const.ui.new.UIController.prototype._runEvents):
+        (prototype.begin):
+        (prototype.move):
+        (prototype.end):
+        (prototype.stationary):
+        (prototype._action):
+
 2018-11-16  Devin Rousso  <drou...@apple.com>
 
         Web Inspector: Audit: minor style improvements

Added: trunk/LayoutTests/pointerevents/ios/pointer-events-is-primary-expected.txt (0 => 238344)


--- trunk/LayoutTests/pointerevents/ios/pointer-events-is-primary-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/pointerevents/ios/pointer-events-is-primary-expected.txt	2018-11-17 09:29:52 UTC (rev 238344)
@@ -0,0 +1,3 @@
+
+PASS Oldest active touch has isPrimary = true. 
+

Added: trunk/LayoutTests/pointerevents/ios/pointer-events-is-primary.html (0 => 238344)


--- trunk/LayoutTests/pointerevents/ios/pointer-events-is-primary.html	                        (rev 0)
+++ trunk/LayoutTests/pointerevents/ios/pointer-events-is-primary.html	2018-11-17 09:29:52 UTC (rev 238344)
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script>
+
+'use strict';
+
+target_test((target, test) => {
+    const eventTracker = new EventTracker(target, ["pointerdown", "pointermove"]);
+
+    const _one_ = ui.finger();
+    const two = ui.finger();
+    ui.sequence([
+        one.begin({ x: 10, y: 10 }),
+        two.begin({ x: 50, y: 50 }),
+        two.move({ x: 70, y: 70 }),
+        one.move({ x: 30, y: 30 }),
+        one.end(),
+        two.move({ x: 50, y: 50 })
+    ]).then(() => {
+        eventTracker.assertMatchesEvents([
+            { id: 1, type: "pointerdown", x: 10, y: 10, isPrimary: true },
+            { id: 2, type: "pointerdown", x: 50, y: 50, isPrimary: false },
+            { id: 2, type: "pointermove", x: 70, y: 70, isPrimary: false },
+            { id: 1, type: "pointermove", x: 30, y: 30, isPrimary: true },
+            { id: 2, type: "pointermove", x: 50, y: 50, isPrimary: true }
+        ]);
+        test.done();
+    });
+}, "Oldest active touch has isPrimary = true.");
+
+</script>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/LayoutTests/pointerevents/utils.js (238343 => 238344)


--- trunk/LayoutTests/pointerevents/utils.js	2018-11-17 06:46:55 UTC (rev 238343)
+++ trunk/LayoutTests/pointerevents/utils.js	2018-11-17 09:29:52 UTC (rev 238344)
@@ -32,8 +32,61 @@
     }, description);
 }
 
+class EventTracker
+{
+
+    constructor(target, eventNames)
+    {
+        this.target = target;
+        this.events = [];
+        this.pointerIdToTouchIdMap = {};
+
+        for (let eventName of eventNames)
+            target.addEventListener(eventName, this);
+    }
+
+    handleEvent(event)
+    {
+        if (!this.pointerIdToTouchIdMap[event.pointerId])
+            this.pointerIdToTouchIdMap[event.pointerId] = Object.keys(this.pointerIdToTouchIdMap).length + 1;
+
+        const id = this.pointerIdToTouchIdMap[event.pointerId];
+        this.events.push({
+            id,
+            type: event.type,
+            x: event.clientX,
+            y: event.clientY,
+            isPrimary: event.isPrimary
+        });
+    }
+
+    assertMatchesEvents(expectedEvents)
+    {
+        assert_true(!!this.events.length, "Event tracker saw some events.");
+        assert_equals(expectedEvents.length, this.events.length, "Expected events and actual events have the same length.");
+        for (let i = 0; i < expectedEvents.length; ++i) {
+            const expectedEvent = expectedEvents[i];
+            const actualEvent = this.events[i];
+            for (let property of Object.getOwnPropertyNames(expectedEvent))
+                assert_equals(expectedEvent[property], actualEvent[property], `Property ${property} matches for event at index ${i}.`);
+        }
+    }
+
+}
+
 const ui = new (class UIController {
 
+    constructor()
+    {
+        this.fingers = {};
+    }
+
+    finger()
+    {
+        const id = Object.keys(this.fingers).length + 1;
+        return this.fingers[id] = new Finger(id);
+    }
+
     beginTouches(options)
     {
         return this._run(`uiController.touchDownAtPoint(${options.x}, ${options.y}, ${options.numberOfTouches || 1})`);
@@ -92,18 +145,52 @@
             ]
         };
 
-        return this._runEvents({
+        return this._runEvents([{
             interpolate : "linear",
             timestep: 0.1,
             coordinateSpace : "content",
             startEvent: startEvent,
             endEvent: endEvent
-        });
+        }]);
     }
 
+    sequence(touches)
+    {
+        const activeFingers = {};
+
+        return this._runEvents(touches.map((touches, index) => {
+            if (!Array.isArray(touches))
+                touches = [touches];
+
+            const processedIDs = {};
+
+            // Update the list of active touches.
+            touches.forEach(touch => {
+                processedIDs[touch.id] = true;
+                if (touch.phase === "ended")
+                    delete activeFingers[touch.id];
+                else
+                    activeFingers[touch.id] = { x: touch.x, y: touch.y };
+            });
+
+            // Now go through the active touches and check that they're all listed in the new touches.
+            for (let id in activeFingers) {
+                if (!processedIDs[id])
+                    touches.push(this.fingers[id].stationary(activeFingers[id]));
+            }
+
+            return {
+                inputType : "hand",
+                timeOffset : index * 0.05,
+                coordinateSpace : "content",
+                touches : touches
+            }
+        }));
+    }
+
     _runEvents(events)
     {
-        return this._run(`uiController.sendEventStream('${JSON.stringify({ events: [events] })}')`);
+        return this._run(`uiController.sendEventStream('${JSON.stringify({ events })}')`);
     }
 
     _run(command)
@@ -115,3 +202,40 @@
     }
 
 })();
+
+class Finger
+{
+
+    constructor(id)
+    {
+        this.id = id;
+    }
+
+    begin(options)
+    {
+        return this._action("began", options.x || 0, options.y || 0);
+    }
+
+    move(options)
+    {
+        return this._action("moved", options.x || 0, options.y || 0);
+    }
+
+    end(options)
+    {
+        return this._action("ended", this._lastX, this._lastY);
+    }
+
+    stationary(options)
+    {
+        return this._action("stationary", options.x || 0, options.y || 0);
+    }
+
+    _action(phase, x, y)
+    {
+        this._lastX = x;
+        this._lastY = y;
+        return { inputType: "finger", id: this.id, phase, x, y };
+    }
+
+}

Modified: trunk/Source/WebCore/ChangeLog (238343 => 238344)


--- trunk/Source/WebCore/ChangeLog	2018-11-17 06:46:55 UTC (rev 238343)
+++ trunk/Source/WebCore/ChangeLog	2018-11-17 09:29:52 UTC (rev 238344)
@@ -1,3 +1,21 @@
+2018-11-16  Antoine Quint  <grao...@apple.com>
+
+        [Pointer Events] event.isPrimary doesn't always represent the oldest active touch
+        https://bugs.webkit.org/show_bug.cgi?id=191752
+        <rdar://problem/46129270>
+
+        Reviewed by Dean Jackson.
+
+        Provide isPrimary to the constructor so its value can be determined at the call site.
+
+        Test: pointerevents/ios/pointer-events-is-primary.html
+
+        * dom/PointerEvent.h:
+        * dom/ios/PointerEventIOS.cpp:
+        (WebCore::PointerEvent::create):
+        (WebCore::PointerEvent::PointerEvent):
+        (WebCore::m_isPrimary):
+
 2018-11-16  Alex Christensen  <achristen...@webkit.org>
 
         Tweak _showSafeBrowsingWarningWithTitle SPI

Modified: trunk/Source/WebCore/dom/PointerEvent.h (238343 => 238344)


--- trunk/Source/WebCore/dom/PointerEvent.h	2018-11-17 06:46:55 UTC (rev 238343)
+++ trunk/Source/WebCore/dom/PointerEvent.h	2018-11-17 09:29:52 UTC (rev 238344)
@@ -60,7 +60,7 @@
     }
 
 #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY)
-    static Ref<PointerEvent> create(const PlatformTouchEvent&, unsigned touchIndex, Ref<WindowProxy>&&);
+    static Ref<PointerEvent> create(const PlatformTouchEvent&, unsigned touchIndex, bool isPrimary, Ref<WindowProxy>&&);
 #endif
 
     virtual ~PointerEvent();
@@ -84,7 +84,7 @@
     PointerEvent();
     PointerEvent(const AtomicString&, Init&&);
 #if ENABLE(TOUCH_EVENTS) && PLATFORM(IOS_FAMILY)
-    PointerEvent(const AtomicString& type, const PlatformTouchEvent&, IsCancelable isCancelable, unsigned touchIndex, Ref<WindowProxy>&&);
+    PointerEvent(const AtomicString& type, const PlatformTouchEvent&, IsCancelable isCancelable, unsigned touchIndex, bool isPrimary, Ref<WindowProxy>&&);
 #endif
 
     long m_pointerId { 0 };

Modified: trunk/Source/WebCore/dom/ios/PointerEventIOS.cpp (238343 => 238344)


--- trunk/Source/WebCore/dom/ios/PointerEventIOS.cpp	2018-11-17 06:46:55 UTC (rev 238343)
+++ trunk/Source/WebCore/dom/ios/PointerEventIOS.cpp	2018-11-17 09:29:52 UTC (rev 238344)
@@ -57,19 +57,19 @@
     return PointerEvent::IsCancelable::Yes;
 }
 
-Ref<PointerEvent> PointerEvent::create(const PlatformTouchEvent& event, unsigned index, Ref<WindowProxy>&& view)
+Ref<PointerEvent> PointerEvent::create(const PlatformTouchEvent& event, unsigned index, bool isPrimary, Ref<WindowProxy>&& view)
 {
     auto phase = event.touchPhaseAtIndex(index);
-    return adoptRef(*new PointerEvent(eventType(phase), event, phaseIsCancelable(phase), index, WTFMove(view)));
+    return adoptRef(*new PointerEvent(eventType(phase), event, phaseIsCancelable(phase), index, isPrimary, WTFMove(view)));
 }
 
-PointerEvent::PointerEvent(const AtomicString& type, const PlatformTouchEvent& event, IsCancelable isCancelable, unsigned index, Ref<WindowProxy>&& 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))
     , m_width(2 * event.radiusXAtIndex(index))
     , m_height(2 * event.radiusYAtIndex(index))
     , m_pointerType(event.touchTypeAtIndex(index) == PlatformTouchPoint::TouchType::Stylus ? "pen"_s : "touch"_s)
-    , m_isPrimary(!index)
+    , m_isPrimary(isPrimary)
 {
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to