Limit user-agent interactions based on the touch-action property on iOS
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Jan 2019 15:08:54 +0000 (15:08 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 28 Jan 2019 15:08:54 +0000 (15:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193447
<rdar://problem/47283874>

Reviewed by Antti Koivisto and Simon Fraser.

Source/WebCore:

We now compile a list of elements with a non-auto touch-action property that is updated whenever an element has its style changed
or is removed from its document. When the content of that list changes, we inform the scrolling coordinator such that it can compile
a list of TouchActionData structures which hold the touch-action value, the ID of the nearest scroll node and the Region containing
the bounds of each of those elements to send it up to the UI process along with touch regions. Computing the list of allowed touch
actions for a given element accounts for not only the value specified directly on that element's style, but also in its hierarchy,
crossing any frame boundary towards the top-level document's root node.

Tests: pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html
       pointerevents/ios/touch-action-none-on-iframe.html
       pointerevents/ios/touch-action-none-on-parent.html
       pointerevents/ios/touch-action-none.html
       pointerevents/ios/touch-action-pan-x-pan-y.html
       pointerevents/ios/touch-action-pan-x.html
       pointerevents/ios/touch-action-pan-y.html
       pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html
       pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html

* WebCore.xcodeproj/project.pbxproj: Update how certain headers are exposed such that they can be used from WebKit.
* dom/Document.cpp:
(WebCore::Document::invalidateRenderingDependentRegions):
(WebCore::Document::nodeWillBeRemoved): Ensure a node that is being removed from this document is no longer listed in its
list of elements with a non-auto touch-action property.
(WebCore::Document::absoluteEventRegionForNode):
(WebCore::Document::absoluteRegionForEventTargets):
(WebCore::Document::updateTouchActionElements): Create a list of elements with a non-auto touch-action property if one doesn't
exist yet and update it to add the given element if it contains a non-auto touch-action, or remove it if it doesn't. If the contents
of that list changed as a result, the scrolling coordinator is informed.
* dom/Document.h:
(WebCore::Document:: const):
* dom/Element.cpp:
(WebCore::parentCrossingFrameBoundaries):
(WebCore::Element::computedTouchActions const): Provide the list of allowed touch actions accounting for the "touch-action" property
specified on this element and all of its hierarchy, crossing frame boundary.
(WebCore::Element::nearestScrollingNodeIDUsingTouchOverflowScrolling const): Provide the ScrollingNodeID, if any, for the nearest scrolling node
for that element. This will allow the UI process to identify which scroll view's behavior to customize to reflect the element's allowed
touch actions.
* dom/Element.h:
* page/scrolling/ScrollingCoordinator.cpp:
(WebCore::ScrollingCoordinator::absoluteEventTrackingRegionsForFrame const): Compute the region for all elements with a non-auto touch-action property
throughout the provided frame and all of its subframes.
* page/scrolling/ScrollingCoordinator.h:
(WebCore::ScrollableAreaParameters::operator== const): Deleted.
* page/scrolling/ScrollingCoordinatorTypes.h: Added.
(WebCore::ScrollableAreaParameters::operator== const):
* page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::touchActionDataAtPoint const): Query the list of TouchActionData objects for a match based on the provided point. Right
now the logic is pretty crude, stopping at the first TouchActionData for which the region contains the provided point, but future patches will
account for overlap and nesting.
* page/scrolling/ScrollingTree.h:
* page/scrolling/ScrollingTreeNode.h:
* platform/EventTrackingRegions.cpp:
(WebCore::operator==):
* platform/EventTrackingRegions.h:
(WebCore::operator!=):
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::resolveElement): Update the list of elements with a non-auto touch-action property when an element's style changes.

Source/WebKit:

Handle the "none", "pan-x", "pan-y" and "pinch-zoom" values for the touch-action property by querying the scrolling tree whenever a touch begins
to identify whether its point is contained within the region of an element with a non-auto touch-action property. If it is, we use the list of
permitted touch actions such to then customize the behavior of the nearest scroll view to pan or zoom only as instructed.

* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<TouchActionData>::encode):
(IPC::ArgumentCoder<TouchActionData>::decode):
(IPC::ArgumentCoder<EventTrackingRegions>::encode):
(IPC::ArgumentCoder<EventTrackingRegions>::decode):
(IPC::ArgumentCoder<Region>::decode):
* Shared/WebCoreArgumentCoders.h:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Account for panning constraints set on the content view to prevent deceleration
to pan the view if it ought not.
(-[WKWebView _scrollView:adjustedOffsetForOffset:translation:startPoint:locationInView:horizontalVelocity:verticalVelocity:]): Implement an additional
UIScrollView delegation method to apply the panning constraints set on the content view while panning.
* UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp:
(WebKit::RemoteScrollingCoordinatorProxy::touchActionDataAtPoint const):
(WebKit::RemoteScrollingCoordinatorProxy::touchActionDataForScrollNodeID const):
(WebKit::RemoteScrollingCoordinatorProxy::setTouchDataForTouchIdentifier):
(WebKit::RemoteScrollingCoordinatorProxy::clearTouchDataForTouchIdentifier):
* UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h:
* UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
* UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
(-[WKScrollingNodeScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Apply the same logic as in WKWebView.
(-[WKScrollingNodeScrollViewDelegate _scrollView:adjustedOffsetForOffset:translation:startPoint:locationInView:horizontalVelocity:verticalVelocity:]): Apply
the same logic as in WKWebView.
(WebKit::ScrollingTreeScrollingNodeDelegateIOS::touchActionData const):
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::isScrollingOrZooming const):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView preventsPanningInXAxis]):
(-[WKContentView preventsPanningInYAxis]):
(-[WKContentView cleanupInteraction]):
(-[WKContentView _webTouchEventsRecognized:]):
(-[WKContentView _handleTouchActionsForTouchEvent:]): As we process touches, check whether there are touch actions set for this touch's points' locations. Based
on those touch actions, either setDefaultPrevented on the _touchEventGestureRecognizer if the touch action is "none" or selectively disable panning and zooming.
(-[WKContentView _resetPanningPreventionFlags]):
(-[WKContentView _didEndScrollingOrZooming]):

LayoutTests:

Add a new series of tests that check that the "none", "pan-x", "pan-y" and "pinch-zoom" values have the expected
impact on page panning on iOS.

* pointerevents/ios/touch-action-none-expected.txt: Added.
* pointerevents/ios/touch-action-none-in-overflow-scrolling-touch-expected.txt: Added.
* pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html: Added.
* pointerevents/ios/touch-action-none-on-iframe-expected.txt: Added.
* pointerevents/ios/touch-action-none-on-iframe.html: Added.
* pointerevents/ios/touch-action-none-on-parent-expected.txt: Added.
* pointerevents/ios/touch-action-none-on-parent.html: Added.
* pointerevents/ios/touch-action-none.html: Added.
* pointerevents/ios/touch-action-pan-x-expected.txt: Added.
* pointerevents/ios/touch-action-pan-x-pan-y-expected.txt: Added.
* pointerevents/ios/touch-action-pan-x-pan-y.html: Added.
* pointerevents/ios/touch-action-pan-x.html: Added.
* pointerevents/ios/touch-action-pan-y-expected.txt: Added.
* pointerevents/ios/touch-action-pan-y.html: Added.
* pointerevents/ios/touch-action-pinch-zoom-allows-zooming-expected.txt: Added.
* pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html: Added.
* pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling-expected.txt: Added.
* pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html: Added.

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

45 files changed:
LayoutTests/ChangeLog
LayoutTests/pointerevents/ios/touch-action-none-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none-in-overflow-scrolling-touch-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none-on-iframe-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none-on-iframe.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none-on-parent-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none-on-parent.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-none.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pan-x-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pan-x.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pan-y-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pan-y.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling-expected.txt [new file with mode: 0644]
LayoutTests/pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/page/scrolling/ScrollingCoordinator.cpp
Source/WebCore/page/scrolling/ScrollingCoordinator.h
Source/WebCore/page/scrolling/ScrollingCoordinatorTypes.h [new file with mode: 0644]
Source/WebCore/page/scrolling/ScrollingTree.cpp
Source/WebCore/page/scrolling/ScrollingTree.h
Source/WebCore/page/scrolling/ScrollingTreeNode.h
Source/WebCore/platform/EventTrackingRegions.cpp
Source/WebCore/platform/EventTrackingRegions.h
Source/WebCore/style/StyleTreeResolver.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebCoreArgumentCoders.cpp
Source/WebKit/Shared/WebCoreArgumentCoders.h
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp
Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h
Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h
Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

