Bug 1785754 - Return HandledByContent (eagerly) for touches prevented by touch-action. r=dlrobertson,hiro,geckoview-reviewers,m_kato

Note, changes to the delayed-result codepath are not required because
touched prevented by touch-action always get an eager HandledByContent
result.

Differential Revision: https://phabricator.services.mozilla.com/D164586
This commit is contained in:
Botond Ballo 2022-12-19 05:16:55 +00:00
parent 7b3b788267
commit abcee8d2c9
4 changed files with 100 additions and 22 deletions

View File

@ -74,11 +74,28 @@ void APZEventResult::SetStatusForTouchEvent(
const InputBlockState& aBlock, TargetConfirmationFlags aFlags,
PointerEventsConsumableFlags aConsumableFlags,
const AsyncPanZoomController* aTarget) {
// Note, we need to continue setting mStatus to eIgnore in the {mHasRoom=true,
// mAllowedByTouchAction=false} case because this is the behaviour expected by
// APZEventState::ProcessTouchEvent() when it determines when to send a
// `pointercancel` event. TODO: Use something more descriptive than
// nsEventStatus for this purpose.
bool consumable = aConsumableFlags.IsConsumable();
mStatus =
consumable ? nsEventStatus_eConsumeDoDefault : nsEventStatus_eIgnore;
if (mHandledResult && !aFlags.mDispatchToContent && !consumable) {
// Set result to Unhandled if we set the status to eIgnore, unless it
// If the touch event's effect is disallowed by touch-action, treat it as if
// a touch event listener had preventDefault()-ed it (i.e. return
// HandledByContent, except we can do it eagerly rather than having to wait
// for the listener to run).
if (!aConsumableFlags.mAllowedByTouchAction) {
mHandledResult =
Some(APZHandledResult{APZHandledPlace::HandledByContent, aTarget});
return;
}
if (mHandledResult && !aFlags.mDispatchToContent &&
!aConsumableFlags.mHasRoom) {
// Set result to Unhandled if we have no room to scroll, unless it
// was HandledByContent because we're over a dispatch-to-content region,
// in which case it should remain HandledByContent.
mHandledResult->mPlace = APZHandledPlace::Unhandled;

View File

@ -22,6 +22,26 @@ body {
</style>
<body>
<div style="width: 100%; height: 50vh; touch-action: auto;"></div>
<div style="width: 100%; height: 50vh; touch-action: none;"></div>
<script>
const searchParams = new URLSearchParams(location.search);
let div = document.querySelector('div');
if (searchParams.has("subframe")) {
const scrolledContents = document.createElement("div");
scrolledContents.style.height = "100%";
div.appendChild(scrolledContents);
div.style.overflow = "auto";
div = scrolledContents;
}
if (searchParams.has("scrollable")) {
// Scrollable for dynamic toolbar purposes.
div.style.height = "100vh";
}
div.style.touchAction = searchParams.get("touch-action");
if (searchParams.has("event")) {
div.addEventListener("touchstart", e => {});
}
</script>
</body>
</html>

View File

@ -85,25 +85,66 @@ class InputResultDetailTest : BaseSessionTest() {
@Test
fun testTouchAction() {
sessionRule.display?.run { setDynamicToolbarMaxHeight(20) }
setupDocument(TOUCH_ACTION_HTML_PATH)
var value = sessionRule.waitForResult(sendDownEvent(50f, 20f))
assertResultDetail(
"`touch-action: auto`",
value,
PanZoomController.INPUT_RESULT_HANDLED,
PanZoomController.SCROLLABLE_FLAG_BOTTOM,
(PanZoomController.OVERSCROLL_FLAG_HORIZONTAL or PanZoomController.OVERSCROLL_FLAG_VERTICAL)
)
for (subframe in arrayOf(true, false)) {
for (scrollable in arrayOf(true, false)) {
for (event in arrayOf(true, false)) {
for (touchAction in arrayOf("auto", "none", "pan-x", "pan-y")) {
var url = TOUCH_ACTION_HTML_PATH + "?"
if (subframe) {
url += "subframe&"
}
if (scrollable) {
url += "scrollable&"
}
if (event) {
url += "event&"
}
url += ("touch-action=" + touchAction)
value = sessionRule.waitForResult(sendDownEvent(50f, 75f))
assertResultDetail(
"`touch-action: none`",
value,
PanZoomController.INPUT_RESULT_UNHANDLED,
PanZoomController.SCROLLABLE_FLAG_NONE,
PanZoomController.OVERSCROLL_FLAG_NONE
)
setupDocument(url)
// Since sendDownEvent() just sends a touch-down, APZ doesn't
// yet know the direction, hence it allows scrolling in both
// the pan-x and pan-y cases.
var expectedPlace = if (touchAction == "none" || (subframe && scrollable)) {
PanZoomController.INPUT_RESULT_HANDLED_CONTENT
} else if (scrollable) {
PanZoomController.INPUT_RESULT_HANDLED
} else {
PanZoomController.INPUT_RESULT_UNHANDLED
}
var expectedScrollableDirections = if (scrollable) {
PanZoomController.SCROLLABLE_FLAG_BOTTOM
} else {
PanZoomController.SCROLLABLE_FLAG_NONE
}
// FIXME: There are a couple of bugs here:
// 1. In the case where touch-action allows the scrolling, the
// overscroll directions shouldn't depend on the presence of
// an event handler, but they do.
// 2. In the case where touch-action doesn't allow the scrolling,
// the overscroll directions should probably be NONE.
var expectedOverscrollDirections = if (touchAction != "none" && !scrollable && event) {
PanZoomController.OVERSCROLL_FLAG_NONE
} else {
(PanZoomController.OVERSCROLL_FLAG_HORIZONTAL or PanZoomController.OVERSCROLL_FLAG_VERTICAL)
}
var value = sessionRule.waitForResult(sendDownEvent(50f, 20f))
assertResultDetail(
"`subframe=$subframe, scrollable=$scrollable, event=$event, touch-action=$touchAction`",
value,
expectedPlace,
expectedScrollableDirections,
expectedOverscrollDirections
)
}
}
}
}
}
@WithDisplay(width = 100, height = 100)

View File

@ -523,9 +523,9 @@ class PanZoomControllerTest : BaseSessionTest() {
setupDocument(TOUCH_ACTION_WHEEL_LISTENER_HTML_PATH)
var value = sessionRule.waitForResult(sendDownEvent(50f, 50f))
assertThat(
"The input result should be UNHANDLED",
"The input result should be HANDLED_CONTENT",
value,
equalTo(PanZoomController.INPUT_RESULT_UNHANDLED)
equalTo(PanZoomController.INPUT_RESULT_HANDLED_CONTENT)
)
}