Bug 1746126 - Allow controlling zoom-to-focused-input behaviour using touch-action. r=botond

Rename AllowedTouchBehavior::DOUBLE_TAP_ZOOM to ANIMATING_ZOOM, and
CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled to
eTouchActionAnimatingZoomDisabled while at it.

Differential Revision: https://phabricator.services.mozilla.com/D167522
This commit is contained in:
Razvan Cojocaru 2023-01-26 01:07:58 +00:00
parent 5ad80fae46
commit d8e9d2a141
12 changed files with 127 additions and 42 deletions

View File

@ -37,6 +37,7 @@
#include "nsIFrame.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/PCompositorBridgeTypes.h"
#include "mozilla/layers/TouchActionHelper.h"
#include "mozilla/media/MediaUtils.h"
#include "nsQueryObject.h"
#include "CubebDeviceEnumerator.h"
@ -3096,10 +3097,15 @@ nsDOMWindowUtils::ZoomToFocusedInput() {
return NS_OK;
}
TouchBehaviorFlags tbf =
layers::TouchActionHelper::GetAllowedTouchBehaviorForFrame(
element->GetPrimaryFrame());
uint32_t flags = layers::DISABLE_ZOOM_OUT;
if (!Preferences::GetBool("formhelper.autozoom") ||
Preferences::GetBool("formhelper.autozoom.force-disable.test-only",
/* aFallback = */ false)) {
/* aFallback = */ false) ||
!(tbf & AllowedTouchBehavior::ANIMATING_ZOOM)) {
flags |= layers::PAN_INTO_VIEW_ONLY;
} else {
flags |= layers::ONLY_ZOOM_TO_DEFAULT_SCALE;

View File

@ -28,7 +28,7 @@ enum AllowedTouchBehavior {
VERTICAL_PAN = 1 << 0,
HORIZONTAL_PAN = 1 << 1,
PINCH_ZOOM = 1 << 2,
DOUBLE_TAP_ZOOM = 1 << 3,
ANIMATING_ZOOM = 1 << 3,
UNKNOWN = 1 << 4
};

View File

@ -1857,7 +1857,7 @@ static TouchBehaviorFlags ConvertToTouchBehavior(
result = AllowedTouchBehavior::VERTICAL_PAN |
AllowedTouchBehavior::HORIZONTAL_PAN |
AllowedTouchBehavior::PINCH_ZOOM |
AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
AllowedTouchBehavior::ANIMATING_ZOOM;
if (info.contains(CompositorHitTestFlags::eTouchActionPanXDisabled)) {
result &= ~AllowedTouchBehavior::HORIZONTAL_PAN;
}
@ -1868,8 +1868,8 @@ static TouchBehaviorFlags ConvertToTouchBehavior(
result &= ~AllowedTouchBehavior::PINCH_ZOOM;
}
if (info.contains(
CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled)) {
result &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled)) {
result &= ~AllowedTouchBehavior::ANIMATING_ZOOM;
}
}
return result;

View File

