Bug 1774875 - Immediately fire click events for non-scrollable elements. r=botond,geckoview-reviewers,m_kato

Non-scrollable elements are immediately activated on touch-start, so it
is not necessary to delay firing the synthesized mouse and click events.

Differential Revision: https://phabricator.services.mozilla.com/D169727
This commit is contained in:
Dan Robertson 2023-04-20 13:26:25 +00:00
Родитель e553a2aae1
Коммит 3e22d70394
19 изменённых файлов: 359 добавлений и 133 удалений

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

@ -1350,7 +1350,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvHandleTap(
switch (aType) {
case GeckoContentController::TapType::eSingleTap:
if (mBrowserChildMessageManager) {
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1);
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1,
aInputBlockId);
}
break;
case GeckoContentController::TapType::eDoubleTap:
@ -1358,7 +1359,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvHandleTap(
break;
case GeckoContentController::TapType::eSecondTap:
if (mBrowserChildMessageManager) {
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2);
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2,
aInputBlockId);
}
break;
case GeckoContentController::TapType::eLongTap:
@ -1392,8 +1394,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityHandleTap(
bool BrowserChild::NotifyAPZStateChange(
const ViewID& aViewId,
const layers::GeckoContentController::APZStateChange& aChange,
const int& aArg) {
mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg);
const int& aArg, Maybe<uint64_t> aInputBlockId) {
mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg, aInputBlockId);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (aChange ==

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

@ -579,7 +579,7 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
bool NotifyAPZStateChange(
const ViewID& aViewId,
const layers::GeckoContentController_APZStateChange& aChange,
const int& aArg);
const int& aArg, Maybe<uint64_t> aInputBlockId);
void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics);
void ZoomToRect(const uint32_t& aPresShellId,
const ScrollableLayerGuid::ViewID& aViewId,

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

@ -113,9 +113,14 @@ class GeckoContentController {
* |aChange| identifies the type of state change
* |aArg| is used by some state changes to pass extra information (see
* the documentation for each state change above)
* |aInputBlockId| is populated for the |eStartTouch| and |eEndTouch|
* state changes and identifies the input block of the
* gesture that triggers the state change.
*/
virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange, int aArg = 0) {}
APZStateChange aChange, int aArg = 0,
Maybe<uint64_t> aInputBlockId = Nothing()) {
}
/**
* Notify content of a MozMouseScrollFailed event.

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

@ -1312,7 +1312,8 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::eStartTouch,
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(
this));
this),
Some(GetCurrentTouchBlock()->GetBlockId()));
}
mLastTouch.mTimeStamp = mTouchStartTime = aEvent.mTimeStamp;
SetState(TOUCHING);
@ -3106,7 +3107,8 @@ void AsyncPanZoomController::OnTouchEndOrCancel() {
MOZ_ASSERT(GetCurrentTouchBlock());
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::eEndTouch,
GetCurrentTouchBlock()->SingleTapOccurred());
GetCurrentTouchBlock()->SingleTapOccurred(),
Some(GetCurrentTouchBlock()->GetBlockId()));
}
}

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

@ -140,8 +140,9 @@ class MockContentController : public GeckoContentController {
void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
NS_DispatchToMainThread(std::move(aTask));
}
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid,
APZStateChange aChange, int aArg));
MOCK_METHOD4(NotifyAPZStateChange,
void(const ScrollableLayerGuid& aGuid, APZStateChange aChange,
int aArg, Maybe<uint64_t> aInputBlockId));
MOCK_METHOD0(NotifyFlushComplete, void());
MOCK_METHOD3(NotifyAsyncScrollbarDragInitiated,
void(uint64_t, const ScrollableLayerGuid::ViewID&,

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

@ -8,6 +8,7 @@
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "mozilla/layers/WebRenderScrollDataWrapper.h"
#include "apz/util/APZEventState.h"
#include "InputUtils.h"
@ -34,6 +35,24 @@ class APZCTransformNotificationTester : public APZCTreeManagerTester {
mRootApzc = ApzcOf(root);
}
void SetupNonScrollableTest() {
const char* treeShape = "x";
LayerIntRegion layerVisibleRegion[] = {
LayerIntRect(0, 0, 100, 100),
};
CreateScrollData(treeShape, layerVisibleRegion);
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
CSSRect(0, 0, 100, 100));
mRegistration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
UpdateHitTestingTree();
mRootApzc = ApzcOf(root);
mRootApzc->GetFrameMetrics().SetIsRootContent(true);
}
};
TEST_F(APZCTransformNotificationTester, PanningTransformNotifications) {
@ -54,48 +73,52 @@ TEST_F(APZCTransformNotificationTester, PanningTransformNotifications) {
{
InSequence s;
EXPECT_CALL(check, Call("Simple pan"));
EXPECT_CALL(*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartTouch, _))
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartTouch, _, _))
.Times(1);
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartPanning, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartPanning, _, _))
.Times(1);
EXPECT_CALL(*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eEndTouch, _))
_, GeckoContentController::APZStateChange::eEndTouch, _, _))
.Times(1);
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Complex pan"));
EXPECT_CALL(*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartTouch, _))
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartTouch, _, _))
.Times(1);
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartPanning, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartPanning, _, _))
.Times(1);
EXPECT_CALL(*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eEndTouch, _))
_, GeckoContentController::APZStateChange::eEndTouch, _, _))
.Times(1);
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
}
@ -118,7 +141,7 @@ TEST_F(APZCTransformNotificationTester, PanWithMomentumTransformNotifications) {
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("Panning"));
@ -129,8 +152,9 @@ TEST_F(APZCTransformNotificationTester, PanWithMomentumTransformNotifications) {
EXPECT_CALL(check, Call("Momentum End"));
// The TransformEnd should only be sent after the momentum pan.
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
@ -195,7 +219,7 @@ TEST_F(APZCTransformNotificationTester,
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("Panning"));
@ -204,8 +228,9 @@ TEST_F(APZCTransformNotificationTester,
// The TransformEnd should only be sent after the pan gesture and 100ms
// timer fire.
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
@ -253,7 +278,7 @@ TEST_F(APZCTransformNotificationTester,
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("Panning"));
@ -262,18 +287,20 @@ TEST_F(APZCTransformNotificationTester,
// new pan gesture begins.
EXPECT_CALL(check, Call("New Pan Start"));
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("New Pan End"));
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
@ -337,7 +364,7 @@ TEST_F(APZCTransformNotificationTester,
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("Panning"));
@ -346,18 +373,20 @@ TEST_F(APZCTransformNotificationTester,
// new wheel event begins.
EXPECT_CALL(check, Call("Wheel Start"));
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("Wheel End"));
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
}
@ -410,7 +439,7 @@ TEST_F(APZCTransformNotificationTester, PanOverscrollTransformNotifications) {
EXPECT_CALL(
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformBegin, _))
_, GeckoContentController::APZStateChange::eTransformBegin, _, _))
.Times(1);
EXPECT_CALL(check, Call("Panning Into Overscroll"));
@ -419,8 +448,9 @@ TEST_F(APZCTransformNotificationTester, PanOverscrollTransformNotifications) {
// The TransformEnd should only be sent after the overscroll animation
// completes.
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _))
*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eTransformEnd, _, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
}
@ -459,3 +489,79 @@ TEST_F(APZCTransformNotificationTester, PanOverscrollTransformNotifications) {
check.Call("Done");
}
#endif
TEST_F(APZCTransformNotificationTester, ScrollableTouchStateChange) {
// Create a scroll frame with available space for a scroll.
SetupBasicTest();
MockFunction<void(std::string checkPointName)> check;
{
EXPECT_CALL(check, Call("Start"));
// We receive a touch-start with the flag indicating that the
// touch-start occurred over a scrollable element.
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartTouch, 1, _))
.Times(1);
EXPECT_CALL(*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eEndTouch, 1, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
}
check.Call("Start");
// Conduct a touch down and touch up in the scrollable element,
// and ensure the correct state change notifications are sent.
QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
TouchDown(mRootApzc, ScreenIntPoint(10, 10), mcc->Time());
mcc->AdvanceByMillis(5);
mRootApzc->AdvanceAnimations(mcc->GetSampleTime());
QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
TouchUp(mRootApzc, ScreenIntPoint(10, 10), mcc->Time());
mcc->AdvanceByMillis(5);
mRootApzc->AdvanceAnimations(mcc->GetSampleTime());
check.Call("Done");
}
TEST_F(APZCTransformNotificationTester, NonScrollableTouchStateChange) {
// Create a non-scrollable frame with no space to scroll.
SetupNonScrollableTest();
MockFunction<void(std::string checkPointName)> check;
{
EXPECT_CALL(check, Call("Start"));
// We receive a touch-start with the flag indicating that the
// touch-start occurred over a non-scrollable element.
EXPECT_CALL(
*mcc, NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eStartTouch, 0, _))
.Times(1);
EXPECT_CALL(*mcc,
NotifyAPZStateChange(
_, GeckoContentController::APZStateChange::eEndTouch, 1, _))
.Times(1);
EXPECT_CALL(check, Call("Done"));
}
check.Call("Start");
// Conduct a touch down and touch up in the non-scrollable element,
// and ensure the correct state change notifications are sent.
QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
TouchDown(mRootApzc, ScreenIntPoint(10, 10), mcc->Time());
mcc->AdvanceByMillis(5);
mRootApzc->AdvanceAnimations(mcc->GetSampleTime());
QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
TouchUp(mRootApzc, ScreenIntPoint(10, 10), mcc->Time());
mcc->AdvanceByMillis(5);
mRootApzc->AdvanceAnimations(mcc->GetSampleTime());
check.Call("Done");
}

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

@ -33,7 +33,6 @@
#include "nsINamed.h"
#include "nsIScrollableFrame.h"
#include "nsIScrollbarMediator.h"
#include "nsITimer.h"
#include "nsIWeakReferenceUtils.h"
#include "nsIWidget.h"
#include "nsLayoutUtils.h"
@ -105,6 +104,7 @@ APZEventState::APZEventState(nsIWidget* aWidget,
mEndTouchIsClick(false),
mFirstTouchCancelled(false),
mTouchEndCancelled(false),
mSingleTapsPendingTargetInfo(),
mLastTouchIdentifier(0) {
nsresult rv;
mWidget = do_GetWeakReference(aWidget, &rv);
@ -115,58 +115,69 @@ APZEventState::APZEventState(nsIWidget* aWidget,
APZEventState::~APZEventState() = default;
class DelayedFireSingleTapEvent final : public nsITimerCallback,
public nsINamed {
public:
NS_DECL_ISUPPORTS
DelayedFireSingleTapEvent(nsWeakPtr aWidget, LayoutDevicePoint& aPoint,
Modifiers aModifiers, int32_t aClickCount,
nsITimer* aTimer, RefPtr<nsIContent>& aTouchRollup)
: mWidget(aWidget),
mPoint(aPoint),
mModifiers(aModifiers),
mClickCount(aClickCount)
// Hold the reference count until we are called back.
,
mTimer(aTimer),
mTouchRollup(aTouchRollup) {}
NS_IMETHOD Notify(nsITimer*) override {
if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
widget::nsAutoRollup rollup(mTouchRollup.get());
APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, mClickCount,
widget);
}
mTimer = nullptr;
return NS_OK;
RefPtr<DelayedFireSingleTapEvent> DelayedFireSingleTapEvent::Create(
Maybe<SingleTapTargetInfo>&& aTargetInfo) {
nsCOMPtr<nsITimer> timer = NS_NewTimer();
RefPtr<DelayedFireSingleTapEvent> event =
new DelayedFireSingleTapEvent(std::move(aTargetInfo), timer);
nsresult rv = timer->InitWithCallback(
event, StaticPrefs::ui_touch_activation_duration_ms(),
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
event->ClearTimer();
event = nullptr;
}
return event;
}
NS_IMETHOD
GetName(nsACString& aName) override {
aName.AssignLiteral("DelayedFireSingleTapEvent");
return NS_OK;
NS_IMETHODIMP DelayedFireSingleTapEvent::Notify(nsITimer*) {
APZES_LOG("DelayedFireSingeTapEvent notification ready=%d",
mTargetInfo.isSome());
// If the required information to fire the synthesized events has not
// been populated yet, we have not received the touch-end. In this case
// we should not fire the synthesized events here. The synthesized events
// will be fired on touch-end in this case.
if (mTargetInfo.isSome()) {
FireSingleTapEvent();
}
mTimer = nullptr;
return NS_OK;
}
void ClearTimer() { mTimer = nullptr; }
NS_IMETHODIMP DelayedFireSingleTapEvent::GetName(nsACString& aName) {
aName.AssignLiteral("DelayedFireSingleTapEvent");
return NS_OK;
}
private:
~DelayedFireSingleTapEvent() = default;
void DelayedFireSingleTapEvent::PopulateTargetInfo(
SingleTapTargetInfo&& aTargetInfo) {
MOZ_ASSERT(!mTargetInfo.isSome());
mTargetInfo = Some(std::move(aTargetInfo));
// If the timer no longer exists, we have surpassed the minimum elapsed
// time to delay the synthesized click. We can immediately fire the
// synthesized events in this case.
if (!mTimer) {
FireSingleTapEvent();
}
}
nsWeakPtr mWidget;
LayoutDevicePoint mPoint;
Modifiers mModifiers;
int32_t mClickCount;
nsCOMPtr<nsITimer> mTimer;
RefPtr<nsIContent> mTouchRollup;
};
void DelayedFireSingleTapEvent::FireSingleTapEvent() {
MOZ_ASSERT(mTargetInfo.isSome());
nsCOMPtr<nsIWidget> widget = do_QueryReferent(mTargetInfo->mWidget);
if (widget) {
widget::nsAutoRollup rollup(mTargetInfo->mTouchRollup.get());
APZCCallbackHelper::FireSingleTapEvent(mTargetInfo->mPoint,
mTargetInfo->mModifiers,
mTargetInfo->mClickCount, widget);
}
}
NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback, nsINamed)
void APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers,
int32_t aClickCount) {
Modifiers aModifiers, int32_t aClickCount,
uint64_t aInputBlockId) {
APZES_LOG("Handling single tap at %s with %d\n", ToString(aPoint).c_str(),
mTouchEndCancelled);
@ -182,19 +193,23 @@ void APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
return;
}
LayoutDevicePoint ldPoint = aPoint * aScale;
SingleTapTargetInfo targetInfo(mWidget, aPoint * aScale, aModifiers,
aClickCount, touchRollup);
APZES_LOG("Scheduling timer for click event\n");
nsCOMPtr<nsITimer> timer = NS_NewTimer();
RefPtr<DelayedFireSingleTapEvent> callback = new DelayedFireSingleTapEvent(
mWidget, ldPoint, aModifiers, aClickCount, timer, touchRollup);
nsresult rv = timer->InitWithCallback(
callback, StaticPrefs::ui_touch_activation_duration_ms(),
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
// Make |callback| not hold the timer, so they will both be destructed when
// we leave the scope of this function.
callback->ClearTimer();
auto delayedEvent = mSingleTapsPendingTargetInfo.find(aInputBlockId);
if (delayedEvent != mSingleTapsPendingTargetInfo.end()) {
APZES_LOG("Found tap for block=%" PRIu64, aInputBlockId);
// With the target info populated, the event will be fired as
// soon as the delay timer expires (or now, if it has already expired).
delayedEvent->second->PopulateTargetInfo(std::move(targetInfo));
mSingleTapsPendingTargetInfo.erase(delayedEvent);
} else {
APZES_LOG("Scheduling timer for click event\n");
// We don't need to keep a reference to the event, because the
// event and its timer keep each other alive until the timer expires
DelayedFireSingleTapEvent::Create(Some(std::move(targetInfo)));
}
}
@ -496,7 +511,8 @@ void APZEventState::ProcessMouseEvent(const WidgetMouseEvent& aEvent,
}
void APZEventState::ProcessAPZStateChange(ViewID aViewId,
APZStateChange aChange, int aArg) {
APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) {
switch (aChange) {
case APZStateChange::eTransformBegin: {
nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
@ -531,7 +547,21 @@ void APZEventState::ProcessAPZStateChange(ViewID aViewId,
break;
}
case APZStateChange::eStartTouch: {
mActiveElementManager->HandleTouchStart(aArg);
bool canBePan = aArg;
mActiveElementManager->HandleTouchStart(canBePan);
// If this is a non-scrollable content, set a timer for the amount of
// time specified by ui.touch_activation.duration_ms to fire the
// synthesized click and mouse events.
APZES_LOG("%s: can-be-pan=%d", __FUNCTION__, aArg);
if (!canBePan) {
MOZ_ASSERT(aInputBlockId.isSome());
RefPtr<DelayedFireSingleTapEvent> delayedEvent =
DelayedFireSingleTapEvent::Create(Nothing());
DebugOnly<bool> insertResult =
mSingleTapsPendingTargetInfo.emplace(*aInputBlockId, delayedEvent)
.second;
MOZ_ASSERT(insertResult, "Failed to insert delayed tap event.");
}
break;
}
case APZStateChange::eStartPanning: {

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

@ -15,11 +15,14 @@
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid
#include "mozilla/layers/TouchCounter.h" // for TouchCounter
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPrefs_ui.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
#include "nsITimer.h"
#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
#include <functional>
#include <unordered_map>
template <class>
class nsCOMPtr;
@ -39,6 +42,59 @@ typedef std::function<void(uint64_t /* input block id */,
bool /* prevent default */)>
ContentReceivedInputBlockCallback;
struct SingleTapTargetInfo {
nsWeakPtr mWidget;
LayoutDevicePoint mPoint;
Modifiers mModifiers;
int32_t mClickCount;
RefPtr<nsIContent> mTouchRollup;
explicit SingleTapTargetInfo(nsWeakPtr aWidget, LayoutDevicePoint aPoint,
Modifiers aModifiers, int32_t aClickCount,
RefPtr<nsIContent> aTouchRollup)
: mWidget(std::move(aWidget)),
mPoint(aPoint),
mModifiers(aModifiers),
mClickCount(aClickCount),
mTouchRollup(std::move(aTouchRollup)) {}
SingleTapTargetInfo(SingleTapTargetInfo&&) = default;
SingleTapTargetInfo& operator=(SingleTapTargetInfo&&) = default;
};
class DelayedFireSingleTapEvent final : public nsITimerCallback,
public nsINamed {
private:
explicit DelayedFireSingleTapEvent(Maybe<SingleTapTargetInfo>&& aTargetInfo,
const nsCOMPtr<nsITimer>& aTimer)
: mTargetInfo(std::move(aTargetInfo))
// Hold the reference count until we are called back.
,
mTimer(aTimer) {}
public:
NS_DECL_ISUPPORTS
static RefPtr<DelayedFireSingleTapEvent> Create(
Maybe<SingleTapTargetInfo>&& aTargetInfo);
NS_IMETHOD Notify(nsITimer*) override;
NS_IMETHOD GetName(nsACString& aName) override;
void PopulateTargetInfo(SingleTapTargetInfo&& aTargetInfo);
void FireSingleTapEvent();
void ClearTimer() { mTimer = nullptr; }
private:
~DelayedFireSingleTapEvent() = default;
Maybe<SingleTapTargetInfo> mTargetInfo;
nsCOMPtr<nsITimer> mTimer;
};
/**
* A content-side component that keeps track of state for handling APZ
* gestures and sending APZ notifications.
@ -55,7 +111,8 @@ class APZEventState final {
void ProcessSingleTap(const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers, int32_t aClickCount);
Modifiers aModifiers, int32_t aClickCount,
uint64_t aInputBlockId);
MOZ_CAN_RUN_SCRIPT
void ProcessLongTap(PresShell* aPresShell, const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
@ -73,7 +130,8 @@ class APZEventState final {
uint64_t aInputBlockId);
void ProcessMouseEvent(const WidgetMouseEvent& aEvent,
uint64_t aInputBlockId);
void ProcessAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
void ProcessAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId);
private:
~APZEventState();
@ -98,6 +156,14 @@ class APZEventState final {
bool mEndTouchIsClick;
bool mFirstTouchCancelled;
bool mTouchEndCancelled;
// Store pending single tap event dispatch tasks keyed on the
// tap gesture's input block id. In the case where multiple taps
// occur in quick succession, we may receive a later tap while the
// dispatch for an earlier tap is still pending.
std::unordered_map<uint64_t, RefPtr<DelayedFireSingleTapEvent>>
mSingleTapsPendingTargetInfo;
int32_t mLastTouchIdentifier;
nsTArray<TouchBehaviorFlags> mTouchBlockAllowedBehaviors;

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

@ -191,13 +191,15 @@ void ChromeProcessController::HandleTap(
switch (aType) {
case TapType::eSingleTap:
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1);
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1,
aInputBlockId);
break;
case TapType::eDoubleTap:
HandleDoubleTap(point, aModifiers, aGuid);
break;
case TapType::eSecondTap:
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2);
mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2,
aInputBlockId);
break;
case TapType::eLongTap: {
RefPtr<APZEventState> eventState(mAPZEventState);
@ -241,13 +243,14 @@ void ChromeProcessController::NotifyPinchGesture(
}
void ChromeProcessController::NotifyAPZStateChange(
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) {
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) {
if (!mUIThread->IsOnCurrentThread()) {
mUIThread->Dispatch(
NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int>(
"layers::ChromeProcessController::NotifyAPZStateChange", this,
&ChromeProcessController::NotifyAPZStateChange, aGuid, aChange,
aArg));
mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, APZStateChange,
int, Maybe<uint64_t>>(
"layers::ChromeProcessController::NotifyAPZStateChange", this,
&ChromeProcessController::NotifyAPZStateChange, aGuid, aChange, aArg,
aInputBlockId));
return;
}
@ -255,7 +258,8 @@ void ChromeProcessController::NotifyAPZStateChange(
return;
}
mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg);
mAPZEventState->ProcessAPZStateChange(aGuid.mScrollId, aChange, aArg,
aInputBlockId);
}
void ChromeProcessController::NotifyMozMouseScrollEvent(

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

@ -63,7 +63,8 @@ class ChromeProcessController : public mozilla::layers::GeckoContentController {
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers) override;
void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange, int aArg) override;
APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) override;
void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
const nsString& aEvent) override;
void NotifyFlushComplete() override;

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

@ -54,9 +54,11 @@ void ContentProcessController::NotifyPinchGesture(
}
void ContentProcessController::NotifyAPZStateChange(
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) {
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) {
if (mBrowser) {
mBrowser->NotifyAPZStateChange(aGuid.mScrollId, aChange, aArg);
mBrowser->NotifyAPZStateChange(aGuid.mScrollId, aChange, aArg,
aInputBlockId);
}
}

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

@ -54,7 +54,8 @@ class ContentProcessController final : public GeckoContentController {
Modifiers aModifiers) override;
void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange, int aArg) override;
APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) override;
void NotifyMozMouseScrollEvent(const ScrollableLayerGuid::ViewID& aScrollId,
const nsString& aEvent) override;

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

@ -69,8 +69,8 @@ mozilla::ipc::IPCResult APZChild::RecvNotifyMozMouseScrollEvent(
mozilla::ipc::IPCResult APZChild::RecvNotifyAPZStateChange(
const ScrollableLayerGuid& aGuid, const APZStateChange& aChange,
const int& aArg) {
mController->NotifyAPZStateChange(aGuid, aChange, aArg);
const int& aArg, Maybe<uint64_t> aInputBlockId) {
mController->NotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId);
return IPC_OK();
}

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

@ -45,7 +45,7 @@ class APZChild final : public PAPZChild {
mozilla::ipc::IPCResult RecvNotifyAPZStateChange(
const ScrollableLayerGuid& aGuid, const APZStateChange& aChange,
const int& aArg);
const int& aArg, Maybe<uint64_t> aInputBlockId);
mozilla::ipc::IPCResult RecvNotifyFlushComplete();

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

@ -62,7 +62,7 @@ child:
async NotifyMozMouseScrollEvent(ViewID aScrollId, nsString aEvent);
async NotifyAPZStateChange(ScrollableLayerGuid aGuid, GeckoContentController_APZStateChange aChange, int aArg);
async NotifyAPZStateChange(ScrollableLayerGuid aGuid, GeckoContentController_APZStateChange aChange, int aArg, uint64_t? aInputBlockId);
[Priority=control]
async NotifyFlushComplete();

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

@ -214,19 +214,21 @@ void RemoteContentController::DispatchToRepaintThread(
}
void RemoteContentController::NotifyAPZStateChange(
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) {
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) {
if (!mCompositorThread->IsOnCurrentThread()) {
// We have to send messages from the compositor thread
mCompositorThread->Dispatch(
NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int>(
NewRunnableMethod<ScrollableLayerGuid, APZStateChange, int,
Maybe<uint64_t>>(
"layers::RemoteContentController::NotifyAPZStateChange", this,
&RemoteContentController::NotifyAPZStateChange, aGuid, aChange,
aArg));
aArg, aInputBlockId));
return;
}
if (mCanSend) {
Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg);
Unused << SendNotifyAPZStateChange(aGuid, aChange, aArg, aInputBlockId);
}
}

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

@ -57,7 +57,8 @@ class RemoteContentController : public GeckoContentController,
void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange, int aArg) override;
APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) override;
void UpdateOverscrollVelocity(const ScrollableLayerGuid& aGuid, float aX,
float aY, bool aIsRootContent) override;

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

@ -40,12 +40,14 @@ void AndroidContentController::UpdateOverscrollOffset(
}
void AndroidContentController::NotifyAPZStateChange(
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) {
const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) {
// This function may get invoked twice, if the first invocation is not on
// the main thread then the ChromeProcessController version of this function
// will redispatch to the main thread. We want to make sure that our handling
// only happens on the main thread.
ChromeProcessController::NotifyAPZStateChange(aGuid, aChange, aArg);
ChromeProcessController::NotifyAPZStateChange(aGuid, aChange, aArg,
aInputBlockId);
if (NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();

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

@ -39,7 +39,8 @@ class AndroidContentController final
const float aY,
const bool aIsRootContent) override;
void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange, int aArg) override;
APZStateChange aChange, int aArg,
Maybe<uint64_t> aInputBlockId) override;
private:
nsWindow* mAndroidWindow;