index f499282..4dc94e8 100644 (file)
@@ -1,3 +1,33 @@
+2019-01-28  Antoine Quint  <graouts@apple.com>
+
+        Limit user-agent interactions based on the touch-action property on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=193447
+        <rdar://problem/47283874>
+
+        Reviewed by Antti Koivisto and Simon Fraser.
+
+        Add a new series of tests that check that the "none", "pan-x", "pan-y" and "pinch-zoom" values have the expected
+        impact on page panning on iOS.
+
+        * pointerevents/ios/touch-action-none-expected.txt: Added.
+        * pointerevents/ios/touch-action-none-in-overflow-scrolling-touch-expected.txt: Added.
+        * pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html: Added.
+        * pointerevents/ios/touch-action-none-on-iframe-expected.txt: Added.
+        * pointerevents/ios/touch-action-none-on-iframe.html: Added.
+        * pointerevents/ios/touch-action-none-on-parent-expected.txt: Added.
+        * pointerevents/ios/touch-action-none-on-parent.html: Added.
+        * pointerevents/ios/touch-action-none.html: Added.
+        * pointerevents/ios/touch-action-pan-x-expected.txt: Added.
+        * pointerevents/ios/touch-action-pan-x-pan-y-expected.txt: Added.
+        * pointerevents/ios/touch-action-pan-x-pan-y.html: Added.
+        * pointerevents/ios/touch-action-pan-x.html: Added.
+        * pointerevents/ios/touch-action-pan-y-expected.txt: Added.
+        * pointerevents/ios/touch-action-pan-y.html: Added.
+        * pointerevents/ios/touch-action-pinch-zoom-allows-zooming-expected.txt: Added.
+        * pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html: Added.
+        * pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling-expected.txt: Added.
+        * pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html: Added.
+
 2019-01-28  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Add Changes panel to Elements tab
 2019-01-28  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Add Changes panel to Elements tab
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-expected.txt b/LayoutTests/pointerevents/ios/touch-action-none-expected.txt
new file mode 100644 (file)
index 0000000..727e8af
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: none on an element prevents page scrolling. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-in-overflow-scrolling-touch-expected.txt b/LayoutTests/pointerevents/ios/touch-action-none-in-overflow-scrolling-touch-expected.txt
new file mode 100644 (file)
index 0000000..2c00729
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: none in content hosted in a scrollable container using '-webkit-overflow-scrolling: touch' prevents scrolling of that container. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html b/LayoutTests/pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html
new file mode 100644 (file)
index 0000000..9e9614f
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+
+#scrolling-container {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 400px;
+    height: 400px;
+    overflow: scroll;
+    -webkit-overflow-scrolling: touch;
+}
+
+#scrolling-container > div {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 200%;
+    height: 200%;
+    touch-action: none;
+}
+
+</style>
+</head>
+<body>
+<div id="scrolling-container"><div></div></div>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../utils.js"></script>
+<script>
+
+'use strict';
+
+async_test(test => {
+    const scrollingContainer = document.body.firstElementChild;
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        assert_equals(scrollingContainer.scrollLeft, 0, "The scrolling container was not scrolled in the x-axis.");
+        assert_equals(scrollingContainer.scrollTop, 0, "The scrolling container was not scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that setting touch-action: none in content hosted in a scrollable container using '-webkit-overflow-scrolling: touch' prevents scrolling of that container.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-on-iframe-expected.txt b/LayoutTests/pointerevents/ios/touch-action-none-on-iframe-expected.txt
new file mode 100644 (file)
index 0000000..46751bd
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+PASS Testing that setting touch-action: none on an iframe and interacting with its content prevents page scrolling. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-on-iframe.html b/LayoutTests/pointerevents/ios/touch-action-none-on-iframe.html
new file mode 100644 (file)
index 0000000..d927ea8
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+
+iframe {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 400px;
+    height: 400px;
+    touch-action: none;
+}
+
+</style>
+</head>
+<body>
+<iframe></iframe>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../utils.js"></script>
+<script>
+
+'use strict';
+
+async_test(test => {
+    const iframe = document.body.firstElementChild;
+    const target = iframe.contentDocument.body.appendChild(document.createElement("div"));
+    target.setAttribute("style", `
+        position: absolute;
+        left: 0;
+        top: 0;
+        width: 200%;
+        height: 200%;
+    `);
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        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.");
+        test.done();
+    });
+}, "Testing that setting touch-action: none on an iframe and interacting with its content prevents page scrolling.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-on-parent-expected.txt b/LayoutTests/pointerevents/ios/touch-action-none-on-parent-expected.txt
new file mode 100644 (file)
index 0000000..d02b130
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: none on a parent element prevents page scrolling. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-none-on-parent.html b/LayoutTests/pointerevents/ios/touch-action-none-on-parent.html
new file mode 100644 (file)
index 0000000..9a48079
--- /dev/null
@@ -0,0 +1,29 @@
+<!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";
+    document.body.style.touchAction = "none";
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        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.");
+        test.done();
+    });
+}, "Testing that setting touch-action: none on a parent element prevents page scrolling.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-none.html b/LayoutTests/pointerevents/ios/touch-action-none.html
new file mode 100644 (file)
index 0000000..ad00b83
--- /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({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "none";
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        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.");
+        test.done();
+    });
+}, "Testing that setting touch-action: none on an element prevents page scrolling.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-x-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pan-x-expected.txt
new file mode 100644 (file)
index 0000000..87e7837
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: pan-x on an element prevents page scrolling in the y-axis. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y-expected.txt
new file mode 100644 (file)
index 0000000..be97bde
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting 'touch-action: pan-x pan-y' on an element allows page scrolling in both axes. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y.html b/LayoutTests/pointerevents/ios/touch-action-pan-x-pan-y.html
new file mode 100644 (file)
index 0000000..52291a4
--- /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({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pan-x pan-y";
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).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 setting 'touch-action: pan-x pan-y' on an element allows page scrolling in both axes.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-x.html b/LayoutTests/pointerevents/ios/touch-action-pan-x.html
new file mode 100644 (file)
index 0000000..cb9205f
--- /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({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pan-x";
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        assert_not_equals(window.pageXOffset, 0, "The page was scrolled in the x-axis.");
+        assert_equals(window.pageYOffset, 0, "The page was not scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that setting touch-action: pan-x on an element prevents page scrolling in the y-axis.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-y-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pan-y-expected.txt
new file mode 100644 (file)
index 0000000..3ff658c
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: pan-x on an element prevents page scrolling in the x-axis. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pan-y.html b/LayoutTests/pointerevents/ios/touch-action-pan-y.html
new file mode 100644 (file)
index 0000000..9090d39
--- /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({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pan-y";
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        assert_equals(window.pageXOffset, 0, "The page was not scrolled in the x-axis.");
+        assert_not_equals(window.pageYOffset, 0, "The page was scrolled in the y-axis.");
+        test.done();
+    });
+}, "Testing that setting touch-action: pan-x on an element prevents page scrolling in the x-axis.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming-expected.txt
new file mode 100644 (file)
index 0000000..094e676
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: pinch-zoom on an element allows page zooming. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html
new file mode 100644 (file)
index 0000000..2da63c9
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html><!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<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: "400px", height: "400px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pinch-zoom";
+
+    ui.pinchOut({ x: 50, y: 50, width: 100, height: 100, scale: 0.5 }).then(() => {
+        assert_not_equals(window.internals.pageScaleFactor(), 1, "The page was scaled.");
+        test.done();
+    });
+}, "Testing that setting touch-action: pinch-zoom on an element allows page zooming.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling-expected.txt b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling-expected.txt
new file mode 100644 (file)
index 0000000..60c85b9
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Testing that setting touch-action: pinch-zoom on an element prevents page scrolling. 
+
diff --git a/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html b/LayoutTests/pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html
new file mode 100644 (file)
index 0000000..02341c6
--- /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({ width: "200px", height: "200px" }, (target, test) => {
+    document.body.style.width = "2000px";
+    document.body.style.height = "2000px";
+
+    target.style.touchAction = "pinch-zoom";
+
+    ui.swipe({ x: 150, y: 150 }, { x: 50, y: 50 }).then(() => {
+        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.");
+        test.done();
+    });
+}, "Testing that setting touch-action: pinch-zoom on an element prevents page scrolling.");
+
+</script>
+</body>
+</html>
\ No newline at end of file
index 57c90ac..424128a 100644 (file)
@@ -1,3 +1,68 @@
+2019-01-28  Antoine Quint  <graouts@apple.com>
+
+        Limit user-agent interactions based on the touch-action property on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=193447
+        <rdar://problem/47283874>
+
+        Reviewed by Antti Koivisto and Simon Fraser.
+
+        We now compile a list of elements with a non-auto touch-action property that is updated whenever an element has its style changed
+        or is removed from its document. When the content of that list changes, we inform the scrolling coordinator such that it can compile
+        a list of TouchActionData structures which hold the touch-action value, the ID of the nearest scroll node and the Region containing
+        the bounds of each of those elements to send it up to the UI process along with touch regions. Computing the list of allowed touch
+        actions for a given element accounts for not only the value specified directly on that element's style, but also in its hierarchy,
+        crossing any frame boundary towards the top-level document's root node.
+
+        Tests: pointerevents/ios/touch-action-none-in-overflow-scrolling-touch.html
+               pointerevents/ios/touch-action-none-on-iframe.html
+               pointerevents/ios/touch-action-none-on-parent.html
+               pointerevents/ios/touch-action-none.html
+               pointerevents/ios/touch-action-pan-x-pan-y.html
+               pointerevents/ios/touch-action-pan-x.html
+               pointerevents/ios/touch-action-pan-y.html
+               pointerevents/ios/touch-action-pinch-zoom-allows-zooming.html
+               pointerevents/ios/touch-action-pinch-zoom-prevents-scrolling.html
+
+        * WebCore.xcodeproj/project.pbxproj: Update how certain headers are exposed such that they can be used from WebKit.
+        * dom/Document.cpp:
+        (WebCore::Document::invalidateRenderingDependentRegions):
+        (WebCore::Document::nodeWillBeRemoved): Ensure a node that is being removed from this document is no longer listed in its
+        list of elements with a non-auto touch-action property.
+        (WebCore::Document::absoluteEventRegionForNode):
+        (WebCore::Document::absoluteRegionForEventTargets):
+        (WebCore::Document::updateTouchActionElements): Create a list of elements with a non-auto touch-action property if one doesn't
+        exist yet and update it to add the given element if it contains a non-auto touch-action, or remove it if it doesn't. If the contents
+        of that list changed as a result, the scrolling coordinator is informed.
+        * dom/Document.h:
+        (WebCore::Document:: const):
+        * dom/Element.cpp:
+        (WebCore::parentCrossingFrameBoundaries):
+        (WebCore::Element::computedTouchActions const): Provide the list of allowed touch actions accounting for the "touch-action" property
+        specified on this element and all of its hierarchy, crossing frame boundary.
+        (WebCore::Element::nearestScrollingNodeIDUsingTouchOverflowScrolling const): Provide the ScrollingNodeID, if any, for the nearest scrolling node
+        for that element. This will allow the UI process to identify which scroll view's behavior to customize to reflect the element's allowed
+        touch actions.
+        * dom/Element.h:
+        * page/scrolling/ScrollingCoordinator.cpp:
+        (WebCore::ScrollingCoordinator::absoluteEventTrackingRegionsForFrame const): Compute the region for all elements with a non-auto touch-action property
+        throughout the provided frame and all of its subframes.
+        * page/scrolling/ScrollingCoordinator.h:
+        (WebCore::ScrollableAreaParameters::operator== const): Deleted.
+        * page/scrolling/ScrollingCoordinatorTypes.h: Added.
+        (WebCore::ScrollableAreaParameters::operator== const):
+        * page/scrolling/ScrollingTree.cpp:
+        (WebCore::ScrollingTree::touchActionDataAtPoint const): Query the list of TouchActionData objects for a match based on the provided point. Right
+        now the logic is pretty crude, stopping at the first TouchActionData for which the region contains the provided point, but future patches will
+        account for overlap and nesting.
+        * page/scrolling/ScrollingTree.h:
+        * page/scrolling/ScrollingTreeNode.h:
+        * platform/EventTrackingRegions.cpp:
+        (WebCore::operator==):
+        * platform/EventTrackingRegions.h:
+        (WebCore::operator!=):
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::resolveElement): Update the list of elements with a non-auto touch-action property when an element's style changes.
+
 2019-01-27  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         Unreviewed, fix WPE/GTK debug builds after r240557
 2019-01-27  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         Unreviewed, fix WPE/GTK debug builds after r240557
index 7ed125d..9cc3bae 100644 (file)
                712BE4831FE865DD002031CC /* FillMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 712BE4811FE865D4002031CC /* FillMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                712BE4881FE8686A002031CC /* JSFillMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 712BE4851FE86818002031CC /* JSFillMode.h */; };
                712BE4891FE86875002031CC /* JSPlaybackDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 712BE4861FE86859002031CC /* JSPlaybackDirection.h */; };
                712BE4831FE865DD002031CC /* FillMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 712BE4811FE865D4002031CC /* FillMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                712BE4881FE8686A002031CC /* JSFillMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 712BE4851FE86818002031CC /* JSFillMode.h */; };
                712BE4891FE86875002031CC /* JSPlaybackDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 712BE4861FE86859002031CC /* JSPlaybackDirection.h */; };
+               712DBA4921F8AD83008F36B2 /* ScrollingCoordinatorTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 712DBA4721F8AD79008F36B2 /* ScrollingCoordinatorTypes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                713171341FBE78DB00F758DE /* CSSPropertyBlendingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 713171321FBE78C500F758DE /* CSSPropertyBlendingClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7132445120109DA500AE7FB2 /* WebAnimationUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 7132444F20109D9B00AE7FB2 /* WebAnimationUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7134496E146941B300720312 /* SVGLengthContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 7134496C146941B300720312 /* SVGLengthContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
                713171341FBE78DB00F758DE /* CSSPropertyBlendingClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 713171321FBE78C500F758DE /* CSSPropertyBlendingClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7132445120109DA500AE7FB2 /* WebAnimationUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 7132444F20109D9B00AE7FB2 /* WebAnimationUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7134496E146941B300720312 /* SVGLengthContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 7134496C146941B300720312 /* SVGLengthContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
                834476EF1DA5BC5E002B6ED2 /* JSScrollToOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E9B3011DA5A51E00FFE8F6 /* JSScrollToOptions.h */; };
                8348BFAC1B85729800912F36 /* ClassCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8348BFAA1B85729500912F36 /* ClassCollection.h */; };
                834DFAD01F7DAE5D00C2725B /* SharedStringHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 834DFACC1F7DAE5600C2725B /* SharedStringHash.h */; settings = {ATTRIBUTES = (Private, ); }; };
                834476EF1DA5BC5E002B6ED2 /* JSScrollToOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E9B3011DA5A51E00FFE8F6 /* JSScrollToOptions.h */; };
                8348BFAC1B85729800912F36 /* ClassCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8348BFAA1B85729500912F36 /* ClassCollection.h */; };
                834DFAD01F7DAE5D00C2725B /* SharedStringHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 834DFACC1F7DAE5600C2725B /* SharedStringHash.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               83520C7E1A71BFCC006BD2AA /* CSSFontFamily.h in Headers */ = {isa = PBXBuildFile; fileRef = 83520C7D1A71BFCC006BD2AA /* CSSFontFamily.h */; };