@ -735,7 +735,7 @@ bool TouchBlockState::TouchActionAllowsPinchZoom() const {
bool TouchBlockState::TouchActionAllowsDoubleTapZoom() const {
for (auto& behavior : mAllowedTouchBehaviors) {
if (!(behavior & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
if (!(behavior & AllowedTouchBehavior::ANIMATING_ZOOM)) {
return false;
}
}

View File

@ -98,7 +98,7 @@ class ScopedGfxSetting {
static inline constexpr auto kDefaultTouchBehavior =
AllowedTouchBehavior::VERTICAL_PAN | AllowedTouchBehavior::HORIZONTAL_PAN |
AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::ANIMATING_ZOOM;
#define FRESH_PREF_VAR_PASTE(id, line) id##line
#define FRESH_PREF_VAR_EXPAND(id, line) FRESH_PREF_VAR_PASTE(id, line)

View File

@ -36,7 +36,7 @@ void SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN |
mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN |
mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM |
mozilla::layers::AllowedTouchBehavior::DOUBLE_TAP_ZOOM);
mozilla::layers::AllowedTouchBehavior::ANIMATING_ZOOM);
}
aTarget->SetAllowedTouchBehavior(aInputBlockId, defaultBehaviors);
}

View File

@ -0,0 +1,67 @@
<!DOCTYPE>
<html>
<head>
<title>Checking zoomToFocusedInput zooms if touch-action allows it</title>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=0.5 minimum-scale=0.5, maximum-scale=1" />
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
</head>
<style type="text/css">
.touch-none {
touch-action: none;
}
.touch-auto {
touch-action: auto;
}
</style>
<body>
<div class="touch-none">
<input id="input1" type="text" style="border: 5px solid black">
</div>
<br>
<div class="touch-auto">
<input id="input2" type="text" style="border: 5px solid black">
</div>
<script type="application/javascript">
async function test() {
let utils = SpecialPowers.getDOMWindowUtils(window);
let resolution = await getResolution();
ok(resolution > 0,
"The initial_resolution is " + resolution + ", which is some sane value");
document.getElementById('input1').focus();
await waitToClearOutAnyPotentialScrolls(window);
await promiseApzFlushedRepaints();
let prev_resolution = resolution;
resolution = await getResolution();
ok(resolution == prev_resolution, "focusing input1 did not change resolution " + resolution);
let transformEndPromise = promiseTransformEnd();
utils.zoomToFocusedInput();
await waitToClearOutAnyPotentialScrolls(window);
await transformEndPromise;
await promiseApzFlushedRepaints();
resolution = await getResolution();
ok(resolution == prev_resolution, "zoomToFocusedInput input1 did not change resolution " + resolution);
document.getElementById('input2').focus();
await waitToClearOutAnyPotentialScrolls(window);
await promiseApzFlushedRepaints();
resolution = await getResolution();
ok(resolution == prev_resolution, "focusing input2 did not change resolution " + resolution);
transformEndPromise = promiseTransformEnd();
utils.zoomToFocusedInput();
await waitToClearOutAnyPotentialScrolls(window);
await transformEndPromise;
await promiseApzFlushedRepaints();
resolution = await getResolution();
ok(resolution != prev_resolution, "zoomToFocusedInput input2 changed resolution " + resolution);
}
waitUntilApzStable().then(test).then(subtestDone, subtestFailed);
</script>
</body>
</html>

View File

@ -24,10 +24,13 @@ var subtests = [
let platform = getPlatform();
if (platform == "android") {
subtests.push(
{"file": "helper_zoomToFocusedInput_nozoom.html"}
{"file": "helper_zoomToFocusedInput_nozoom.html"}
);
subtests.push(
{"file": "helper_zoomToFocusedInput_zoom.html"}
{"file": "helper_zoomToFocusedInput_zoom.html"}
);
subtests.push(
{"file": "helper_zoomToFocusedInput_touch-action.html"}
);
}

View File

@ -21,7 +21,7 @@ static void UpdateAllowedBehavior(StyleTouchAction aTouchActionValue,
TouchBehaviorFlags& aOutBehavior) {
if (aTouchActionValue != StyleTouchAction::AUTO) {
// Double-tap-zooming need property value AUTO
aOutBehavior &= ~AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
aOutBehavior &= ~AllowedTouchBehavior::ANIMATING_ZOOM;
if (aTouchActionValue != StyleTouchAction::MANIPULATION &&
!(aTouchActionValue & StyleTouchAction::PINCH_ZOOM)) {
// Pinch-zooming needs value AUTO or MANIPULATION, or the PINCH_ZOOM bit
@ -52,20 +52,46 @@ static void UpdateAllowedBehavior(StyleTouchAction aTouchActionValue,
static TouchBehaviorFlags GetAllowedTouchBehaviorForPoint(
nsIWidget* aWidget, RelativeTo aRootFrame,
const LayoutDeviceIntPoint& aPoint) {
TouchBehaviorFlags behavior = AllowedTouchBehavior::VERTICAL_PAN |
AllowedTouchBehavior::HORIZONTAL_PAN |
AllowedTouchBehavior::PINCH_ZOOM |
AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
nsPoint relativePoint =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aPoint, aRootFrame);
nsIFrame* target = nsLayoutUtils::GetFrameForPoint(aRootFrame, relativePoint);
if (!target) {
return TouchActionHelper::GetAllowedTouchBehaviorForFrame(target);
}
nsTArray<TouchBehaviorFlags> TouchActionHelper::GetAllowedTouchBehavior(
nsIWidget* aWidget, dom::Document* aDocument,
const WidgetTouchEvent& aEvent) {
nsTArray<TouchBehaviorFlags> flags;
if (!aWidget || !aDocument) {
return flags;
}
if (PresShell* presShell = aDocument->GetPresShell()) {
if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
for (const auto& touch : aEvent.mTouches) {
flags.AppendElement(GetAllowedTouchBehaviorForPoint(
aWidget, RelativeTo{rootFrame, ViewportType::Visual},
touch->mRefPoint));
}
}
}
return flags;
}
TouchBehaviorFlags TouchActionHelper::GetAllowedTouchBehaviorForFrame(
nsIFrame* aFrame) {
TouchBehaviorFlags behavior = AllowedTouchBehavior::VERTICAL_PAN |
AllowedTouchBehavior::HORIZONTAL_PAN |
AllowedTouchBehavior::PINCH_ZOOM |
AllowedTouchBehavior::ANIMATING_ZOOM;
if (!aFrame) {
return behavior;
}
nsIScrollableFrame* nearestScrollableParent =
nsLayoutUtils::GetNearestScrollableFrame(target, 0);
nsLayoutUtils::GetNearestScrollableFrame(aFrame, 0);
nsIFrame* nearestScrollableFrame = do_QueryFrame(nearestScrollableParent);
// We're walking up the DOM tree until we meet the element with touch behavior
@ -88,7 +114,7 @@ static TouchBehaviorFlags GetAllowedTouchBehaviorForPoint(
bool considerPanning = true;
for (nsIFrame* frame = target; frame && frame->GetContent() && behavior;
for (nsIFrame* frame = aFrame; frame && frame->GetContent() && behavior;
frame = frame->GetInFlowParent()) {
UpdateAllowedBehavior(frame->UsedTouchAction(), considerPanning, behavior);
@ -102,23 +128,4 @@ static TouchBehaviorFlags GetAllowedTouchBehaviorForPoint(
return behavior;
}
nsTArray<TouchBehaviorFlags> TouchActionHelper::GetAllowedTouchBehavior(
nsIWidget* aWidget, dom::Document* aDocument,
const WidgetTouchEvent& aEvent) {
nsTArray<TouchBehaviorFlags> flags;
if (!aWidget || !aDocument) {
return flags;
}
if (PresShell* presShell = aDocument->GetPresShell()) {
if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
for (auto& touch : aEvent.mTouches) {
flags.AppendElement(GetAllowedTouchBehaviorForPoint(
aWidget, RelativeTo{rootFrame, ViewportType::Visual},
touch->mRefPoint));
}
}
}
return flags;
}
} // namespace mozilla::layers

View File

@ -37,6 +37,8 @@ class TouchActionHelper {
static nsTArray<TouchBehaviorFlags> GetAllowedTouchBehavior(
nsIWidget* aWidget, dom::Document* aDocument,
const WidgetTouchEvent& aPoint);
static TouchBehaviorFlags GetAllowedTouchBehaviorForFrame(nsIFrame* aFrame);
};
} // namespace mozilla::layers

View File

@ -41,7 +41,7 @@ enum class CompositorHitTestFlags : uint8_t {
eTouchActionPanXDisabled, // 0x0010
eTouchActionPanYDisabled, // 0x0020
eTouchActionPinchZoomDisabled, // 0x0040
eTouchActionDoubleTapZoomDisabled, // 0x0080
eTouchActionAnimatingZoomDisabled, // 0x0080
// The frame is a scrollbar or a subframe inside a scrollbar (including
// scroll thumbs)
@ -76,7 +76,7 @@ constexpr CompositorHitTestInfo CompositorHitTestTouchActionMask(
CompositorHitTestFlags::eTouchActionPanXDisabled,
CompositorHitTestFlags::eTouchActionPanYDisabled,
CompositorHitTestFlags::eTouchActionPinchZoomDisabled,
CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled);
CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled);
// Mask to check all the flags that involve APZ waiting for results from the
// main thread

View File

@ -11675,7 +11675,7 @@ CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
if (touchAction == StyleTouchAction::AUTO) {
// nothing to do
} else if (touchAction & StyleTouchAction::MANIPULATION) {
result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
} else {
// This path handles the cases none | [pan-x || pan-y || pinch-zoom] so
// double-tap is disabled in here.
@ -11683,7 +11683,7 @@ CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
}
result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
if (!(touchAction & StyleTouchAction::PAN_X)) {
result += CompositorHitTestFlags::eTouchActionPanXDisabled;