[iOS] Correctly handle overlapping regions for elements with a touch-action property
https://bugs.webkit.org/show_bug.cgi?id=194813
<rdar://problem/48194708>

Reviewed by Antti Koivisto.

Source/WebKit:

We now use WebKit::touchActionsForPoint() to determine the touch actions for a given touch using its location in -[WKContentViewInteraction
_handleTouchActionsForTouchEvent:]. We then record these touch actions for the touch's identifier on the RemoteScrollingCoordinatorProxy.

Then, as we interact with a UIScrollView, we get its gesture recognizer and get its active touch identifier through the new
-[WKContentViewInteraction activeTouchIdentifierForGestureRecognizer:] method, and query the RemoteScrollingCoordinatorProxy for the touch
actions matching that touch identifier.

Tests: pointerevents/ios/touch-action-none-overlap.html
       pointerevents/ios/touch-action-pan-x-overlap.html
       pointerevents/ios/touch-action-pan-y-overlap.html
       pointerevents/ios/touch-action-pinch-zoom-overlap.html

* UIProcess/PageClient.h:
(WebKit::PageClient::activeTouchIdentifierForGestureRecognizer):
* UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp: Maintain a touch identifier to touch actions map.
(WebKit::RemoteScrollingCoordinatorProxy::activeTouchActionsForTouchIdentifier const):
(WebKit::RemoteScrollingCoordinatorProxy::setTouchActionsForTouchIdentifier):
(WebKit::RemoteScrollingCoordinatorProxy::clearTouchActionsForTouchIdentifier):
(WebKit::RemoteScrollingCoordinatorProxy::touchActionDataAtPoint const): Deleted.
(WebKit::RemoteScrollingCoordinatorProxy::touchActionDataForScrollNodeID const): Deleted.
(WebKit::RemoteScrollingCoordinatorProxy::setTouchDataForTouchIdentifier): Deleted.
(WebKit::RemoteScrollingCoordinatorProxy::clearTouchDataForTouchIdentifier): Deleted.
* UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h:
* UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
* UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
(-[WKScrollingNodeScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]):
(-[WKScrollingNodeScrollViewDelegate _scrollView:adjustedOffsetForOffset:translation:startPoint:locationInView:horizontalVelocity:verticalVelocity:]):
(WebKit::ScrollingTreeScrollingNodeDelegateIOS::activeTouchActionsForGestureRecognizer const):
(WebKit::ScrollingTreeScrollingNodeDelegateIOS::touchActionData const): Deleted.
* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::activeTouchIdentifierForGestureRecognizer):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView activeTouchIdentifierForGestureRecognizer:]):
(-[WKContentView _handleTouchActionsForTouchEvent:]):

LayoutTests:

Add new tests that check that an element overlapping another element with a "touch-action" property is not affected
by the property set on that underlying element.

