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)
commit405d4cd9d0653f712c07b460a21355635fa8a5f3
tree8340213157ad2ce9407348b2cf840ea65b77d4ad
parent07eec90790ce58fa84ed4749e8b53301f9306a9a
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.

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