+               83520C7E1A71BFCC006BD2AA /* CSSFontFamily.h in Headers */ = {isa = PBXBuildFile; fileRef = 83520C7D1A71BFCC006BD2AA /* CSSFontFamily.h */; settings = {ATTRIBUTES = (Private, ); }; };
                8358CB701C53277500E0C2D8 /* JSXMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F570AD1C53268E007FD6CB /* JSXMLDocument.h */; };
                835D2D781F5F1FBD00141DED /* HTMLInputElementEntriesAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 835D2D751F5F1FB800141DED /* HTMLInputElementEntriesAPI.h */; };
                835D363719FF6193004C93AB /* StyleBuilderCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 835D363619FF6193004C93AB /* StyleBuilderCustom.h */; };
                8358CB701C53277500E0C2D8 /* JSXMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F570AD1C53268E007FD6CB /* JSXMLDocument.h */; };
                835D2D781F5F1FBD00141DED /* HTMLInputElementEntriesAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 835D2D751F5F1FB800141DED /* HTMLInputElementEntriesAPI.h */; };
                835D363719FF6193004C93AB /* StyleBuilderCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 835D363619FF6193004C93AB /* StyleBuilderCustom.h */; };
                9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 9920398118B95BC600B39AF9 /* UserInputBridge.h */; settings = {ATTRIBUTES = (Private, ); }; };
                996E59DF1DF0128D006612B9 /* NavigatorWebDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 996E59DC1DF00D90006612B9 /* NavigatorWebDriver.h */; };
                9A528E8417D7F52F00AA9518 /* FloatingObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A528E8217D7F52F00AA9518 /* FloatingObjects.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 9920398118B95BC600B39AF9 /* UserInputBridge.h */; settings = {ATTRIBUTES = (Private, ); }; };
                996E59DF1DF0128D006612B9 /* NavigatorWebDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 996E59DC1DF00D90006612B9 /* NavigatorWebDriver.h */; };
                9A528E8417D7F52F00AA9518 /* FloatingObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A528E8217D7F52F00AA9518 /* FloatingObjects.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               9AB1F38018E2489A00534743 /* CSSToLengthConversionData.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AB1F37E18E2489A00534743 /* CSSToLengthConversionData.h */; };
+               9AB1F38018E2489A00534743 /* CSSToLengthConversionData.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AB1F37E18E2489A00534743 /* CSSToLengthConversionData.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9B24DE8E15194B9500C59C27 /* HTMLBDIElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B24DE8C15194B9500C59C27 /* HTMLBDIElement.h */; };
                9B2D8A7914997CCF00ECEF3E /* UndoStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B2D8A7814997CCF00ECEF3E /* UndoStep.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9B32CDA913DF7FA900F34D13 /* RenderedPosition.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B32CDA713DF7FA900F34D13 /* RenderedPosition.h */; };
                9B24DE8E15194B9500C59C27 /* HTMLBDIElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B24DE8C15194B9500C59C27 /* HTMLBDIElement.h */; };
                9B2D8A7914997CCF00ECEF3E /* UndoStep.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B2D8A7814997CCF00ECEF3E /* UndoStep.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9B32CDA913DF7FA900F34D13 /* RenderedPosition.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B32CDA713DF7FA900F34D13 /* RenderedPosition.h */; };
                E1E1BF00115FF6FB006F52CA /* WindowsKeyboardCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E1BEFF115FF6FB006F52CA /* WindowsKeyboardCodes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1E6EEA80B628DB3005F2F70 /* JSHTMLSelectElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E6EEA70B628DB3005F2F70 /* JSHTMLSelectElement.h */; };
                E1EC29A00BB04C6B00EA187B /* XPathNodeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = E1EC299E0BB04C6B00EA187B /* XPathNodeSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1E1BF00115FF6FB006F52CA /* WindowsKeyboardCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E1BEFF115FF6FB006F52CA /* WindowsKeyboardCodes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1E6EEA80B628DB3005F2F70 /* JSHTMLSelectElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E1E6EEA70B628DB3005F2F70 /* JSHTMLSelectElement.h */; };
                E1EC29A00BB04C6B00EA187B /* XPathNodeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = E1EC299E0BB04C6B00EA187B /* XPathNodeSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               E1ED8AC30CC49BE000BFC557 /* CSSPrimitiveValueMappings.h in Headers */ = {isa = PBXBuildFile; fileRef = E1ED8AC20CC49BE000BFC557 /* CSSPrimitiveValueMappings.h */; };
+               E1ED8AC30CC49BE000BFC557 /* CSSPrimitiveValueMappings.h in Headers */ = {isa = PBXBuildFile; fileRef = E1ED8AC20CC49BE000BFC557 /* CSSPrimitiveValueMappings.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1F1E8300C3C2BB9006DB391 /* XSLTExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F1E82E0C3C2BB9006DB391 /* XSLTExtensions.h */; };
                E1F80B8818317252007885C3 /* CryptoKeyPair.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F80B8618317252007885C3 /* CryptoKeyPair.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1F80B8E183172B5007885C3 /* JSCryptoKeyPair.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F80B8C183172B5007885C3 /* JSCryptoKeyPair.h */; };
                E1F1E8300C3C2BB9006DB391 /* XSLTExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F1E82E0C3C2BB9006DB391 /* XSLTExtensions.h */; };
                E1F80B8818317252007885C3 /* CryptoKeyPair.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F80B8618317252007885C3 /* CryptoKeyPair.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1F80B8E183172B5007885C3 /* JSCryptoKeyPair.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F80B8C183172B5007885C3 /* JSCryptoKeyPair.h */; };
                712BE4851FE86818002031CC /* JSFillMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFillMode.h; sourceTree = "<group>"; };
                712BE4861FE86859002031CC /* JSPlaybackDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPlaybackDirection.h; sourceTree = "<group>"; };
                712BE4871FE8685A002031CC /* JSPlaybackDirection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPlaybackDirection.cpp; sourceTree = "<group>"; };
                712BE4851FE86818002031CC /* JSFillMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFillMode.h; sourceTree = "<group>"; };
                712BE4861FE86859002031CC /* JSPlaybackDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPlaybackDirection.h; sourceTree = "<group>"; };
                712BE4871FE8685A002031CC /* JSPlaybackDirection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPlaybackDirection.cpp; sourceTree = "<group>"; };
+               712DBA4721F8AD79008F36B2 /* ScrollingCoordinatorTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollingCoordinatorTypes.h; sourceTree = "<group>"; };
                7130141D1DC9C08600CA3A88 /* pip-support.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "pip-support.js"; sourceTree = "<group>"; };
                713171321FBE78C500F758DE /* CSSPropertyBlendingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSPropertyBlendingClient.h; sourceTree = "<group>"; };
                7132444F20109D9B00AE7FB2 /* WebAnimationUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebAnimationUtilities.h; sourceTree = "<group>"; };
                7130141D1DC9C08600CA3A88 /* pip-support.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "pip-support.js"; sourceTree = "<group>"; };
                713171321FBE78C500F758DE /* CSSPropertyBlendingClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSPropertyBlendingClient.h; sourceTree = "<group>"; };
                7132444F20109D9B00AE7FB2 /* WebAnimationUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebAnimationUtilities.h; sourceTree = "<group>"; };
                                0F605AEB15F94848004DF0C0 /* ScrollingConstraints.h */,
                                1AF62EE414DA22A70041556C /* ScrollingCoordinator.cpp */,
                                1AF62EE514DA22A70041556C /* ScrollingCoordinator.h */,
                                0F605AEB15F94848004DF0C0 /* ScrollingConstraints.h */,
                                1AF62EE414DA22A70041556C /* ScrollingCoordinator.cpp */,
                                1AF62EE514DA22A70041556C /* ScrollingCoordinator.h */,
+                               712DBA4721F8AD79008F36B2 /* ScrollingCoordinatorTypes.h */,
                                51C61B081DE536E7008A212D /* ScrollingMomentumCalculator.cpp */,
                                51C61B091DE536E7008A212D /* ScrollingMomentumCalculator.h */,
                                93C38BFC164473C700091EB2 /* ScrollingStateFixedNode.cpp */,
                                51C61B081DE536E7008A212D /* ScrollingMomentumCalculator.cpp */,
                                51C61B091DE536E7008A212D /* ScrollingMomentumCalculator.h */,
                                93C38BFC164473C700091EB2 /* ScrollingStateFixedNode.cpp */,
                                F55B3DC21251F12D003EF269 /* ImageInputType.h in Headers */,
                                089582560E857A7E00F82C83 /* ImageLoader.h in Headers */,
                                BC7F44A80B9E324E00A9D081 /* ImageObserver.h in Headers */,
                                F55B3DC21251F12D003EF269 /* ImageInputType.h in Headers */,
                                089582560E857A7E00F82C83 /* ImageLoader.h in Headers */,
                                BC7F44A80B9E324E00A9D081 /* ImageObserver.h in Headers */,
-                               A59C230A21F29206004EC939 /* InspectorCPUProfilerAgent.h in Headers */,
                                2D5A5931152525D00036EE51 /* ImageOrientation.h in Headers */,
                                B51A2F3F17D7D3AE0072517A /* ImageQualityController.h in Headers */,
                                49291E4B134172C800E753DE /* ImageRenderingMode.h in Headers */,
                                2D5A5931152525D00036EE51 /* ImageOrientation.h in Headers */,
                                B51A2F3F17D7D3AE0072517A /* ImageQualityController.h in Headers */,
                                49291E4B134172C800E753DE /* ImageRenderingMode.h in Headers */,
                                A5B81CA81FAA44620037D1E6 /* InspectorCanvasAgent.h in Headers */,
                                1C81B95C0E97330800266E07 /* InspectorClient.h in Headers */,
                                1C81B95A0E97330800266E07 /* InspectorController.h in Headers */,
                                A5B81CA81FAA44620037D1E6 /* InspectorCanvasAgent.h in Headers */,
                                1C81B95C0E97330800266E07 /* InspectorClient.h in Headers */,
                                1C81B95A0E97330800266E07 /* InspectorController.h in Headers */,
+                               A59C230A21F29206004EC939 /* InspectorCPUProfilerAgent.h in Headers */,
                                A5B81CA91FAA44620037D1E6 /* InspectorCSSAgent.h in Headers */,
                                4A9CC82116BF9BB400EC645A /* InspectorCSSOMWrappers.h in Headers */,
                                A5B81CAA1FAA44620037D1E6 /* InspectorDatabaseAgent.h in Headers */,
                                A5B81CA91FAA44620037D1E6 /* InspectorCSSAgent.h in Headers */,
                                4A9CC82116BF9BB400EC645A /* InspectorCSSOMWrappers.h in Headers */,
                                A5B81CAA1FAA44620037D1E6 /* InspectorDatabaseAgent.h in Headers */,
                                1AF62EE814DA22A70041556C /* ScrollingCoordinator.h in Headers */,
                                0FC4E40D187F82E10045882C /* ScrollingCoordinatorIOS.h in Headers */,
                                9391A991162746CB00297330 /* ScrollingCoordinatorMac.h in Headers */,
                                1AF62EE814DA22A70041556C /* ScrollingCoordinator.h in Headers */,
                                0FC4E40D187F82E10045882C /* ScrollingCoordinatorIOS.h in Headers */,
                                9391A991162746CB00297330 /* ScrollingCoordinatorMac.h in Headers */,