* pointerevents/ios/touch-action-none-overlap-expected.txt: Added.
* pointerevents/ios/touch-action-none-overlap.html: Added.
* pointerevents/ios/touch-action-pan-x-overlap-expected.txt: Added.
* pointerevents/ios/touch-action-pan-x-overlap.html: Added.
* pointerevents/ios/touch-action-pan-y-overlap-expected.txt: Added.
* pointerevents/ios/touch-action-pan-y-overlap.html: Added.
* pointerevents/ios/touch-action-pinch-zoom-overlap-expected.txt: Added.
* pointerevents/ios/touch-action-pinch-zoom-overlap.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@245112 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index e875ba8..98e9a49 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,23 @@
+2019-05-08  Antoine Quint  <graouts@apple.com>
+
+        [iOS] Correctly handle overlapping regions for elements with a touch-action property
+        https://bugs.webkit.org/show_bug.cgi?id=194813
+        <rdar://problem/48194708>
+
+        Reviewed by Antti Koivisto.
+
+        Add new tests that check that an element overlapping another element with a "touch-action" property is not affected
+        by the property set on that underlying element.
+
+        * pointerevents/ios/touch-action-none-overlap-expected.txt: Added.
+        * pointerevents/ios/touch-action-none-overlap.html: Added.
+        * pointerevents/ios/touch-action-pan-x-overlap-expected.txt: Added.
+        * pointerevents/ios/touch-action-pan-x-overlap.html: Added.
+        * pointerevents/ios/touch-action-pan-y-overlap-expected.txt: Added.
+        * pointerevents/ios/touch-action-pan-y-overlap.html: Added.
+        * pointerevents/ios/touch-action-pinch-zoom-overlap-expected.txt: Added.
+        * pointerevents/ios/touch-action-pinch-zoom-overlap.html: Added.
+
 2019-05-08  Chris Dumez  <cdumez@apple.com>
 
         [iOS Debug] ASSERTION FAILED: !m_originalNode in WebCore::JSLazyEventListener::checkValidityForEventTarget(WebCore::EventTarget &)
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-overlap-expected.txt b/LayoutTests/pointerevents/ios/touch-action-none-overlap-expected.txt
new file mode 100644
index 0000000..dc42c15
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-none-overlap-expected.txt
@@ -0,0 +1,3 @@
+
+PASS Testing that an element overlapping an element with touch-action: none allows for scrolling while the touch-action: none element correctly prevents scrolling. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-overlap.html b/LayoutTests/pointerevents/ios/touch-action-none-overlap.html
new file mode 100644
index 0000000..f3b43f0
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-none-overlap.html
@@ -0,0 +1,45 @@
+<!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';
+
+function assertNoScroll()
+{
+    assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
+    assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
+}
+
+target_test({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "none";
+
+    const overlap = document.body.insertBefore(document.createElement("div"), target.nextElementSibling);
+    overlap.setAttribute("style", "position: absolute; left: 50px; top: 50px; width: 100px; height: 100px;");
+
+    // Swipe over the "touch-action: none" element and around the overlapping element.
+    ui.swipe({ x: 25, y: 175 }, { x: 25, y: 25 }).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 175 }, { x: 175, y: 25 })).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 25 }, { x: 25, y: 25 })).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 175 }, { x: 25, y: 175 })).then(assertNoScroll)
+    // Now swipe over the overlapping element, this should scroll.
+    .then(() => ui.swipe({ x: 125, y: 125 }, { x: 75, y: 75 })).then(() => {
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that an element overlapping an element with touch-action: none allows for scrolling while the touch-action: none element correctly prevents scrolling.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-x-overlap-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pan-x-overlap-expected.txt
new file mode 100644
index 0000000..3c0de1d
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-pan-x-overlap-expected.txt
@@ -0,0 +1,3 @@
+
+PASS Testing that an element overlapping an element with touch-action: pan-x allows for scrolling while the touch-action: pan-x element correctly prevents scrolling in the y-axis. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-x-overlap.html b/LayoutTests/pointerevents/ios/touch-action-pan-x-overlap.html
new file mode 100644
index 0000000..16d5a35
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-pan-x-overlap.html
@@ -0,0 +1,43 @@
+<!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';
+
+function assertNoScroll()
+{
+    assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
+    assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
+}
+
+target_test({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pan-x";
+
+    const overlap = document.body.insertBefore(document.createElement("div"), target.nextElementSibling);
+    overlap.setAttribute("style", "position: absolute; left: 50px; top: 50px; width: 100px; height: 100px;");
+
+    // Swipe in the y-axis over the "touch-action: pan-x" element and around the overlapping element.
+    ui.swipe({ x: 25, y: 175 }, { x: 25, y: 25 }).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 175 }, { x: 175, y: 25 })).then(assertNoScroll)
+    // Now swipe over the overlapping element, this should scroll in both directions.
+    .then(() => ui.swipe({ x: 125, y: 125 }, { x: 75, y: 75 })).then(() => {
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that an element overlapping an element with touch-action: pan-x allows for scrolling while the touch-action: pan-x element correctly prevents scrolling in the y-axis.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-y-overlap-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pan-y-overlap-expected.txt
new file mode 100644
index 0000000..924183b
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-pan-y-overlap-expected.txt
@@ -0,0 +1,3 @@
+
+PASS Testing that an element overlapping an element with touch-action: pan-y allows for scrolling while the touch-action: pan-y element correctly prevents scrolling in the x-axis. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-y-overlap.html b/LayoutTests/pointerevents/ios/touch-action-pan-y-overlap.html
new file mode 100644
index 0000000..e86f2d7
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-pan-y-overlap.html
@@ -0,0 +1,43 @@
+<!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';
+
+function assertNoScroll()
+{
+    assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
+    assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
+}
+
+target_test({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pan-y";
+
+    const overlap = document.body.insertBefore(document.createElement("div"), target.nextElementSibling);
+    overlap.setAttribute("style", "position: absolute; left: 50px; top: 50px; width: 100px; height: 100px;");
+
+    // Swipe in the x-axis over the "touch-action: pan-y" element and around the overlapping element.
+    ui.swipe({ x: 175, y: 25 }, { x: 25, y: 25 }).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 175 }, { x: 25, y: 175 })).then(assertNoScroll)
+    // Now swipe over the overlapping element, this should scroll in both directions.
+    .then(() => ui.swipe({ x: 125, y: 125 }, { x: 75, y: 75 })).then(() => {
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that an element overlapping an element with touch-action: pan-y allows for scrolling while the touch-action: pan-y element correctly prevents scrolling in the x-axis.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-overlap-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-overlap-expected.txt
new file mode 100644
index 0000000..36ff36c
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-overlap-expected.txt
@@ -0,0 +1,3 @@
+
+PASS Testing that an element overlapping an element with touch-action: pinch-zoom allows for scrolling while the touch-action: pinch-zoom element correctly prevents scrolling. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-overlap.html b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-overlap.html
new file mode 100644
index 0000000..75427e4
--- /dev/null
+++ b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-overlap.html
@@ -0,0 +1,45 @@
+<!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';
+
+function assertNoScroll()
+{
+    assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
+    assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
+}
+
+target_test({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pinch-zoom";
+
+    const overlap = document.body.insertBefore(document.createElement("div"), target.nextElementSibling);
+    overlap.setAttribute("style", "position: absolute; left: 50px; top: 50px; width: 100px; height: 100px;");
+
+    // Swipe over the "touch-action: pinch-zoom" element and around the overlapping element.
+    ui.swipe({ x: 25, y: 175 }, { x: 25, y: 25 }).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 175 }, { x: 175, y: 25 })).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 25 }, { x: 25, y: 25 })).then(assertNoScroll)
+    .then(() => ui.swipe({ x: 175, y: 175 }, { x: 25, y: 175 })).then(assertNoScroll)
+    // Now swipe over the overlapping element, this should scroll.
+    .then(() => ui.swipe({ x: 125, y: 125 }, { x: 75, y: 75 })).then(() => {
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that an element overlapping an element with touch-action: pinch-zoom allows for scrolling while the touch-action: pinch-zoom element correctly prevents scrolling.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 5c62974..b808218 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,48 @@
+2019-05-08  Antoine Quint  <graouts@apple.com>
+
+        [iOS] Correctly handle overlapping regions for elements with a touch-action property
+        https://bugs.webkit.org/show_bug.cgi?id=194813
+        <rdar://problem/48194708>
+
+        Reviewed by Antti Koivisto.
+
+        We now use WebKit::touchActionsForPoint() to determine the touch actions for a given touch using its location in -[WKContentViewInteraction
+        _handleTouchActionsForTouchEvent:]. We then record these touch actions for the touch's identifier on the RemoteScrollingCoordinatorProxy.
+
+        Then, as we interact with a UIScrollView, we get its gesture recognizer and get its active touch identifier through the new
+        -[WKContentViewInteraction activeTouchIdentifierForGestureRecognizer:] method, and query the RemoteScrollingCoordinatorProxy for the touch
+        actions matching that touch identifier.
+
+        Tests: pointerevents/ios/touch-action-none-overlap.html
+               pointerevents/ios/touch-action-pan-x-overlap.html
+               pointerevents/ios/touch-action-pan-y-overlap.html
+               pointerevents/ios/touch-action-pinch-zoom-overlap.html
+
+        * UIProcess/PageClient.h:
+        (WebKit::PageClient::activeTouchIdentifierForGestureRecognizer):
+        * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp: Maintain a touch identifier to touch actions map.
+        (WebKit::RemoteScrollingCoordinatorProxy::activeTouchActionsForTouchIdentifier const):
+        (WebKit::RemoteScrollingCoordinatorProxy::setTouchActionsForTouchIdentifier):
+        (WebKit::RemoteScrollingCoordinatorProxy::clearTouchActionsForTouchIdentifier):
+        (WebKit::RemoteScrollingCoordinatorProxy::touchActionDataAtPoint const): Deleted.
+        (WebKit::RemoteScrollingCoordinatorProxy::touchActionDataForScrollNodeID const): Deleted.
+        (WebKit::RemoteScrollingCoordinatorProxy::setTouchDataForTouchIdentifier): Deleted.
+        (WebKit::RemoteScrollingCoordinatorProxy::clearTouchDataForTouchIdentifier): Deleted.
+        * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h:
+        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
+        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
+        (-[WKScrollingNodeScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]):
+        (-[WKScrollingNodeScrollViewDelegate _scrollView:adjustedOffsetForOffset:translation:startPoint:locationInView:horizontalVelocity:verticalVelocity:]):
+        (WebKit::ScrollingTreeScrollingNodeDelegateIOS::activeTouchActionsForGestureRecognizer const):
+        (WebKit::ScrollingTreeScrollingNodeDelegateIOS::touchActionData const): Deleted.
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::activeTouchIdentifierForGestureRecognizer):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView activeTouchIdentifierForGestureRecognizer:]):
+        (-[WKContentView _handleTouchActionsForTouchEvent:]):
+
 2019-05-08  Megan Gardner  <megan_gardner@apple.com>
 
         Add quirks to emulate undo and redo in hidden editable areas on some websites
