Bug 1794070 - Make mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection private. r=botond

And initialized it only in ctors so that in the next change we can use it to
tell whether this pan event may trigger swipe or not without calling
SwipeTracker::CanTriggerSwipe every time.

There's an unintuitive change in a GTest sending a horizontal pan-start event.
With this changeset, all incomming pan-start events on desktop platforms
basically set mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection
to true even if the event is generated in GTests. So the pan start event runs
into the short circuit path for swipe-to-navigation. This is a good thing because
it means the GTest replicates what our browser does properly.

Differential Revision: https://phabricator.services.mozilla.com/D160435
This commit is contained in:
Hiroyuki Ikezoe 2022-11-22 06:52:51 +00:00
Родитель 4c8d1c6a7f
Коммит ebfe7e293e
9 изменённых файлов: 145 добавлений и 100 удалений

Просмотреть файл

@ -455,7 +455,7 @@ APZEventResult InputQueue::ReceivePanGestureInput(
if (aFlags.mTargetConfirmed && event.mOverscrollBehaviorAllowsSwipe &&
event
.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
.RequiresContentResponseIfCannotScrollHorizontallyInStartDirection() &&
!CanScrollTargetHorizontally(event, block)) {
// This event may trigger a swipe gesture, depending on what our caller
// wants to do it. We need to suspend handling of this block until we get

Просмотреть файл

@ -1784,8 +1784,15 @@ TEST_F(APZCOverscrollTesterMock,
// Start a new horizontal pan gesture on the child scroller which should be
// handled by the child APZC now.
QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint,
ScreenPoint(-2, 0), mcc->Time());
APZEventResult result = PanGesture(PanGestureInput::PANGESTURE_START, manager,
panPoint, ScreenPoint(-2, 0), mcc->Time());
// The above horizontal pan start event was flagged as "this event may trigger
// swipe" and either the root scrollable frame or the horizontal child
// scrollable frame is not scrollable in the pan start direction, thus the pan
// start event run into the short circuit path for swipe-to-navigation in
// InputQueue::ReceivePanGestureInput, which means it's waiting for the
// content response, so we need to respond explicitly here.
manager->ContentReceivedInputBlock(result.mInputBlockId, false);
mcc->AdvanceByMillis(10);
QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,

Просмотреть файл

@ -14,6 +14,7 @@
#include "nsThreadUtils.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/SwipeTracker.h"
#include "UnitTransforms.h"
namespace mozilla {
@ -422,10 +423,11 @@ PanGestureInput::PanGestureInput()
mUserDeltaMultiplierX(1.0),
mUserDeltaMultiplierY(1.0),
mHandledByAPZ(false),
mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(false),
mOverscrollBehaviorAllowsSwipe(false),
mSimulateMomentum(false),
mIsNoLineOrPageDelta(true) {}
mIsNoLineOrPageDelta(true),
mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(
false) {}
PanGestureInput::PanGestureInput(PanGestureType aType, uint32_t aTime,
TimeStamp aTimeStamp,
@ -441,10 +443,24 @@ PanGestureInput::PanGestureInput(PanGestureType aType, uint32_t aTime,
mUserDeltaMultiplierX(1.0),
mUserDeltaMultiplierY(1.0),
mHandledByAPZ(false),
mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(false),
mOverscrollBehaviorAllowsSwipe(false),
mSimulateMomentum(false),
mIsNoLineOrPageDelta(true) {}
mIsNoLineOrPageDelta(true) {
mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection =
SwipeTracker::CanTriggerSwipe(*this);
}
PanGestureInput::PanGestureInput(PanGestureType aType, uint32_t aTime,
TimeStamp aTimeStamp,
const ScreenPoint& aPanStartPoint,
const ScreenPoint& aPanDisplacement,
Modifiers aModifiers,
IsEligibleForSwipe aIsEligibleForSwipe)
: PanGestureInput(aType, aTime, aTimeStamp, aPanStartPoint,
aPanDisplacement, aModifiers) {
mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &=
bool(aIsEligibleForSwipe);
}
void PanGestureInput::SetLineOrPageDeltas(int32_t aLineOrPageDeltaX,
int32_t aLineOrPageDeltaY) {

Просмотреть файл

@ -323,6 +323,8 @@ class MouseInput : public InputData {
* These events are currently only used for scrolling on desktop.
*/
class PanGestureInput : public InputData {
friend struct IPC::ParamTraits<PanGestureInput>;
protected:
friend mozilla::layers::APZInputBridgeChild;
friend mozilla::layers::PAPZInputBridgeParent;
@ -398,6 +400,12 @@ class PanGestureInput : public InputData {
const ScreenPoint& aPanStartPoint,
const ScreenPoint& aPanDisplacement, Modifiers aModifiers);
enum class IsEligibleForSwipe : bool { No, Yes };
PanGestureInput(PanGestureType aType, uint32_t aTime, TimeStamp aTimeStamp,
const ScreenPoint& aPanStartPoint,
const ScreenPoint& aPanDisplacement, Modifiers aModifiers,
IsEligibleForSwipe aIsEligibleForSwipe);
void SetLineOrPageDeltas(int32_t aLineOrPageDeltaX,
int32_t aLineOrPageDeltaY);
@ -410,6 +418,19 @@ class PanGestureInput : public InputData {
ScreenPoint UserMultipliedPanDisplacement() const;
ParentLayerPoint UserMultipliedLocalPanDisplacement() const;
void SetHandledByAPZ(bool aHandled) { mHandledByAPZ = aHandled; }
void SetOverscrollBehaviorAllowsSwipe(bool aAllows) {
mOverscrollBehaviorAllowsSwipe = aAllows;
}
void SetSimulateMomentum(bool aSimulate) { mSimulateMomentum = aSimulate; }
void SetIsNoLineOrPageDelta(bool aIsNoLineOrPageDelta) {
mIsNoLineOrPageDelta = aIsNoLineOrPageDelta;
}
bool RequiresContentResponseIfCannotScrollHorizontallyInStartDirection()
const {
return mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection;
}
static gfx::IntPoint GetIntegerDeltaForEvent(bool aIsStart, float x, float y);
// Warning, this class is serialized and sent over IPC. Any change to its
@ -437,14 +458,6 @@ class PanGestureInput : public InputData {
bool mHandledByAPZ : 1;
// If this is true, and this event started a new input block that couldn't
// find a scrollable target which is scrollable in the horizontal component
// of the scroll start direction, then this input block needs to be put on
// hold until a content response has arrived, even if the block has a
// confirmed target.
// This is used by events that can result in a swipe instead of a scroll.
bool mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection : 1;
// This is used by APZ to communicate to widget code whether the
// overscroll-behavior of the scroll frame handling this swipe allows
// non-local overscroll behaviors in the horizontal direction (such as
@ -463,19 +476,19 @@ class PanGestureInput : public InputData {
// code).
bool mIsNoLineOrPageDelta : 1;
void SetHandledByAPZ(bool aHandled) { mHandledByAPZ = aHandled; }
private:
// If this is true, and this event started a new input block that couldn't
// find a scrollable target which is scrollable in the horizontal component
// of the scroll start direction, then this input block needs to be put on
// hold until a content response has arrived, even if the block has a
// confirmed target.
// This is used by events that can result in a swipe instead of a scroll.
bool mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection : 1;
void SetRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(
bool aRequires) {
mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection =
aRequires;
}
void SetOverscrollBehaviorAllowsSwipe(bool aAllows) {
mOverscrollBehaviorAllowsSwipe = aAllows;
}
void SetSimulateMomentum(bool aSimulate) { mSimulateMomentum = aSimulate; }
void SetIsNoLineOrPageDelta(bool aIsNoLineOrPageDelta) {
mIsNoLineOrPageDelta = aIsNoLineOrPageDelta;
}
};
/**

Просмотреть файл

@ -200,8 +200,6 @@ static uint32_t sUniqueKeyEventId = 0;
- (bool)beginOrEndGestureForEventPhase:(NSEvent*)aEvent;
- (bool)shouldConsiderStartingSwipeFromEvent:(NSEvent*)aEvent;
@end
#pragma mark -
@ -2722,20 +2720,6 @@ NSEvent* gLastDragMouseDownEvent = nil; // [strong]
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
- (bool)shouldConsiderStartingSwipeFromEvent:(NSEvent*)anEvent {
// Only initiate horizontal tracking for gestures that have just begun --
// otherwise a scroll to one side of the page can have a swipe tacked on
// to it.
// [NSEvent isSwipeTrackingFromScrollEventsEnabled] checks whether the
// AppleEnableSwipeNavigateWithScrolls global preference is set. If it isn't,
// fluid swipe tracking is disabled, and a horizontal two-finger gesture is
// always a scroll (even in Safari). This preference can't (currently) be set
// from the Preferences UI -- only using 'defaults write'.
NSEventPhase eventPhase = [anEvent phase];
return [anEvent type] == NSEventTypeScrollWheel && eventPhase == NSEventPhaseBegan &&
[anEvent hasPreciseScrollingDeltas] && [NSEvent isSwipeTrackingFromScrollEventsEnabled];
}
- (void)setUsingOMTCompositor:(BOOL)aUseOMTC {
mUsingOMTCompositor = aUseOMTC;
}
@ -3095,36 +3079,6 @@ static bool ShouldDispatchBackForwardCommandForMouseButton(int16_t aButton) {
[self sendWheelStartOrStop:second forEvent:theEvent];
}
static PanGestureInput::PanGestureType PanGestureTypeForEvent(NSEvent* aEvent) {
switch ([aEvent phase]) {
case NSEventPhaseMayBegin:
return PanGestureInput::PANGESTURE_MAYSTART;
case NSEventPhaseCancelled:
return PanGestureInput::PANGESTURE_CANCELLED;
case NSEventPhaseBegan:
return PanGestureInput::PANGESTURE_START;
case NSEventPhaseChanged:
return PanGestureInput::PANGESTURE_PAN;
case NSEventPhaseEnded:
return PanGestureInput::PANGESTURE_END;
case NSEventPhaseNone:
switch ([aEvent momentumPhase]) {
case NSEventPhaseBegan:
return PanGestureInput::PANGESTURE_MOMENTUMSTART;
case NSEventPhaseChanged:
return PanGestureInput::PANGESTURE_MOMENTUMPAN;
case NSEventPhaseEnded:
return PanGestureInput::PANGESTURE_MOMENTUMEND;
default:
NS_ERROR("unexpected event phase");
return PanGestureInput::PANGESTURE_PAN;
}
default:
NS_ERROR("unexpected event phase");
return PanGestureInput::PANGESTURE_PAN;
}
}
static int32_t RoundUp(double aDouble) {
return aDouble < 0 ? static_cast<int32_t>(floor(aDouble)) : static_cast<int32_t>(ceil(aDouble));
}
@ -3213,23 +3167,13 @@ static gfx::IntPoint GetIntegerDeltaForEvent(NSEvent* aEvent) {
}
if (usePreciseDeltas && hasPhaseInformation) {
PanGestureInput panEvent(PanGestureTypeForEvent(theEvent), eventIntervalTime, eventTimeStamp,
position, ScreenPoint(), modifiers);
PanGestureInput panEvent =
nsCocoaUtils::CreatePanGestureEvent(theEvent, eventIntervalTime, eventTimeStamp, position,
preciseDelta, lineOrPageDelta, modifiers);
// Always force zero deltas on event types that shouldn't cause any scrolling,
// so that we don't dispatch DOM wheel events for them.
bool shouldIgnoreDeltas = panEvent.mType == PanGestureInput::PANGESTURE_MAYSTART ||
panEvent.mType == PanGestureInput::PANGESTURE_CANCELLED;
if (!shouldIgnoreDeltas) {
panEvent.mPanDisplacement = preciseDelta;
panEvent.SetLineOrPageDeltas(lineOrPageDelta.x, lineOrPageDelta.y);
}
bool canTriggerSwipe = [self shouldConsiderStartingSwipeFromEvent:theEvent] &&
// NOTE: This `canTriggerSwipe` will be dropped in a subsequent change.
bool canTriggerSwipe = nsCocoaUtils::ShouldConsiderStartingSwipeFromEvent(theEvent) &&
SwipeTracker::CanTriggerSwipe(panEvent);
;
panEvent.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection = canTriggerSwipe;
geckoChildDeathGrip->DispatchAPZWheelInputEvent(panEvent, canTriggerSwipe);
} else if (usePreciseDeltas) {
// This is on 10.6 or old touchpads that don't have any phase information.

Просмотреть файл

@ -8,6 +8,7 @@
#import <Cocoa/Cocoa.h>
#include "InputData.h"
#include "nsRect.h"
#include "imgIContainer.h"
#include "nsTArray.h"
@ -455,6 +456,11 @@ class nsCocoaUtils {
static void InvalidateHiDPIState();
static mozilla::PanGestureInput CreatePanGestureEvent(
NSEvent* aNativeEvent, uint32_t aTime, mozilla::TimeStamp aTimeStamp,
const mozilla::ScreenPoint& aPanStartPoint, const mozilla::ScreenPoint& aPreciseDelta,
const mozilla::gfx::IntPoint& aLineOrPageDelta, mozilla::Modifiers aModifiers);
private:
/**
* Completion handlers used as an argument to the macOS API to

Просмотреть файл

@ -46,12 +46,12 @@ using namespace mozilla::widget;
using mozilla::dom::Promise;
using mozilla::gfx::DataSourceSurface;
using mozilla::gfx::DrawTarget;
using mozilla::gfx::SamplingFilter;
using mozilla::gfx::IntPoint;
using mozilla::gfx::IntRect;
using mozilla::gfx::IntSize;
using mozilla::gfx::SurfaceFormat;
using mozilla::gfx::SamplingFilter;
using mozilla::gfx::SourceSurface;
using mozilla::gfx::SurfaceFormat;
using mozilla::image::ImageRegion;
LazyLogModule gCocoaUtilsLog("nsCocoaUtils");
@ -808,11 +808,15 @@ struct KeyConversionData {
static const KeyConversionData gKeyConversions[] = {
#define KEYCODE_ENTRY(aStr, aCode) \
{ #aStr, sizeof(#aStr) - 1, NS_##aStr, aCode }
{ \
# aStr, sizeof(#aStr) - 1, NS_##aStr, aCode \
}
// Some keycodes may have different name in KeyboardEvent from its key name.
#define KEYCODE_ENTRY2(aStr, aNSName, aCode) \
{ #aStr, sizeof(#aStr) - 1, NS_##aNSName, aCode }
{ \
# aStr, sizeof(#aStr) - 1, NS_##aNSName, aCode \
}
KEYCODE_ENTRY(VK_CANCEL, 0x001B),
KEYCODE_ENTRY(VK_DELETE, NSDeleteFunctionKey),
@ -1445,3 +1449,68 @@ void nsCocoaUtils::ResolveVideoCapturePromises(bool aGranted) {
}));
NS_DispatchToMainThread(runnable.forget());
}
static PanGestureInput::PanGestureType PanGestureTypeForEvent(NSEvent* aEvent) {
switch ([aEvent phase]) {
case NSEventPhaseMayBegin:
return PanGestureInput::PANGESTURE_MAYSTART;
case NSEventPhaseCancelled:
return PanGestureInput::PANGESTURE_CANCELLED;
case NSEventPhaseBegan:
return PanGestureInput::PANGESTURE_START;
case NSEventPhaseChanged:
return PanGestureInput::PANGESTURE_PAN;
case NSEventPhaseEnded:
return PanGestureInput::PANGESTURE_END;
case NSEventPhaseNone:
switch ([aEvent momentumPhase]) {
case NSEventPhaseBegan:
return PanGestureInput::PANGESTURE_MOMENTUMSTART;
case NSEventPhaseChanged:
return PanGestureInput::PANGESTURE_MOMENTUMPAN;
case NSEventPhaseEnded:
return PanGestureInput::PANGESTURE_MOMENTUMEND;
default:
NS_ERROR("unexpected event phase");
return PanGestureInput::PANGESTURE_PAN;
}
default:
NS_ERROR("unexpected event phase");
return PanGestureInput::PANGESTURE_PAN;
}
}
bool static ShouldConsiderStartingSwipeFromEvent(NSEvent* anEvent) {
// Only initiate horizontal tracking for gestures that have just begun --
// otherwise a scroll to one side of the page can have a swipe tacked on
// to it.
// [NSEvent isSwipeTrackingFromScrollEventsEnabled] checks whether the
// AppleEnableSwipeNavigateWithScrolls global preference is set. If it isn't,
// fluid swipe tracking is disabled, and a horizontal two-finger gesture is
// always a scroll (even in Safari). This preference can't (currently) be set
// from the Preferences UI -- only using 'defaults write'.
NSEventPhase eventPhase = [anEvent phase];
return [anEvent type] == NSEventTypeScrollWheel && eventPhase == NSEventPhaseBegan &&
[anEvent hasPreciseScrollingDeltas] && [NSEvent isSwipeTrackingFromScrollEventsEnabled];
}
PanGestureInput nsCocoaUtils::CreatePanGestureEvent(
NSEvent* aNativeEvent, uint32_t aTime, TimeStamp aTimeStamp, const ScreenPoint& aPanStartPoint,
const ScreenPoint& aPreciseDelta, const gfx::IntPoint& aLineOrPageDelta, Modifiers aModifiers) {
PanGestureInput::PanGestureType type = PanGestureTypeForEvent(aNativeEvent);
// Always force zero deltas on event types that shouldn't cause any scrolling,
// so that we don't dispatch DOM wheel events for them.
bool shouldIgnoreDeltas =
type == PanGestureInput::PANGESTURE_MAYSTART || type == PanGestureInput::PANGESTURE_CANCELLED;
PanGestureInput panEvent(
type, aTime, aTimeStamp, aPanStartPoint, !shouldIgnoreDeltas ? aPreciseDelta : ScreenPoint(),
aModifiers,
PanGestureInput::IsEligibleForSwipe(ShouldConsiderStartingSwipeFromEvent(aNativeEvent)));
if (!shouldIgnoreDeltas) {
panEvent.SetLineOrPageDeltas(aLineOrPageDelta.x, aLineOrPageDelta.y);
}
return panEvent;
}

Просмотреть файл

@ -5017,10 +5017,6 @@ void nsWindow::OnScrollEvent(GdkEventScroll* aEvent) {
panEvent.mSimulateMomentum =
StaticPrefs::apz_gtk_kinetic_scroll_enabled();
panEvent
.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection =
SwipeTracker::CanTriggerSwipe(panEvent);
DispatchPanGesture(panEvent);
if (mCurrentSynthesizedTouchpadPan.mSavedObserver != 0) {

Просмотреть файл

@ -528,12 +528,6 @@ void DManipEventHandler::SendPanCommon(nsWindow* aWindow, Phase aPhase,
ScreenPoint(aDeltaX, aDeltaY),
aMods};
// This `SendPanCommon` gets called only if the Windows setting, "Drag two
// fingers to scroll" option, is enabled (or it gets called in tests), so we
// don't need to explicitly check whether the option is enabled or not here.
event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection =
SwipeTracker::CanTriggerSwipe(event);
aWindow->SendAnAPZEvent(event);
}