+                               712DBA4921F8AD83008F36B2 /* ScrollingCoordinatorTypes.h in Headers */,
                                51C61B0B1DE536E7008A212D /* ScrollingMomentumCalculator.h in Headers */,
                                517DEEE81DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h in Headers */,
                                93C38BFF164473C700091EB2 /* ScrollingStateFixedNode.h in Headers */,
                                51C61B0B1DE536E7008A212D /* ScrollingMomentumCalculator.h in Headers */,
                                517DEEE81DE94B0800B91644 /* ScrollingMomentumCalculatorMac.h in Headers */,
                                93C38BFF164473C700091EB2 /* ScrollingStateFixedNode.h in Headers */,
index 6d443e7..612c35f 100644 (file)
 #include "TextAutoSizing.h"
 #include "TextEvent.h"
 #include "TextNodeTraversal.h"
 #include "TextAutoSizing.h"
 #include "TextEvent.h"
 #include "TextNodeTraversal.h"
+#include "TouchAction.h"
 #include "TransformSource.h"
 #include "TreeWalker.h"
 #include "UndoManager.h"
 #include "TransformSource.h"
 #include "TreeWalker.h"
 #include "UndoManager.h"
@@ -4151,6 +4152,15 @@ void Document::invalidateRenderingDependentRegions(AnnotationsAction annotations
 #if PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
     setTouchEventRegionsNeedUpdate();
 #endif
 #if PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
     setTouchEventRegionsNeedUpdate();
 #endif
+
+#if ENABLE(POINTER_EVENTS)
+    if (auto* page = this->page()) {
+        if (auto* frameView = view()) {
+            if (auto* scrollingCoordinator = page->scrollingCoordinator())
+                scrollingCoordinator->frameViewEventTrackingRegionsChanged(*frameView);
+        }
+    }
+#endif
 }
 
 void Document::invalidateScrollbarDependentRegions()
 }
 
 void Document::invalidateScrollbarDependentRegions()
@@ -4534,6 +4544,11 @@ void Document::nodeWillBeRemoved(Node& node)
 
     if (is<Text>(node))
         m_markers->removeMarkers(node);
 
     if (is<Text>(node))
         m_markers->removeMarkers(node);
+
+#if ENABLE(POINTER_EVENTS)
+    if (m_touchActionElements && is<Element>(node))
+        m_touchActionElements->remove(&downcast<Element>(node));
+#endif
 }
 
 static Node* fallbackFocusNavigationStartingNodeAfterRemoval(Node& node)
 }
 
 static Node* fallbackFocusNavigationStartingNodeAfterRemoval(Node& node)
@@ -7096,6 +7111,34 @@ LayoutRect Document::absoluteEventHandlerBounds(bool& includesFixedPositionEleme
     return LayoutRect();
 }
 
     return LayoutRect();
 }
 
+Document::RegionFixedPair Document::absoluteEventRegionForNode(Node& node)
+{
+    Region region;
+    LayoutRect rootRelativeBounds;
+    bool insideFixedPosition = false;
+
+    if (is<Document>(node)) {
+        auto& document = downcast<Document>(node);
+        if (&document == this)
+            rootRelativeBounds = absoluteEventHandlerBounds(insideFixedPosition);
+        else if (Element* element = document.ownerElement())
+            rootRelativeBounds = element->absoluteEventHandlerBounds(insideFixedPosition);
+    } else if (is<Element>(node)) {
+        auto& element = downcast<Element>(node);
+        if (is<HTMLBodyElement>(element)) {
+            // For the body, just use the document bounds.
+            // The body may not cover this whole area, but it's OK for this region to be an overestimate.
+            rootRelativeBounds = absoluteEventHandlerBounds(insideFixedPosition);
+        } else
+            rootRelativeBounds = element.absoluteEventHandlerBounds(insideFixedPosition);
+    }
+
+    if (!rootRelativeBounds.isEmpty())
+        region.unite(Region(enclosingIntRect(rootRelativeBounds)));
+
+    return RegionFixedPair(region, insideFixedPosition);
+}
+
 Document::RegionFixedPair Document::absoluteRegionForEventTargets(const EventTargetSet* targets)
 {
     LayoutDisallowedScope layoutDisallowedScope(LayoutDisallowedScope::Reason::ReentrancyAvoidance);
 Document::RegionFixedPair Document::absoluteRegionForEventTargets(const EventTargetSet* targets)
 {
     LayoutDisallowedScope layoutDisallowedScope(LayoutDisallowedScope::Reason::ReentrancyAvoidance);
@@ -7107,26 +7150,11 @@ Document::RegionFixedPair Document::absoluteRegionForEventTargets(const EventTar
     bool insideFixedPosition = false;
 
     for (auto& keyValuePair : *targets) {
     bool insideFixedPosition = false;
 
     for (auto& keyValuePair : *targets) {
-        LayoutRect rootRelativeBounds;
-
-        if (is<Document>(keyValuePair.key)) {
-            Document* document = downcast<Document>(keyValuePair.key);
-            if (document == this)
-                rootRelativeBounds = absoluteEventHandlerBounds(insideFixedPosition);
-            else if (Element* element = document->ownerElement())
-                rootRelativeBounds = element->absoluteEventHandlerBounds(insideFixedPosition);
-        } else if (is<Element>(keyValuePair.key)) {
-            Element* element = downcast<Element>(keyValuePair.key);
-            if (is<HTMLBodyElement>(element)) {
-                // For the body, just use the document bounds.
-                // The body may not cover this whole area, but it's OK for this region to be an overestimate.
-                rootRelativeBounds = absoluteEventHandlerBounds(insideFixedPosition);
-            } else
-                rootRelativeBounds = element->absoluteEventHandlerBounds(insideFixedPosition);
+        if (auto* node = keyValuePair.key) {
+            auto targetRegionFixedPair = absoluteEventRegionForNode(*node);
+            targetRegion.unite(targetRegionFixedPair.first);
+            insideFixedPosition |= targetRegionFixedPair.second;
         }
         }
-        
-        if (!rootRelativeBounds.isEmpty())
-            targetRegion.unite(Region(enclosingIntRect(rootRelativeBounds)));
     }
 
     return RegionFixedPair(targetRegion, insideFixedPosition);
     }
 
     return RegionFixedPair(targetRegion, insideFixedPosition);
@@ -8658,4 +8686,32 @@ void Document::setPaintWorkletGlobalScopeForName(const String& name, Ref<PaintWo
 }
 #endif
 
 }
 #endif
 
+#if ENABLE(POINTER_EVENTS)
+void Document::updateTouchActionElements(Element& element, const RenderStyle& style)
+{
+    bool changed = false;
+
+    if (style.touchActions() != TouchAction::Auto) {
+        if (!m_touchActionElements)
+            m_touchActionElements = std::make_unique<HashSet<Element*>>();
+        changed |= m_touchActionElements->add(&element).isNewEntry;
+    } else if (m_touchActionElements)
+        changed |= m_touchActionElements->remove(&element);
+
+#if PLATFORM(IOS_FAMILY)
+    if (!changed)
+        return;
+
+    Page* page = this->page();
+    if (!page)
+        return;
+
+    if (FrameView* frameView = view()) {
+        if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
+            scrollingCoordinator->frameViewEventTrackingRegionsChanged(*frameView);
+    }
+#endif
+}
+#endif
+
 } // namespace WebCore
 } // namespace WebCore
index 30f0ee6..8e2878f 100644 (file)
@@ -1276,6 +1276,10 @@ public:
     bool hasTouchEventHandlers() const { return false; }
     bool touchEventTargetsContain(Node&) const { return false; }
 #endif
     bool hasTouchEventHandlers() const { return false; }
     bool touchEventTargetsContain(Node&) const { return false; }
 #endif
+#if ENABLE(POINTER_EVENTS)
+    void updateTouchActionElements(Element&, const RenderStyle&);
+    const HashSet<Element*>* touchActionElements() const { return m_touchActionElements.get(); }
+#endif
 
     void didAddTouchEventHandler(Node&);
     void didRemoveTouchEventHandler(Node&, EventHandlerRemoval = EventHandlerRemoval::One);
 
     void didAddTouchEventHandler(Node&);
     void didRemoveTouchEventHandler(Node&, EventHandlerRemoval = EventHandlerRemoval::One);
@@ -1294,6 +1298,7 @@ public:
     const EventTargetSet* wheelEventTargets() const { return m_wheelEventTargets.get(); }
 
     typedef std::pair<Region, bool> RegionFixedPair;
     const EventTargetSet* wheelEventTargets() const { return m_wheelEventTargets.get(); }
 
     typedef std::pair<Region, bool> RegionFixedPair;
+    RegionFixedPair absoluteEventRegionForNode(Node&);
     RegionFixedPair absoluteRegionForEventTargets(const EventTargetSet*);
 
     LayoutRect absoluteEventHandlerBounds(bool&) final;
     RegionFixedPair absoluteRegionForEventTargets(const EventTargetSet*);
 
     LayoutRect absoluteEventHandlerBounds(bool&) final;
@@ -1875,6 +1880,9 @@ private:
 #if ENABLE(TOUCH_EVENTS)
     std::unique_ptr<EventTargetSet> m_touchEventTargets;
 #endif
 #if ENABLE(TOUCH_EVENTS)
     std::unique_ptr<EventTargetSet> m_touchEventTargets;
 #endif
+#if ENABLE(POINTER_EVENTS)
+    std::unique_ptr<HashSet<Element*>> m_touchActionElements;
+#endif
     std::unique_ptr<EventTargetSet> m_wheelEventTargets;
 
     MonotonicTime m_lastHandledUserGestureTimestamp;
     std::unique_ptr<EventTargetSet> m_wheelEventTargets;
 
     MonotonicTime m_lastHandledUserGestureTimestamp;
index ea5493a..ca4bc99 100644 (file)
@@ -81,6 +81,8 @@
 #include "PointerLockController.h"
 #include "RenderFragmentContainer.h"
 #include "RenderLayer.h"
 #include "PointerLockController.h"
 #include "RenderFragmentContainer.h"
 #include "RenderLayer.h"
+#include "RenderLayerBacking.h"
+#include "RenderLayerCompositor.h"
 #include "RenderListBox.h"
 #include "RenderTheme.h"
 #include "RenderTreeUpdater.h"
 #include "RenderListBox.h"
 #include "RenderTheme.h"
 #include "RenderTreeUpdater.h"
@@ -3418,7 +3420,7 @@ void Element::setContainsFullScreenElement(bool flag)
     invalidateStyleAndLayerComposition();
 }
 
     invalidateStyleAndLayerComposition();
 }
 
-static Element* parentCrossingFrameBoundaries(Element* element)
+static Element* parentCrossingFrameBoundaries(const Element* element)
 {
     ASSERT(element);
     return element->parentElement() ? element->parentElement() : element->document().ownerElement();
 {
     ASSERT(element);
     return element->parentElement() ? element->parentElement() : element->document().ownerElement();
@@ -4122,4 +4124,66 @@ void Element::setAttributeStyleMap(Ref<StylePropertyMap>&& map)
 }
 #endif
 
 }
 #endif
 