diff --git a/Source/WebKit/UIProcess/PageClient.h b/Source/WebKit/UIProcess/PageClient.h
index e767f5e..afc94ce 100644
--- a/Source/WebKit/UIProcess/PageClient.h
+++ b/Source/WebKit/UIProcess/PageClient.h
@@ -501,6 +501,7 @@
 
 #if ENABLE(POINTER_EVENTS)
     virtual void cancelPointersForGestureRecognizer(UIGestureRecognizer*) { }
+    virtual WTF::Optional<unsigned> activeTouchIdentifierForGestureRecognizer(UIGestureRecognizer*) { return WTF::nullopt; }
 #endif
 
 #if PLATFORM(WPE)
diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp b/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp
index 774792c..3b838ea 100644
--- a/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp
+++ b/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp
@@ -247,28 +247,22 @@
 }
 
 #if ENABLE(POINTER_EVENTS)
-Optional<TouchActionData> RemoteScrollingCoordinatorProxy::touchActionDataAtPoint(const IntPoint p) const
+OptionSet<TouchAction> RemoteScrollingCoordinatorProxy::activeTouchActionsForTouchIdentifier(unsigned touchIdentifier) const
 {
-    return m_scrollingTree->touchActionDataAtPoint(p);
+    auto iterator = m_touchActionsByTouchIdentifier.find(touchIdentifier);
+    if (iterator == m_touchActionsByTouchIdentifier.end())
+        return { };
+    return iterator->value;
 }
 