+#if ENABLE(POINTER_EVENTS)
+OptionSet<TouchAction> Element::computedTouchActions() const
+{
+    OptionSet<TouchAction> computedTouchActions = TouchAction::Auto;
+    for (auto* element = this; element; element = parentCrossingFrameBoundaries(element)) {
+        auto* renderer = element->renderer();
+        if (!renderer)
+            continue;
+
+        auto touchActions = renderer->style().touchActions();
+
+        // Once we've encountered touch-action: none, we know that this will be the computed value.
+        if (touchActions == TouchAction::None)
+            return touchActions;
+
+        // If the computed touch-action so far was "auto", we can just use the current element's touch-action.
+        if (computedTouchActions == TouchAction::Auto) {
+            computedTouchActions = touchActions;
+            continue;
+        }
+
+        // If the current element has touch-action: auto or the same touch-action as the computed touch-action,
+        // we need to keep going up the ancestry chain.
+        if (touchActions == TouchAction::Auto || touchActions == computedTouchActions)
+            continue;
+
+        // Now, the element's touch-action and the computed touch-action are different and are neither "auto" nor "none".
+        if (computedTouchActions == TouchAction::Manipulation) {
+            // If the computed touch-action is "manipulation", we can take the current element's touch-action as the newly
+            // computed touch-action.
+            computedTouchActions = touchActions;
+        } else if (touchActions == TouchAction::Manipulation) {
+            // Otherwise, we have a restricted computed touch-action so far. If the current element's touch-action is "manipulation"
+            // then we can just keep going and leave the computed touch-action untouched.
+            continue;
+        }
+
+        // In any other case, we have competing restrictive touch-action values that can only yield "none".
+        return TouchAction::None;
+    }
+    return computedTouchActions;
+}
+
+#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
+ScrollingNodeID Element::nearestScrollingNodeIDUsingTouchOverflowScrolling() const
+{
+    if (!renderer())
+        return 0;
+
+    // We are not interested in the root, so check that we also have a valid parent.
+    for (auto* layer = renderer()->enclosingLayer(); layer && layer->parent(); layer = layer->parent()) {
+        if (layer->isComposited()) {
+            if (auto scrollingNodeID = layer->backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling))
+                return scrollingNodeID;
+        }
+    }
+
+    return 0;
+}
+#endif
+#endif
+
 } // namespace WebCore
 } // namespace WebCore
index c5eb6cb..6ae1618 100644 (file)
@@ -31,6 +31,7 @@
 #include "KeyframeAnimationOptions.h"
 #include "ScrollToOptions.h"
 #include "ScrollTypes.h"
 #include "KeyframeAnimationOptions.h"
 #include "ScrollToOptions.h"
 #include "ScrollTypes.h"
+#include "ScrollingCoordinator.h"
 #include "ShadowRootMode.h"
 #include "SimulatedClickOptions.h"
 #include "StyleChange.h"
 #include "ShadowRootMode.h"
 #include "SimulatedClickOptions.h"
 #include "StyleChange.h"
@@ -75,6 +76,10 @@ enum class SelectionRevealMode {
     DoNotReveal
 };
 
     DoNotReveal
 };
 
+#if ENABLE(POINTER_EVENTS)
+enum class TouchAction : uint8_t;
+#endif
+
 class Element : public ContainerNode {
     WTF_MAKE_ISO_ALLOCATED(Element);
 public:
 class Element : public ContainerNode {
     WTF_MAKE_ISO_ALLOCATED(Element);
 public:
@@ -588,6 +593,13 @@ public:
     ExceptionOr<Ref<WebAnimation>> animate(JSC::ExecState&, JSC::Strong<JSC::JSObject>&&, Optional<Variant<double, KeyframeAnimationOptions>>&&);
     Vector<RefPtr<WebAnimation>> getAnimations();
 
     ExceptionOr<Ref<WebAnimation>> animate(JSC::ExecState&, JSC::Strong<JSC::JSObject>&&, Optional<Variant<double, KeyframeAnimationOptions>>&&);
     Vector<RefPtr<WebAnimation>> getAnimations();
 
+#if ENABLE(POINTER_EVENTS)
+    OptionSet<TouchAction> computedTouchActions() const;
+#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
+    ScrollingNodeID nearestScrollingNodeIDUsingTouchOverflowScrolling() const;
+#endif
+#endif
+
 protected:
     Element(const QualifiedName&, Document&, ConstructionType);
 
 protected:
     Element(const QualifiedName&, Document&, ConstructionType);
 
index c4faedd..d056a8b 100644 (file)
@@ -38,6 +38,7 @@
 #include "Region.h"
 #include "RenderLayerCompositor.h"
 #include "RenderView.h"
 #include "Region.h"
 #include "RenderLayerCompositor.h"
 #include "RenderView.h"
+#include "RuntimeEnabledFeatures.h"
 #include "ScrollAnimator.h"
 #include "Settings.h"
 #include <wtf/MainThread.h>
 #include "ScrollAnimator.h"
 #include "Settings.h"
 #include <wtf/MainThread.h>
@@ -107,7 +108,25 @@ EventTrackingRegions ScrollingCoordinator::absoluteEventTrackingRegionsForFrame(
     auto* document = frame.document();
     if (!document)
         return EventTrackingRegions();
     auto* document = frame.document();
     if (!document)
         return EventTrackingRegions();
-    return document->eventTrackingRegions();
+    auto eventTrackingRegions = document->eventTrackingRegions();
+
+#if ENABLE(POINTER_EVENTS)
+    if (RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled()) {
+        if (auto* touchActionElements = frame.document()->touchActionElements()) {
+            auto& touchActionData = eventTrackingRegions.touchActionData;
+            for (const auto& element : *touchActionElements) {
+                ASSERT(element);
+                touchActionData.append({
+                    element->computedTouchActions(),
+                    element->nearestScrollingNodeIDUsingTouchOverflowScrolling(),
+                    element->document().absoluteEventRegionForNode(*element).first
+                });
+            }
+        }
+    }
+#endif
+
+    return eventTrackingRegions;
 #else
     auto* frameView = frame.view();
     if (!frameView)
 #else
     auto* frameView = frame.view();
     if (!frameView)
index 6547daa..53322f6 100644 (file)
@@ -30,6 +30,7 @@
 #include "PlatformWheelEvent.h"
 #include "ScrollSnapOffsetsInfo.h"
 #include "ScrollTypes.h"
 #include "PlatformWheelEvent.h"
 #include "ScrollSnapOffsetsInfo.h"
 #include "ScrollTypes.h"
+#include "ScrollingCoordinatorTypes.h"
 #include <wtf/Forward.h>
 #include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/TypeCasts.h>
 #include <wtf/Forward.h>
 #include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/TypeCasts.h>
@@ -51,27 +52,6 @@ class TextStream;
 
 namespace WebCore {
 
 
 namespace WebCore {
 
-typedef unsigned SynchronousScrollingReasons;
-typedef uint64_t ScrollingNodeID;
-
-enum class ScrollingNodeType : uint8_t {
-    MainFrame,
-    Subframe,
-    FrameHosting,
-    Overflow,
-    Fixed,
-    Sticky
-};
-
-enum ScrollingStateTreeAsTextBehaviorFlags {
-    ScrollingStateTreeAsTextBehaviorNormal                  = 0,
-    ScrollingStateTreeAsTextBehaviorIncludeLayerIDs         = 1 << 0,
-    ScrollingStateTreeAsTextBehaviorIncludeNodeIDs          = 1 << 1,
-    ScrollingStateTreeAsTextBehaviorIncludeLayerPositions   = 1 << 2,
-    ScrollingStateTreeAsTextBehaviorDebug                   = ScrollingStateTreeAsTextBehaviorIncludeLayerIDs | ScrollingStateTreeAsTextBehaviorIncludeNodeIDs | ScrollingStateTreeAsTextBehaviorIncludeLayerPositions
-};
-typedef unsigned ScrollingStateTreeAsTextBehavior;
-
 class Document;
 class Frame;
 class FrameView;
 class Document;
 class Frame;
 class FrameView;
@@ -86,42 +66,6 @@ class ViewportConstraints;
 class ScrollingTree;
 #endif
 
 class ScrollingTree;
 #endif
 
-enum class ScrollingLayerPositionAction {
-    Set,
-    SetApproximate,
-    Sync
-};
-
-struct ScrollableAreaParameters {
-    ScrollElasticity horizontalScrollElasticity { ScrollElasticityNone };
-    ScrollElasticity verticalScrollElasticity { ScrollElasticityNone };
-
-    ScrollbarMode horizontalScrollbarMode { ScrollbarAuto };
-    ScrollbarMode verticalScrollbarMode { ScrollbarAuto };
-
-    bool hasEnabledHorizontalScrollbar { false };
-    bool hasEnabledVerticalScrollbar { false };
-
-    bool useDarkAppearanceForScrollbars { false };
-
-    bool operator==(const ScrollableAreaParameters& other) const
-    {
-        return horizontalScrollElasticity == other.horizontalScrollElasticity
-            && verticalScrollElasticity == other.verticalScrollElasticity
-            && horizontalScrollbarMode == other.horizontalScrollbarMode
-            && verticalScrollbarMode == other.verticalScrollbarMode
-            && hasEnabledHorizontalScrollbar == other.hasEnabledHorizontalScrollbar
-            && hasEnabledVerticalScrollbar == other.hasEnabledVerticalScrollbar
-            && useDarkAppearanceForScrollbars == other.useDarkAppearanceForScrollbars;
-    }
-};
-
-enum class ViewportRectStability {
-    Stable,
-    Unstable,
-    ChangingObscuredInsetsInteractively // This implies Unstable.
-};
-
 class ScrollingCoordinator : public ThreadSafeRefCounted<ScrollingCoordinator> {
 public:
     static Ref<ScrollingCoordinator> create(Page*);
 class ScrollingCoordinator : public ThreadSafeRefCounted<ScrollingCoordinator> {
 public:
     static Ref<ScrollingCoordinator> create(Page*);
diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinatorTypes.h b/Source/WebCore/page/scrolling/ScrollingCoordinatorTypes.h
new file mode 100644 (file)
index 0000000..137ebd2
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ScrollTypes.h"
+
+namespace WebCore {
+
+typedef unsigned SynchronousScrollingReasons;
+typedef uint64_t ScrollingNodeID;
+
+enum class ScrollingNodeType : uint8_t {
+    MainFrame,
+    Subframe,
+    FrameHosting,
+    Overflow,
+    Fixed,
+    Sticky
+};
+
+enum ScrollingStateTreeAsTextBehaviorFlags {
+    ScrollingStateTreeAsTextBehaviorNormal                  = 0,
+    ScrollingStateTreeAsTextBehaviorIncludeLayerIDs         = 1 << 0,
+    ScrollingStateTreeAsTextBehaviorIncludeNodeIDs          = 1 << 1,
+    ScrollingStateTreeAsTextBehaviorIncludeLayerPositions   = 1 << 2,
+    ScrollingStateTreeAsTextBehaviorDebug                   = ScrollingStateTreeAsTextBehaviorIncludeLayerIDs | ScrollingStateTreeAsTextBehaviorIncludeNodeIDs | ScrollingStateTreeAsTextBehaviorIncludeLayerPositions
+};
+typedef unsigned ScrollingStateTreeAsTextBehavior;
+
+enum class ScrollingLayerPositionAction {
+    Set,
+    SetApproximate,
+    Sync
+};
+
+struct ScrollableAreaParameters {
+    ScrollElasticity horizontalScrollElasticity { ScrollElasticityNone };
+    ScrollElasticity verticalScrollElasticity { ScrollElasticityNone };
+
+    ScrollbarMode horizontalScrollbarMode { ScrollbarAuto };
+    ScrollbarMode verticalScrollbarMode { ScrollbarAuto };
+
+    bool hasEnabledHorizontalScrollbar { false };
+    bool hasEnabledVerticalScrollbar { false };
+
+    bool useDarkAppearanceForScrollbars { false };
+
+    bool operator==(const ScrollableAreaParameters& other) const
+    {
+        return horizontalScrollElasticity == other.horizontalScrollElasticity
+            && verticalScrollElasticity == other.verticalScrollElasticity
+            && horizontalScrollbarMode == other.horizontalScrollbarMode
+            && verticalScrollbarMode == other.verticalScrollbarMode
+            && hasEnabledHorizontalScrollbar == other.hasEnabledHorizontalScrollbar
+            && hasEnabledVerticalScrollbar == other.hasEnabledVerticalScrollbar
+            && useDarkAppearanceForScrollbars == other.useDarkAppearanceForScrollbars;
+    }
+};
+
+enum class ViewportRectStability {
+    Stable,
+    Unstable,
+    ChangingObscuredInsetsInteractively // This implies Unstable.
+};
+
+}
index 524393d..434f584 100644 (file)
@@ -403,6 +403,19 @@ String ScrollingTree::scrollingTreeAsText()
     return ts.release();
 }
 
     return ts.release();
 }
 
+#if ENABLE(POINTER_EVENTS)
+Optional<TouchActionData> ScrollingTree::touchActionDataAtPoint(IntPoint p) const
+{
+    // FIXME: This does not handle the case where there are multiple regions matching this point.
+    for (auto& touchActionData : m_eventTrackingRegions.touchActionData) {
+        if (touchActionData.region.contains(p))
+            return touchActionData;
+    }
+
+    return WTF::nullopt;
+}
+#endif
+
 } // namespace WebCore
 
 #endif // ENABLE(ASYNC_SCROLLING)
 } // namespace WebCore
 
 #endif // ENABLE(ASYNC_SCROLLING)
index efc6841..6d24683 100644 (file)
@@ -30,6 +30,7 @@
 #include "PlatformWheelEvent.h"
 #include "Region.h"
 #include "ScrollingCoordinator.h"
 #include "PlatformWheelEvent.h"
 #include "Region.h"
 #include "ScrollingCoordinator.h"
+#include "TouchAction.h"
 #include "WheelEventTestTrigger.h"
 #include <wtf/HashMap.h>
 #include <wtf/Lock.h>
 #include "WheelEventTestTrigger.h"
 #include <wtf/HashMap.h>
 #include <wtf/Lock.h>
@@ -104,7 +105,10 @@ public:
 #endif
 
     WEBCORE_EXPORT TrackingType eventTrackingTypeForPoint(const AtomicString& eventName, IntPoint);
 #endif
 
     WEBCORE_EXPORT TrackingType eventTrackingTypeForPoint(const AtomicString& eventName, IntPoint);
-    
+#if ENABLE(POINTER_EVENTS)
+    WEBCORE_EXPORT Optional<TouchActionData> touchActionDataAtPoint(IntPoint) const;
+#endif
+
 #if PLATFORM(MAC)
     virtual void handleWheelEventPhase(PlatformWheelEventPhase) = 0;
     virtual void setActiveScrollSnapIndices(ScrollingNodeID, unsigned /*horizontalIndex*/, unsigned /*verticalIndex*/) { }
 #if PLATFORM(MAC)
     virtual void handleWheelEventPhase(PlatformWheelEventPhase) = 0;
     virtual void setActiveScrollSnapIndices(ScrollingNodeID, unsigned /*horizontalIndex*/, unsigned /*verticalIndex*/) { }
index 2f4415a..2ae1a36 100644 (file)
@@ -31,6 +31,7 @@
 #include "ScrollTypes.h"
 #include "ScrollingCoordinator.h"
 #include "ScrollingStateNode.h"
 #include "ScrollTypes.h"
 #include "ScrollingCoordinator.h"
 #include "ScrollingStateNode.h"
+#include "TouchAction.h"
 #include <wtf/RefCounted.h>
 #include <wtf/TypeCasts.h>
 
 #include <wtf/RefCounted.h>
 #include <wtf/TypeCasts.h>
 
index 7e763c8..4377800 100644 (file)
@@ -73,7 +73,19 @@ void EventTrackingRegions::unite(const EventTrackingRegions& eventTrackingRegion
 bool operator==(const EventTrackingRegions& a, const EventTrackingRegions& b)
 {
     return a.asynchronousDispatchRegion == b.asynchronousDispatchRegion
 bool operator==(const EventTrackingRegions& a, const EventTrackingRegions& b)
 {
     return a.asynchronousDispatchRegion == b.asynchronousDispatchRegion
+#if ENABLE(POINTER_EVENTS)
+        && a.touchActionData == b.touchActionData
+#endif
         && a.eventSpecificSynchronousDispatchRegions == b.eventSpecificSynchronousDispatchRegions;
 }
 
         && a.eventSpecificSynchronousDispatchRegions == b.eventSpecificSynchronousDispatchRegions;
 }
 
+#if ENABLE(POINTER_EVENTS)
+bool operator==(const TouchActionData& a, const TouchActionData& b)
+{
+    return a.touchActions == b.touchActions
+        && a.scrollingNodeID == b.scrollingNodeID
+        && a.region == b.region;
+}
+#endif
+
 } // namespace WebCore
 } // namespace WebCore
index 3d58ba5..e71f51a 100644 (file)
 #include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
 
 #include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
 
+#if ENABLE(POINTER_EVENTS)
+#include "CSSPrimitiveValueMappings.h"
+#include "ScrollingCoordinatorTypes.h"
+#include "TouchAction.h"
+#endif
+
 namespace WebCore {
 
 enum class TrackingType : uint8_t {
 namespace WebCore {
 
 enum class TrackingType : uint8_t {
@@ -38,6 +44,18 @@ enum class TrackingType : uint8_t {
     Synchronous = 2
 };
 
     Synchronous = 2
 };
 
+#if ENABLE(POINTER_EVENTS)
+typedef uint64_t ScrollingNodeID;
+struct TouchActionData {
+    OptionSet<TouchAction> touchActions { TouchAction::Auto };
+    ScrollingNodeID scrollingNodeID { 0 };
+    Region region;
+};
+
+bool operator==(const TouchActionData&, const TouchActionData&);
+inline bool operator!=(const TouchActionData& a, const TouchActionData& b) { return !(a == b); }
+#endif
+
 struct EventTrackingRegions {
     // Region for which events can be dispatched without blocking scrolling.
     Region asynchronousDispatchRegion;
 struct EventTrackingRegions {
     // Region for which events can be dispatched without blocking scrolling.
     Region asynchronousDispatchRegion;
@@ -46,6 +64,10 @@ struct EventTrackingRegions {
     // The key is the Event Name with an active handler.
     HashMap<String, Region> eventSpecificSynchronousDispatchRegions;
 
     // The key is the Event Name with an active handler.
     HashMap<String, Region> eventSpecificSynchronousDispatchRegions;
 
+#if ENABLE(POINTER_EVENTS)
+    Vector<TouchActionData> touchActionData;
+#endif
+
     bool isEmpty() const;
 
     void translate(IntSize);
     bool isEmpty() const;
 
     void translate(IntSize);
index 1b134df..3d57b26 100644 (file)
@@ -236,6 +236,11 @@ ElementUpdates TreeResolver::resolveElement(Element& element)
     auto beforeUpdate = resolvePseudoStyle(element, update, PseudoId::Before);
     auto afterUpdate = resolvePseudoStyle(element, update, PseudoId::After);
 
     auto beforeUpdate = resolvePseudoStyle(element, update, PseudoId::Before);
     auto afterUpdate = resolvePseudoStyle(element, update, PseudoId::After);
 
+#if ENABLE(POINTER_EVENTS)
+    if (RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled())
+        m_document.updateTouchActionElements(element, *update.style.get());
+#endif
+
     return { WTFMove(update), descendantsToResolve, WTFMove(beforeUpdate), WTFMove(afterUpdate) };
 }
 
     return { WTFMove(update), descendantsToResolve, WTFMove(beforeUpdate), WTFMove(afterUpdate) };
 }
 
index 00243e1..30a40ff 100644 (file)
@@ -1,3 +1,52 @@
+2019-01-28  Antoine Quint  <graouts@apple.com>
+
+        Limit user-agent interactions based on the touch-action property on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=193447
+        <rdar://problem/47283874>
+
+        Reviewed by Antti Koivisto and Simon Fraser.
+
+        Handle the "none", "pan-x", "pan-y" and "pinch-zoom" values for the touch-action property by querying the scrolling tree whenever a touch begins
+        to identify whether its point is contained within the region of an element with a non-auto touch-action property. If it is, we use the list of
+        permitted touch actions such to then customize the behavior of the nearest scroll view to pan or zoom only as instructed.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<TouchActionData>::encode):
+        (IPC::ArgumentCoder<TouchActionData>::decode):
+        (IPC::ArgumentCoder<EventTrackingRegions>::encode):
+        (IPC::ArgumentCoder<EventTrackingRegions>::decode):
+        (IPC::ArgumentCoder<Region>::decode):
+        * Shared/WebCoreArgumentCoders.h:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Account for panning constraints set on the content view to prevent deceleration
+        to pan the view if it ought not.
+        (-[WKWebView _scrollView:adjustedOffsetForOffset:translation:startPoint:locationInView:horizontalVelocity:verticalVelocity:]): Implement an additional
+        UIScrollView delegation method to apply the panning constraints set on the content view while panning.
+        * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp:
+        (WebKit::RemoteScrollingCoordinatorProxy::touchActionDataAtPoint const):
+        (WebKit::RemoteScrollingCoordinatorProxy::touchActionDataForScrollNodeID const):
+        (WebKit::RemoteScrollingCoordinatorProxy::setTouchDataForTouchIdentifier):
+        (WebKit::RemoteScrollingCoordinatorProxy::clearTouchDataForTouchIdentifier):
+        * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.h:
+        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
+        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
+        (-[WKScrollingNodeScrollViewDelegate scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Apply the same logic as in WKWebView.
+        (-[WKScrollingNodeScrollViewDelegate _scrollView:adjustedOffsetForOffset:translation:startPoint:locationInView:horizontalVelocity:verticalVelocity:]): Apply
+        the same logic as in WKWebView.
+        (WebKit::ScrollingTreeScrollingNodeDelegateIOS::touchActionData const):
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::isScrollingOrZooming const):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView preventsPanningInXAxis]):
+        (-[WKContentView preventsPanningInYAxis]):
+        (-[WKContentView cleanupInteraction]):
+        (-[WKContentView _webTouchEventsRecognized:]):
+        (-[WKContentView _handleTouchActionsForTouchEvent:]): As we process touches, check whether there are touch actions set for this touch's points' locations. Based
+        on those touch actions, either setDefaultPrevented on the _touchEventGestureRecognizer if the touch action is "none" or selectively disable panning and zooming.
+        (-[WKContentView _resetPanningPreventionFlags]):
+        (-[WKContentView _didEndScrollingOrZooming]):
+
 2019-01-28  Antti Koivisto  <antti@apple.com>
 
         WebUserContentController::removeUserScriptMessageHandlerInternal may deref and delete itself
 2019-01-28  Antti Koivisto  <antti@apple.com>
 
         WebUserContentController::removeUserScriptMessageHandlerInternal may deref and delete itself
index 3b0529a..54d199e 100644 (file)
@@ -329,10 +329,40 @@ Optional<DOMCacheEngine::Record> ArgumentCoder<DOMCacheEngine::Record>::decode(D
     return {{ WTFMove(identifier), WTFMove(updateResponseCounter), WTFMove(requestHeadersGuard), WTFMove(request), WTFMove(options.value()), WTFMove(referrer), WTFMove(responseHeadersGuard), WTFMove(response), WTFMove(responseBody), responseBodySize }};
 }
 
     return {{ WTFMove(identifier), WTFMove(updateResponseCounter), WTFMove(requestHeadersGuard), WTFMove(request), WTFMove(options.value()), WTFMove(referrer), WTFMove(responseHeadersGuard), WTFMove(response), WTFMove(responseBody), responseBodySize }};
 }
 
+#if ENABLE(POINTER_EVENTS)
+void ArgumentCoder<TouchActionData>::encode(Encoder& encoder, const TouchActionData& touchActionData)
+{
+    encoder << touchActionData.touchActions << touchActionData.scrollingNodeID << touchActionData.region;
+}
+
+Optional<TouchActionData> ArgumentCoder<TouchActionData>::decode(Decoder& decoder)
+{
+    Optional<OptionSet<TouchAction>> touchActions;
+    decoder >> touchActions;
+    if (!touchActions)
+        return WTF::nullopt;
+
+    Optional<ScrollingNodeID> scrollingNodeID;
+    decoder >> scrollingNodeID;
+    if (!scrollingNodeID)
+        return WTF::nullopt;
+
+    Optional<Region> region;
+    decoder >> region;
+    if (!region)
+        return WTF::nullopt;
+
+    return {{ WTFMove(*touchActions), WTFMove(*scrollingNodeID), WTFMove(*region) }};
+}
+#endif
+
 void ArgumentCoder<EventTrackingRegions>::encode(Encoder& encoder, const EventTrackingRegions& eventTrackingRegions)
 {
     encoder << eventTrackingRegions.asynchronousDispatchRegion;
     encoder << eventTrackingRegions.eventSpecificSynchronousDispatchRegions;
 void ArgumentCoder<EventTrackingRegions>::encode(Encoder& encoder, const EventTrackingRegions& eventTrackingRegions)
 {
     encoder << eventTrackingRegions.asynchronousDispatchRegion;
     encoder << eventTrackingRegions.eventSpecificSynchronousDispatchRegions;
+#if ENABLE(POINTER_EVENTS)
+    encoder << eventTrackingRegions.touchActionData;
+#endif
 }
 
 bool ArgumentCoder<EventTrackingRegions>::decode(Decoder& decoder, EventTrackingRegions& eventTrackingRegions)
 }
 
 bool ArgumentCoder<EventTrackingRegions>::decode(Decoder& decoder, EventTrackingRegions& eventTrackingRegions)
@@ -343,8 +373,16 @@ bool ArgumentCoder<EventTrackingRegions>::decode(Decoder& decoder, EventTracking
     HashMap<String, Region> eventSpecificSynchronousDispatchRegions;
     if (!decoder.decode(eventSpecificSynchronousDispatchRegions))
         return false;
     HashMap<String, Region> eventSpecificSynchronousDispatchRegions;
     if (!decoder.decode(eventSpecificSynchronousDispatchRegions))
         return false;
+#if ENABLE(POINTER_EVENTS)
+    Vector<TouchActionData> touchActionData;
+    if (!decoder.decode(touchActionData))
+        return false;
+#endif
     eventTrackingRegions.asynchronousDispatchRegion = WTFMove(asynchronousDispatchRegion);
     eventTrackingRegions.eventSpecificSynchronousDispatchRegions = WTFMove(eventSpecificSynchronousDispatchRegions);
     eventTrackingRegions.asynchronousDispatchRegion = WTFMove(asynchronousDispatchRegion);
     eventTrackingRegions.eventSpecificSynchronousDispatchRegions = WTFMove(eventSpecificSynchronousDispatchRegions);
+#if ENABLE(POINTER_EVENTS)
+    eventTrackingRegions.touchActionData = WTFMove(touchActionData);
+#endif
     return true;
 }
 
     return true;
 }
 
@@ -915,6 +953,15 @@ bool ArgumentCoder<Region>::decode(Decoder& decoder, Region& region)
     return true;
 }
 
     return true;
 }
 