-Optional<TouchActionData> RemoteScrollingCoordinatorProxy::touchActionDataForScrollNodeID(ScrollingNodeID scrollingNodeID) const
+void RemoteScrollingCoordinatorProxy::setTouchActionsForTouchIdentifier(OptionSet<TouchAction> touchActions, unsigned touchIdentifier)
 {
-    for (auto& touchActionData : m_touchActionDataByTouchIdentifier.values()) {
-        if (touchActionData.scrollingNodeID == scrollingNodeID)
-            return touchActionData;
-    }
-    return WTF::nullopt;
+    m_touchActionsByTouchIdentifier.set(touchIdentifier, touchActions);
 }
 
-void RemoteScrollingCoordinatorProxy::setTouchDataForTouchIdentifier(TouchActionData touchActionData, unsigned touchIdentifier)
+void RemoteScrollingCoordinatorProxy::clearTouchActionsForTouchIdentifier(unsigned touchIdentifier)
 {
-    m_touchActionDataByTouchIdentifier.set(touchIdentifier, touchActionData);
-}
-
-void RemoteScrollingCoordinatorProxy::clearTouchDataForTouchIdentifier(unsigned touchIdentifier)
-{
-    m_touchActionDataByTouchIdentifier.remove(touchIdentifier);
+    m_touchActionsByTouchIdentifier.remove(touchIdentifier);
 }
 
 #endif
diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h b/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h
index 2cc1024..dc19989 100644
--- a/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h
+++ b/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h
@@ -102,10 +102,9 @@
     String scrollingTreeAsText() const;
 
 #if ENABLE(POINTER_EVENTS)
-    Optional<WebCore::TouchActionData> touchActionDataAtPoint(const WebCore::IntPoint) const;
-    Optional<WebCore::TouchActionData> touchActionDataForScrollNodeID(WebCore::ScrollingNodeID) const;
-    void setTouchDataForTouchIdentifier(WebCore::TouchActionData, unsigned);
-    void clearTouchDataForTouchIdentifier(unsigned);
+    OptionSet<WebCore::TouchAction> activeTouchActionsForTouchIdentifier(unsigned touchIdentifier) const;
+    void setTouchActionsForTouchIdentifier(OptionSet<WebCore::TouchAction>, unsigned);
+    void clearTouchActionsForTouchIdentifier(unsigned);
 #endif
 
 private:
@@ -120,7 +119,7 @@
     WebPageProxy& m_webPageProxy;
     RefPtr<RemoteScrollingTree> m_scrollingTree;
 #if ENABLE(POINTER_EVENTS)
-    HashMap<unsigned, WebCore::TouchActionData> m_touchActionDataByTouchIdentifier;
+    HashMap<unsigned, OptionSet<WebCore::TouchAction>> m_touchActionsByTouchIdentifier;
 #endif
     RequestedScrollInfo* m_requestedScrollInfo;
 #if ENABLE(CSS_SCROLL_SNAP)
diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h b/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h
index f508f16..0656656 100644
--- a/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h
+++ b/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h
@@ -66,7 +66,7 @@
     void repositionScrollingLayers();
 
 #if ENABLE(POINTER_EVENTS)
-    Optional<WebCore::TouchActionData> touchActionData() const;
+    OptionSet<TouchAction> activeTouchActionsForGestureRecognizer(UIGestureRecognizer*) const;
     void cancelPointersForGestureRecognizer(UIGestureRecognizer*);
 #endif
 
diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm b/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm
index 39e475c..ac685f2 100644
--- a/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm
+++ b/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm
@@ -79,19 +79,17 @@
 {
 #if ENABLE(POINTER_EVENTS)
     if (![scrollView isZooming]) {
-        if (auto touchActionData = _scrollingTreeNodeDelegate->touchActionData()) {
-            auto touchActions = touchActionData->touchActions;
-            if (touchActions != WebCore::TouchAction::Auto && touchActions != WebCore::TouchAction::Manipulation) {
-                bool canPanX = true;
-                bool canPanY = true;
-                if (!touchActions.contains(WebCore::TouchAction::PanX)) {
-                    canPanX = false;
-                    targetContentOffset->x = scrollView.contentOffset.x;
-                }
-                if (!touchActions.contains(WebCore::TouchAction::PanY)) {
-                    canPanY = false;
-                    targetContentOffset->y = scrollView.contentOffset.y;
-                }
+        auto touchActions = _scrollingTreeNodeDelegate->activeTouchActionsForGestureRecognizer(scrollView.panGestureRecognizer);
+        if (touchActions && !touchActions.containsAny({ WebCore::TouchAction::Auto, WebCore::TouchAction::Manipulation })) {
+            bool canPanX = true;
+            bool canPanY = true;
+            if (!touchActions.contains(WebCore::TouchAction::PanX)) {
+                canPanX = false;
+                targetContentOffset->x = scrollView.contentOffset.x;
+            }
+            if (!touchActions.contains(WebCore::TouchAction::PanY)) {
+                canPanY = false;
+                targetContentOffset->y = scrollView.contentOffset.y;
             }
         }
     }
@@ -148,14 +146,14 @@
 #if ENABLE(POINTER_EVENTS)
 - (CGPoint)_scrollView:(UIScrollView *)scrollView adjustedOffsetForOffset:(CGPoint)offset translation:(CGPoint)translation startPoint:(CGPoint)start locationInView:(CGPoint)locationInView horizontalVelocity:(inout double *)hv verticalVelocity:(inout double *)vv
 {
-    auto touchActionData = _scrollingTreeNodeDelegate->touchActionData();
-    if (!touchActionData) {
-        [self cancelPointersForGestureRecognizer:scrollView.panGestureRecognizer];
+    auto* panGestureRecognizer = scrollView.panGestureRecognizer;
+    auto touchActions = _scrollingTreeNodeDelegate->activeTouchActionsForGestureRecognizer(panGestureRecognizer);
+    if (!touchActions) {
+        [self cancelPointersForGestureRecognizer:panGestureRecognizer];
         return offset;
     }
 
-    auto touchActions = touchActionData->touchActions;
-    if (touchActions == WebCore::TouchAction::Auto || touchActions == WebCore::TouchAction::Manipulation)
+    if (touchActions.containsAny({ WebCore::TouchAction::Auto, WebCore::TouchAction::Manipulation }))
         return offset;
 
     CGPoint adjustedContentOffset = CGPointMake(offset.x, offset.y);
@@ -338,9 +336,12 @@
 }
 
 #if ENABLE(POINTER_EVENTS)
-Optional<TouchActionData> ScrollingTreeScrollingNodeDelegateIOS::touchActionData() const
+OptionSet<TouchAction> ScrollingTreeScrollingNodeDelegateIOS::activeTouchActionsForGestureRecognizer(UIGestureRecognizer* gestureRecognizer) const
 {
-    return downcast<RemoteScrollingTree>(scrollingTree()).scrollingCoordinatorProxy().touchActionDataForScrollNodeID(scrollingNode().scrollingNodeID());
+    auto& scrollingCoordinatorProxy = downcast<RemoteScrollingTree>(scrollingTree()).scrollingCoordinatorProxy();
+    if (auto touchIdentifier = scrollingCoordinatorProxy.webPageProxy().pageClient().activeTouchIdentifierForGestureRecognizer(gestureRecognizer))
+        return scrollingCoordinatorProxy.activeTouchActionsForTouchIdentifier(*touchIdentifier);
+    return { };
 }
 
 void ScrollingTreeScrollingNodeDelegateIOS::cancelPointersForGestureRecognizer(UIGestureRecognizer* gestureRecognizer)
diff --git a/Source/WebKit/UIProcess/ios/PageClientImplIOS.h b/Source/WebKit/UIProcess/ios/PageClientImplIOS.h
index 3872fbd..876ac91 100644
--- a/Source/WebKit/UIProcess/ios/PageClientImplIOS.h
+++ b/Source/WebKit/UIProcess/ios/PageClientImplIOS.h
@@ -251,6 +251,7 @@
 
 #if ENABLE(POINTER_EVENTS)
     void cancelPointersForGestureRecognizer(UIGestureRecognizer*) override;
+    WTF::Optional<unsigned> activeTouchIdentifierForGestureRecognizer(UIGestureRecognizer*) override;
 #endif
 
     WKContentView *m_contentView;
diff --git a/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm b/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
index 9d27bc4..67fb2e3 100644
--- a/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
+++ b/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
@@ -866,6 +866,11 @@
 {
     [m_contentView cancelPointersForGestureRecognizer:gestureRecognizer];
 }
+
+WTF::Optional<unsigned> PageClientImpl::activeTouchIdentifierForGestureRecognizer(UIGestureRecognizer* gestureRecognizer)
+{
+    return [m_contentView activeTouchIdentifierForGestureRecognizer:gestureRecognizer];
+}
 #endif
 
 void PageClientImpl::handleAutocorrectionContext(const WebAutocorrectionContext& context)
diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
index a223ba11..9736c2b 100644
--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
@@ -409,6 +409,7 @@
 
 #if ENABLE(POINTER_EVENTS)
 - (void)cancelPointersForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
+- (WTF::Optional<unsigned>)activeTouchIdentifierForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
 #endif
 
 #define DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW(_action) \
diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
index d9c674a..953ebf9 100644
--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
@@ -37,6 +37,7 @@
 #import "NativeWebKeyboardEvent.h"
 #import "NativeWebTouchEvent.h"
 #import "RemoteLayerTreeDrawingAreaProxy.h"
+#import "RemoteLayerTreeViews.h"
 #import "SmartMagnificationController.h"
 #import "TextInputSPI.h"
 #import "UIKitSPI.h"
@@ -1257,6 +1258,20 @@
     }
 #endif
 }
+
+- (WTF::Optional<unsigned>)activeTouchIdentifierForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
+{
+#if HAVE(UI_WEB_TOUCH_EVENTS_GESTURE_RECOGNIZER_WITH_ACTIVE_TOUCHES_BY_ID)
+    // FIXME: <rdar://problem/48035706>
+    NSMapTable<NSNumber *, UITouch *> *activeTouches = [_touchEventGestureRecognizer activeTouchesByIdentifier];
+    for (NSNumber *touchIdentifier in activeTouches) {
+        UITouch *touch = [activeTouches objectForKey:touchIdentifier];
+        if ([touch.gestureRecognizers containsObject:gestureRecognizer])
+            return [touchIdentifier unsignedIntValue];
+    }
+#endif
+    return WTF::nullopt;
+}
 #endif
 
 inline static UIKeyModifierFlags gestureRecognizerModifierFlags(UIGestureRecognizer *recognizer)
@@ -1318,19 +1333,18 @@
     for (const auto& touchPoint : touchEvent.touchPoints()) {
         auto phase = touchPoint.phase();
         if (phase == WebKit::WebPlatformTouchPoint::TouchPressed) {
-            auto touchActionData = scrollingCoordinator->touchActionDataAtPoint(touchPoint.location());
-            if (!touchActionData || touchActionData->touchActions.contains(WebCore::TouchAction::Manipulation))
+            auto touchActions = WebKit::touchActionsForPoint(self, touchPoint.location());
+            if (!touchActions || touchActions.containsAny({ WebCore::TouchAction::Auto, WebCore::TouchAction::Manipulation }))
                 continue;
-            if (auto scrollingNodeID = touchActionData->scrollingNodeID)
-                scrollingCoordinator->setTouchDataForTouchIdentifier(*touchActionData, touchPoint.identifier());
-            else {
-                if (!touchActionData->touchActions.contains(WebCore::TouchAction::PinchZoom))
-                    _webView.scrollView.pinchGestureRecognizer.enabled = NO;
-                _preventsPanningInXAxis = !touchActionData->touchActions.contains(WebCore::TouchAction::PanX);
-                _preventsPanningInYAxis = !touchActionData->touchActions.contains(WebCore::TouchAction::PanY);
-            }
+            scrollingCoordinator->setTouchActionsForTouchIdentifier(touchActions, touchPoint.identifier());
+
+            if (!touchActions.contains(WebCore::TouchAction::PinchZoom))
+                _webView.scrollView.pinchGestureRecognizer.enabled = NO;
+            _preventsPanningInXAxis = !touchActions.contains(WebCore::TouchAction::PanX);
+            _preventsPanningInYAxis = !touchActions.contains(WebCore::TouchAction::PanY);
+
         } else if (phase == WebKit::WebPlatformTouchPoint::TouchReleased || phase == WebKit::WebPlatformTouchPoint::TouchCancelled)
-            scrollingCoordinator->clearTouchDataForTouchIdentifier(touchPoint.identifier());
+            scrollingCoordinator->clearTouchActionsForTouchIdentifier(touchPoint.identifier());
     }
 }
 #endif