+Optional<Region> ArgumentCoder<Region>::decode(Decoder& decoder)
+{
+    Region region;
+    if (!decode(decoder, region))
+        return WTF::nullopt;
+
+    return region;
+}
+
 void ArgumentCoder<Length>::encode(Encoder& encoder, const Length& length)
 {
     SimpleArgumentCoder<Length>::encode(encoder, length);
 void ArgumentCoder<Length>::encode(Encoder& encoder, const Length& length)
 {
     SimpleArgumentCoder<Length>::encode(encoder, length);
index 3ae7e47..2ef5e43 100644 (file)
@@ -121,6 +121,9 @@ struct ResourceLoadStatistics;
 struct ScrollableAreaParameters;
 struct TextCheckingResult;
 struct TextIndicatorData;
 struct ScrollableAreaParameters;
 struct TextCheckingResult;
 struct TextIndicatorData;
+#if ENABLE(POINTER_EVENTS)
+struct TouchActionData;
+#endif
 struct ViewportAttributes;
 struct WindowFeatures;
     
 struct ViewportAttributes;
 struct WindowFeatures;
     
@@ -199,6 +202,13 @@ template<> struct ArgumentCoder<WebCore::DOMCacheEngine::Record> {
     static Optional<WebCore::DOMCacheEngine::Record> decode(Decoder&);
 };
 
     static Optional<WebCore::DOMCacheEngine::Record> decode(Decoder&);
 };
 
+#if ENABLE(POINTER_EVENTS)
+template<> struct ArgumentCoder<WebCore::TouchActionData> {
+    static void encode(Encoder&, const WebCore::TouchActionData&);
+    static Optional<WebCore::TouchActionData> decode(Decoder&);
+};
+#endif
+
 template<> struct ArgumentCoder<WebCore::EventTrackingRegions> {
     static void encode(Encoder&, const WebCore::EventTrackingRegions&);
     static bool decode(Decoder&, WebCore::EventTrackingRegions&);
 template<> struct ArgumentCoder<WebCore::EventTrackingRegions> {
     static void encode(Encoder&, const WebCore::EventTrackingRegions&);
     static bool decode(Decoder&, WebCore::EventTrackingRegions&);
@@ -316,6 +326,7 @@ template<> struct ArgumentCoder<WebCore::Path> {
 template<> struct ArgumentCoder<WebCore::Region> {
     static void encode(Encoder&, const WebCore::Region&);
     static bool decode(Decoder&, WebCore::Region&);
 template<> struct ArgumentCoder<WebCore::Region> {
     static void encode(Encoder&, const WebCore::Region&);
     static bool decode(Decoder&, WebCore::Region&);
+    static Optional<WebCore::Region> decode(Decoder&);
 };
 
 template<> struct ArgumentCoder<WebCore::Length> {
 };
 
 template<> struct ArgumentCoder<WebCore::Length> {
index 0a30480..b987c99 100644 (file)
@@ -2583,6 +2583,14 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     // zooming. We'll animate to the right place once the zoom finishes.
     if ([scrollView isZooming])
         *targetContentOffset = [scrollView contentOffset];
     // zooming. We'll animate to the right place once the zoom finishes.
     if ([scrollView isZooming])
         *targetContentOffset = [scrollView contentOffset];
+#if ENABLE(POINTER_EVENTS)
+    else {
+        if ([_contentView preventsPanningInXAxis])
+            targetContentOffset->x = scrollView.contentOffset.x;
+        if ([_contentView preventsPanningInYAxis])
+            targetContentOffset->y = scrollView.contentOffset.y;
+    }
+#endif
 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
 #if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
@@ -2620,6 +2628,20 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     [self _didFinishScrolling];
 }
 
     [self _didFinishScrolling];
 }
 
+- (CGPoint)_scrollView:(UIScrollView *)scrollView adjustedOffsetForOffset:(CGPoint)offset translation:(CGPoint)translation startPoint:(CGPoint)start locationInView:(CGPoint)locationInView horizontalVelocity:(inout double *)hv verticalVelocity:(inout double *)vv
+{
+    if (![_contentView preventsPanningInXAxis] && ![_contentView preventsPanningInYAxis])
+        return offset;
+
+    CGPoint adjustedContentOffset = CGPointMake(offset.x, offset.y);
+    if ([_contentView preventsPanningInXAxis])
+        adjustedContentOffset.x = start.x;
+    if ([_contentView preventsPanningInYAxis])
+        adjustedContentOffset.y = start.y;
+
+    return adjustedContentOffset;
+}
+
 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
 {
     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidScroll:)])
 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
 {
     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidScroll:)])
index a959c93..f964fc9 100644 (file)
@@ -206,6 +206,33 @@ String RemoteScrollingCoordinatorProxy::scrollingTreeAsText() const
     return emptyString();
 }
 
     return emptyString();
 }
 
+#if ENABLE(POINTER_EVENTS)
+Optional<TouchActionData> RemoteScrollingCoordinatorProxy::touchActionDataAtPoint(const IntPoint p) const
+{
+    return m_scrollingTree->touchActionDataAtPoint(p);
+}
+
+Optional<TouchActionData> RemoteScrollingCoordinatorProxy::touchActionDataForScrollNodeID(ScrollingNodeID scrollingNodeID) const
+{
+    for (auto& touchActionData : m_touchActionDataByTouchIdentifier.values()) {
+        if (touchActionData.scrollingNodeID == scrollingNodeID)
+            return touchActionData;
+    }
+    return WTF::nullopt;
+}
+
+void RemoteScrollingCoordinatorProxy::setTouchDataForTouchIdentifier(TouchActionData touchActionData, unsigned touchIdentifier)
+{
+    m_touchActionDataByTouchIdentifier.set(touchIdentifier, touchActionData);
+}
+
+void RemoteScrollingCoordinatorProxy::clearTouchDataForTouchIdentifier(unsigned touchIdentifier)
+{
+    m_touchActionDataByTouchIdentifier.remove(touchIdentifier);
+}
+
+#endif
+
 } // namespace WebKit
 
 #endif // ENABLE(ASYNC_SCROLLING)
 } // namespace WebKit
 
 #endif // ENABLE(ASYNC_SCROLLING)
index b32d79f..df07156 100644 (file)
@@ -98,6 +98,13 @@ public:
 
     String scrollingTreeAsText() const;
 
 
     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);
+#endif
+
 private:
     void connectStateNodeLayers(WebCore::ScrollingStateTree&, const RemoteLayerTreeHost&);
 #if ENABLE(CSS_SCROLL_SNAP)
 private:
     void connectStateNodeLayers(WebCore::ScrollingStateTree&, const RemoteLayerTreeHost&);
 #if ENABLE(CSS_SCROLL_SNAP)
@@ -107,6 +114,9 @@ private:
 
     WebPageProxy& m_webPageProxy;
     RefPtr<RemoteScrollingTree> m_scrollingTree;
 
     WebPageProxy& m_webPageProxy;
     RefPtr<RemoteScrollingTree> m_scrollingTree;
+#if ENABLE(POINTER_EVENTS)
+    HashMap<unsigned, WebCore::TouchActionData> m_touchActionDataByTouchIdentifier;
+#endif
     RequestedScrollInfo* m_requestedScrollInfo;
 #if ENABLE(CSS_SCROLL_SNAP)
     unsigned m_currentHorizontalSnapPointIndex { 0 };
     RequestedScrollInfo* m_requestedScrollInfo;
 #if ENABLE(CSS_SCROLL_SNAP)
     unsigned m_currentHorizontalSnapPointIndex { 0 };
index 397efed..a34c80c 100644 (file)
@@ -65,6 +65,9 @@ public:
     WebCore::FloatPoint scrollPosition() const;
     void setScrollLayerPosition(const WebCore::FloatPoint&);
     void updateChildNodesAfterScroll(const WebCore::FloatPoint& scrollPosition);
     WebCore::FloatPoint scrollPosition() const;
     void setScrollLayerPosition(const WebCore::FloatPoint&);
     void updateChildNodesAfterScroll(const WebCore::FloatPoint& scrollPosition);
+#if ENABLE(POINTER_EVENTS)
+    Optional<TouchActionData> touchActionData() const;
+#endif
 
 private:
     RetainPtr<CALayer> m_scrollLayer;
 
 private:
     RetainPtr<CALayer> m_scrollLayer;
index 4cc7e1a..dc55da9 100644 (file)
     _scrollingTreeNodeDelegate->scrollWillStart();
 }
 
     _scrollingTreeNodeDelegate->scrollWillStart();
 }
 
-#if ENABLE(CSS_SCROLL_SNAP)
 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
 {
 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
 {
+#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;
+                }
+            }
+        }
+    }
+#endif
+
+#if ENABLE(CSS_SCROLL_SNAP)
     CGFloat horizontalTarget = targetContentOffset->x;
     CGFloat verticalTarget = targetContentOffset->y;
 
     CGFloat horizontalTarget = targetContentOffset->x;
     CGFloat verticalTarget = targetContentOffset->y;
 
         || originalVerticalSnapPosition != _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex()) {
         _scrollingTreeNodeDelegate->currentSnapPointIndicesDidChange(_scrollingTreeNodeDelegate->scrollingNode().currentHorizontalSnapPointIndex(), _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex());
     }
         || originalVerticalSnapPosition != _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex()) {
         _scrollingTreeNodeDelegate->currentSnapPointIndicesDidChange(_scrollingTreeNodeDelegate->scrollingNode().currentHorizontalSnapPointIndex(), _scrollingTreeNodeDelegate->scrollingNode().currentVerticalSnapPointIndex());
     }
-}
 #endif
 #endif
+}
 
 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)willDecelerate
 {
 
 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)willDecelerate
 {
     }
 }
 
     }
 }
 
+#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)
+        return offset;
+
+    auto touchActions = touchActionData->touchActions;
+    if (touchActions == WebCore::TouchAction::Auto || touchActions == WebCore::TouchAction::Manipulation)
+        return offset;
+
+    CGPoint adjustedContentOffset = CGPointMake(offset.x, offset.y);
+
+    if (!touchActions.contains(WebCore::TouchAction::PanX))
+        adjustedContentOffset.x = start.x;
+    if (!touchActions.contains(WebCore::TouchAction::PanY))
+        adjustedContentOffset.y = start.y;
+
+    return adjustedContentOffset;
+}
+#endif
+
 @end
 
 namespace WebKit {
 @end
 
 namespace WebKit {
@@ -287,6 +329,13 @@ void ScrollingTreeScrollingNodeDelegateIOS::currentSnapPointIndicesDidChange(uns
     scrollingTree().currentSnapPointIndicesDidChange(scrollingNode().scrollingNodeID(), horizontal, vertical);
 }
 
     scrollingTree().currentSnapPointIndicesDidChange(scrollingNode().scrollingNodeID(), horizontal, vertical);
 }
 
+#if ENABLE(POINTER_EVENTS)
+Optional<TouchActionData> ScrollingTreeScrollingNodeDelegateIOS::touchActionData() const
+{
+    return downcast<RemoteScrollingTree>(scrollingTree()).scrollingCoordinatorProxy().touchActionDataForScrollNodeID(scrollingNode().scrollingNodeID());
+}
+#endif
+
 } // namespace WebKit
 
 #endif // PLATFORM(IOS_FAMILY) && ENABLE(ASYNC_SCROLLING)
 } // namespace WebKit
 
 #endif // PLATFORM(IOS_FAMILY) && ENABLE(ASYNC_SCROLLING)
index 6c8f85c..19b12f4 100644 (file)
@@ -678,6 +678,7 @@ public:
     void startAutoscrollAtPosition(const WebCore::FloatPoint& positionInWindow);
     void cancelAutoscroll();
     void hardwareKeyboardAvailabilityChanged();
     void startAutoscrollAtPosition(const WebCore::FloatPoint& positionInWindow);
     void cancelAutoscroll();
     void hardwareKeyboardAvailabilityChanged();
+    bool isScrollingOrZooming() const { return m_isScrollingOrZooming; }
 #if ENABLE(DATA_INTERACTION)
     void didHandleDragStartRequest(bool started);
     void didHandleAdditionalDragItemsRequest(bool added);
 #if ENABLE(DATA_INTERACTION)
     void didHandleDragStartRequest(bool started);
     void didHandleAdditionalDragItemsRequest(bool added);
index 5ea7776..baca3fa 100644 (file)
@@ -196,6 +196,10 @@ struct WKAutoCorrectionData {
     RetainPtr<UIWebTouchEventsGestureRecognizer> _touchEventGestureRecognizer;
 
     BOOL _canSendTouchEventsAsynchronously;
     RetainPtr<UIWebTouchEventsGestureRecognizer> _touchEventGestureRecognizer;
 
     BOOL _canSendTouchEventsAsynchronously;
+#if ENABLE(POINTER_EVENTS)
+    BOOL _preventsPanningInXAxis;
+    BOOL _preventsPanningInYAxis;
+#endif
 
     RetainPtr<WKSyntheticClickTapGestureRecognizer> _singleTapGestureRecognizer;
     RetainPtr<_UIWebHighlightLongPressGestureRecognizer> _highlightLongPressGestureRecognizer;
 
     RetainPtr<WKSyntheticClickTapGestureRecognizer> _singleTapGestureRecognizer;
     RetainPtr<_UIWebHighlightLongPressGestureRecognizer> _highlightLongPressGestureRecognizer;
@@ -349,6 +353,10 @@ struct WKAutoCorrectionData {
 @property (nonatomic, readonly) const WebKit::FocusedElementInformation& focusedElementInformation;
 @property (nonatomic, readonly) UIWebFormAccessory *formAccessoryView;
 @property (nonatomic, readonly) UITextInputAssistantItem *inputAssistantItemForWebView;
 @property (nonatomic, readonly) const WebKit::FocusedElementInformation& focusedElementInformation;
 @property (nonatomic, readonly) UIWebFormAccessory *formAccessoryView;
 @property (nonatomic, readonly) UITextInputAssistantItem *inputAssistantItemForWebView;
+#if ENABLE(POINTER_EVENTS)
+@property (nonatomic, readonly) BOOL preventsPanningInXAxis;
+@property (nonatomic, readonly) BOOL preventsPanningInYAxis;
+#endif
 
 #if ENABLE(DATALIST_ELEMENT)
 @property (nonatomic, strong) UIView <WKFormControl> *dataListTextSuggestionsInputView;
 
 #if ENABLE(DATALIST_ELEMENT)
 @property (nonatomic, strong) UIView <WKFormControl> *dataListTextSuggestionsInputView;
index 8ca9fc2..86c3d43 100644 (file)
 #import <WebKitAdditions/WKPlatformFileUploadPanel.mm>
 #endif
 
 #import <WebKitAdditions/WKPlatformFileUploadPanel.mm>
 #endif
 
+#if ENABLE(POINTER_EVENTS)
+#import "RemoteScrollingCoordinatorProxy.h"
+#import <WebCore/TouchAction.h>
+#endif
+
 #if PLATFORM(WATCHOS)
 
 @interface WKContentView (WatchSupport) <WKFocusedFormControlViewDelegate, WKSelectMenuListViewControllerDelegate, WKTextInputListViewControllerDelegate>
 #if PLATFORM(WATCHOS)
 
 @interface WKContentView (WatchSupport) <WKFocusedFormControlViewDelegate, WKSelectMenuListViewControllerDelegate, WKTextInputListViewControllerDelegate>
@@ -608,6 +613,18 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     return (focusedElementInformation.elementType != WebKit::InputType::None);
 }
 
     return (focusedElementInformation.elementType != WebKit::InputType::None);
 }
 
+#if ENABLE(POINTER_EVENTS)
+- (BOOL)preventsPanningInXAxis
+{
+    return _preventsPanningInXAxis;
+}
+
+- (BOOL)preventsPanningInYAxis
+{
+    return _preventsPanningInYAxis;
+}
+#endif
+
 - (WKFormInputSession *)_formInputSession
 {
     return _formInputSession.get();
 - (WKFormInputSession *)_formInputSession
 {
     return _formInputSession.get();
@@ -854,6 +871,10 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     _hasSetUpInteractions = NO;
     _suppressSelectionAssistantReasons = { };
     _isZoomingToRevealFocusedElement = NO;
     _hasSetUpInteractions = NO;
     _suppressSelectionAssistantReasons = { };
     _isZoomingToRevealFocusedElement = NO;
+
+#if ENABLE(POINTER_EVENTS)
+    [self _resetPanningPreventionFlags];
+#endif
 }
 
 - (void)_removeDefaultGestureRecognizers
 }
 
 - (void)_removeDefaultGestureRecognizers
@@ -1121,16 +1142,63 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     WebKit::NativeWebTouchEvent nativeWebTouchEvent(lastTouchEvent);
     nativeWebTouchEvent.setCanPreventNativeGestures(!_canSendTouchEventsAsynchronously || [gestureRecognizer isDefaultPrevented]);
 
     WebKit::NativeWebTouchEvent nativeWebTouchEvent(lastTouchEvent);
     nativeWebTouchEvent.setCanPreventNativeGestures(!_canSendTouchEventsAsynchronously || [gestureRecognizer isDefaultPrevented]);
 
+#if ENABLE(POINTER_EVENTS)
+    [self _handleTouchActionsForTouchEvent:nativeWebTouchEvent];
+#endif
+
     if (_canSendTouchEventsAsynchronously)
         _page->handleTouchEventAsynchronously(nativeWebTouchEvent);
     else
         _page->handleTouchEventSynchronously(nativeWebTouchEvent);
 
     if (_canSendTouchEventsAsynchronously)
         _page->handleTouchEventAsynchronously(nativeWebTouchEvent);
     else
         _page->handleTouchEventSynchronously(nativeWebTouchEvent);
 
-    if (nativeWebTouchEvent.allTouchPointsAreReleased())
+    if (nativeWebTouchEvent.allTouchPointsAreReleased()) {
         _canSendTouchEventsAsynchronously = NO;
         _canSendTouchEventsAsynchronously = NO;
+
+#if ENABLE(POINTER_EVENTS)
+        if (!_page->isScrollingOrZooming())
+            [self _resetPanningPreventionFlags];
 #endif
 #endif
+    }
+#endif
+}
+
+#if ENABLE(POINTER_EVENTS)
+- (void)_handleTouchActionsForTouchEvent:(const WebKit::NativeWebTouchEvent&)touchEvent
+{
+    auto* scrollingCoordinator = _page->scrollingCoordinatorProxy();
+    if (!scrollingCoordinator)
+        return;
+
+    for (const auto& touchPoint : touchEvent.touchPoints()) {
+        auto phase = touchPoint.phase();
+        if (phase == WebKit::WebPlatformTouchPoint::TouchPressed) {
+            auto touchActionData = scrollingCoordinator->touchActionDataAtPoint(touchPoint.location());
+            if (!touchActionData)
+                continue;
+            if (touchActionData->touchActions == WebCore::TouchAction::None)
+                [_touchEventGestureRecognizer setDefaultPrevented:YES];
+            else if (!touchActionData->touchActions.contains(WebCore::TouchAction::Manipulation)) {
+                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);
+                }
+            }
+        } else if (phase == WebKit::WebPlatformTouchPoint::TouchReleased || phase == WebKit::WebPlatformTouchPoint::TouchCancelled)
+            scrollingCoordinator->clearTouchDataForTouchIdentifier(touchPoint.identifier());
+    }
 }
 
 }
 
+- (void)_resetPanningPreventionFlags
+{
+    _preventsPanningInXAxis = NO;
+    _preventsPanningInYAxis = NO;
+}
+#endif
+
 - (void)_inspectorNodeSearchRecognized:(UIGestureRecognizer *)gestureRecognizer
 {
     ASSERT(_inspectorNodeSearchEnabled);
 - (void)_inspectorNodeSearchRecognized:(UIGestureRecognizer *)gestureRecognizer
 {
     ASSERT(_inspectorNodeSearchEnabled);
@@ -2192,6 +2260,10 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
     }
     _page->setIsScrollingOrZooming(false);
 
     }
     _page->setIsScrollingOrZooming(false);
 
+#if ENABLE(POINTER_EVENTS)
+    [self _resetPanningPreventionFlags];
+#endif
+
 #if PLATFORM(WATCHOS)
     [_focusedFormControlView engageFocusedFormControlNavigation];
 #endif
 #if PLATFORM(WATCHOS)
     [_focusedFormControlView engageFocusedFormControlNavigation];
 #endif