зеркало из https://github.com/mozilla/gecko-dev.git
End APZ wheel transactions when the mouse moves out of frame. (bug 1142866 part 2, r=kats,botond)
This commit is contained in:
Родитель
a0a7580fcf
Коммит
302fa4036d
|
@ -806,6 +806,47 @@ APZCTreeManager::TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::UpdateWheelTransaction(WidgetInputEvent& aEvent)
|
||||
{
|
||||
WheelBlockState* txn = mInputQueue->GetCurrentWheelTransaction();
|
||||
if (!txn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the transaction has simply timed out, we don't need to do anything
|
||||
// else.
|
||||
if (txn->MaybeTimeout(TimeStamp::Now())) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aEvent.message) {
|
||||
case NS_MOUSE_MOVE:
|
||||
case NS_DRAGDROP_OVER: {
|
||||
WidgetMouseEvent* mouseEvent = aEvent.AsMouseEvent();
|
||||
if (!mouseEvent->IsReal()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScreenIntPoint point =
|
||||
ViewAs<ScreenPixel>(aEvent.refPoint, PixelCastJustification::LayoutDeviceToScreenForUntransformedEvent);
|
||||
txn->OnMouseMove(point);
|
||||
return;
|
||||
}
|
||||
case NS_KEY_PRESS:
|
||||
case NS_KEY_UP:
|
||||
case NS_KEY_DOWN:
|
||||
case NS_MOUSE_BUTTON_UP:
|
||||
case NS_MOUSE_BUTTON_DOWN:
|
||||
case NS_MOUSE_DOUBLECLICK:
|
||||
case NS_MOUSE_CLICK:
|
||||
case NS_CONTEXTMENU:
|
||||
case NS_DRAGDROP_DROP:
|
||||
txn->EndTransaction();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
|
@ -814,6 +855,9 @@ APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent,
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsEventStatus result = nsEventStatus_eIgnore;
|
||||
|
||||
// Note, we call this before having transformed the reference point.
|
||||
UpdateWheelTransaction(aEvent);
|
||||
|
||||
// Transform the refPoint.
|
||||
// If the event hits an overscrolled APZC, instruct the caller to ignore it.
|
||||
HitTestResult hitResult = HitNothing;
|
||||
|
|
|
@ -436,6 +436,7 @@ private:
|
|||
nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent,
|
||||
ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId);
|
||||
void UpdateWheelTransaction(WidgetInputEvent& aEvent);
|
||||
void UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode,
|
||||
const ZoomConstraints& aConstraints);
|
||||
void FlushRepaintsRecursively(HitTestingTreeNode* aNode);
|
||||
|
|
|
@ -1767,6 +1767,19 @@ ParentLayerPoint AsyncPanZoomController::ToParentLayerCoordinates(const ScreenPo
|
|||
return TransformVector<ParentLayerPixel>(GetTransformToThis(), aVector, aAnchor);
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::Contains(const ScreenIntPoint& aPoint) const
|
||||
{
|
||||
Matrix4x4 transformToThis = GetTransformToThis();
|
||||
ParentLayerIntPoint point = TransformTo<ParentLayerPixel>(transformToThis, aPoint);
|
||||
|
||||
ParentLayerIntRect cb;
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
GetFrameMetrics().mCompositionBounds.ToIntRect(&cb);
|
||||
}
|
||||
return cb.Contains(point);
|
||||
}
|
||||
|
||||
ScreenCoord AsyncPanZoomController::PanDistance() const {
|
||||
ParentLayerPoint panVector;
|
||||
ParentLayerPoint panStart;
|
||||
|
|
|
@ -999,6 +999,10 @@ public:
|
|||
return mAncestorTransform;
|
||||
}
|
||||
|
||||
// Returns whether or not this apzc contains the given screen point within
|
||||
// its composition bounds.
|
||||
bool Contains(const ScreenIntPoint& aPoint) const;
|
||||
|
||||
bool IsOverscrolled() const {
|
||||
return mX.IsOverscrolled() || mY.IsOverscrolled();
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ CancelableBlockState::IsReadyForHandling() const
|
|||
}
|
||||
|
||||
void
|
||||
CancelableBlockState::DispatchImmediate(const InputData& aEvent)
|
||||
CancelableBlockState::DispatchImmediate(const InputData& aEvent) const
|
||||
{
|
||||
MOZ_ASSERT(!HasEvents());
|
||||
MOZ_ASSERT(GetTargetApzc());
|
||||
|
@ -158,7 +158,6 @@ WheelBlockState::WheelBlockState(const nsRefPtr<AsyncPanZoomController>& aTarget
|
|||
, mTransactionEnded(false)
|
||||
{
|
||||
sLastWheelBlockId = GetBlockId();
|
||||
Update(aInitialEvent);
|
||||
|
||||
if (aTargetConfirmed) {
|
||||
// Find the nearest APZC in the overscroll handoff chain that is scrollable.
|
||||
|
@ -167,7 +166,15 @@ WheelBlockState::WheelBlockState(const nsRefPtr<AsyncPanZoomController>& aTarget
|
|||
// that case.
|
||||
nsRefPtr<AsyncPanZoomController> apzc =
|
||||
mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
|
||||
if (apzc && apzc != GetTargetApzc()) {
|
||||
|
||||
// If nothing is scrollable, we don't consider this block as starting a
|
||||
// transaction.
|
||||
if (!apzc) {
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
if (apzc != GetTargetApzc()) {
|
||||
UpdateTargetApzc(apzc);
|
||||
}
|
||||
}
|
||||
|
@ -176,23 +183,36 @@ WheelBlockState::WheelBlockState(const nsRefPtr<AsyncPanZoomController>& aTarget
|
|||
void
|
||||
WheelBlockState::Update(const ScrollWheelInput& aEvent)
|
||||
{
|
||||
// We might not be in a transaction if the block never started in a
|
||||
// transaction - for example, if nothing was scrollable.
|
||||
if (!InTransaction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we can't scroll in the direction of the wheel event, we don't update
|
||||
// the last move time. This allows us to timeout a transaction even if the
|
||||
// mouse isn't moving.
|
||||
//
|
||||
// We skip this check if the target is not yet confirmed, so that when it is
|
||||
// confirmed, we don't timeout the transaction.
|
||||
nsRefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
|
||||
if (IsTargetConfirmed() && !apzc->CanScroll(aEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the time of the last known good event, and reset the mouse move
|
||||
// time to null. This will reset the delays on both the general transaction
|
||||
// timeout and the mouse-move-in-frame timeout.
|
||||
mLastEventTime = aEvent.mTimeStamp;
|
||||
mLastMouseMove = TimeStamp();
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::AddEvent(const ScrollWheelInput& aEvent)
|
||||
{
|
||||
Update(aEvent);
|
||||
mEvents.AppendElement(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::DispatchImmediate(const InputData& aEvent)
|
||||
{
|
||||
Update(aEvent.AsScrollWheelInput());
|
||||
CancelableBlockState::DispatchImmediate(aEvent);
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::IsReadyForHandling() const
|
||||
{
|
||||
|
@ -239,7 +259,7 @@ WheelBlockState::Type()
|
|||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::ShouldAcceptNewEvent(const ScrollWheelInput& aEvent) const
|
||||
WheelBlockState::ShouldAcceptNewEvent() const
|
||||
{
|
||||
if (!InTransaction()) {
|
||||
// If we're not in a transaction, start a new one.
|
||||
|
@ -250,17 +270,65 @@ WheelBlockState::ShouldAcceptNewEvent(const ScrollWheelInput& aEvent) const
|
|||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::MaybeTimeout(const ScrollWheelInput& aEvent)
|
||||
{
|
||||
if (MaybeTimeout(aEvent.mTimeStamp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mLastMouseMove.IsNull()) {
|
||||
// If there's a recent mouse movement, we can time out the transaction early.
|
||||
TimeDuration duration = TimeStamp::Now() - mLastMouseMove;
|
||||
if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
|
||||
TBS_LOG("%p wheel transaction timed out after mouse move\n", this);
|
||||
EndTransaction();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::MaybeTimeout(const TimeStamp& aTimeStamp)
|
||||
{
|
||||
// End the transaction if the event occurred > 1.5s after the most recently
|
||||
// seen wheel event.
|
||||
TimeDuration duration = aEvent.mTimeStamp - mLastEventTime;
|
||||
if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelTransactionTimeoutMs()) {
|
||||
TimeDuration duration = aTimeStamp - mLastEventTime;
|
||||
if (duration.ToMilliseconds() < gfxPrefs::MouseWheelTransactionTimeoutMs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accept the event, even if we can't scroll anymore.
|
||||
TBS_LOG("%p wheel transaction timed out\n", this);
|
||||
|
||||
EndTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WheelBlockState::OnMouseMove(const ScreenIntPoint& aPoint)
|
||||
{
|
||||
if (!GetTargetApzc()->Contains(aPoint)) {
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLastMouseMove.IsNull()) {
|
||||
// If the cursor is moving inside the frame, and it is more than the
|
||||
// ignoremovedelay time since the last scroll operation, we record
|
||||
// this as the most recent mouse movement.
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
TimeDuration duration = now - mLastEventTime;
|
||||
if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
|
||||
mLastMouseMove = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WheelBlockState::InTransaction() const
|
||||
{
|
||||
|
@ -283,6 +351,7 @@ WheelBlockState::AllowScrollHandoff() const
|
|||
void
|
||||
WheelBlockState::EndTransaction()
|
||||
{
|
||||
TBS_LOG("%p ending wheel transaction\n", this);
|
||||
mTransactionEnded = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ public:
|
|||
* This input block must not have pending events, and its apzc must not be
|
||||
* nullptr.
|
||||
*/
|
||||
virtual void DispatchImmediate(const InputData& aEvent);
|
||||
void DispatchImmediate(const InputData& aEvent) const;
|
||||
|
||||
/**
|
||||
* @return true iff this block has received all the information needed
|
||||
|
@ -167,7 +167,6 @@ public:
|
|||
void HandleEvents() override;
|
||||
bool MustStayActive() override;
|
||||
const char* Type() override;
|
||||
void DispatchImmediate(const InputData& aEvent) override;
|
||||
|
||||
void AddEvent(const ScrollWheelInput& aEvent);
|
||||
|
||||
|
@ -176,13 +175,21 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the block should accept new events. If the APZC
|
||||
* has been destroyed, or the block is not part of a wheel transaction, then
|
||||
* this will return false.
|
||||
*
|
||||
* @return True if the event should be accepted, false otherwise.
|
||||
* Determine whether this wheel block is accepting new events.
|
||||
*/
|
||||
bool ShouldAcceptNewEvent(const ScrollWheelInput& aEvent) const;
|
||||
bool ShouldAcceptNewEvent() const;
|
||||
|
||||
/**
|
||||
* Call to check whether a wheel event will cause the current transaction to
|
||||
* timeout.
|
||||
*/
|
||||
bool MaybeTimeout(const ScrollWheelInput& aEvent);
|
||||
|
||||
/**
|
||||
* Called from APZCTM when a mouse move or drag+drop event occurs, before
|
||||
* the event has been processed.
|
||||
*/
|
||||
void OnMouseMove(const ScreenIntPoint& aPoint);
|
||||
|
||||
/**
|
||||
* Returns whether or not the block is participating in a wheel transaction.
|
||||
|
@ -205,7 +212,13 @@ public:
|
|||
*/
|
||||
bool AllowScrollHandoff() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Called to check and possibly end the transaction due to a timeout.
|
||||
*
|
||||
* @return True if the transaction ended, false otherwise.
|
||||
*/
|
||||
bool MaybeTimeout(const TimeStamp& aTimeStamp);
|
||||
|
||||
/**
|
||||
* Update the wheel transaction state for a new event.
|
||||
*/
|
||||
|
@ -214,6 +227,7 @@ private:
|
|||
private:
|
||||
nsTArray<ScrollWheelInput> mEvents;
|
||||
TimeStamp mLastEventTime;
|
||||
TimeStamp mLastMouseMove;
|
||||
bool mTransactionEnded;
|
||||
};
|
||||
|
||||
|
|
|
@ -162,7 +162,10 @@ InputQueue::ReceiveScrollWheelInput(const nsRefPtr<AsyncPanZoomController>& aTar
|
|||
|
||||
// If the block is not accepting new events we'll create a new input block
|
||||
// (and therefore a new wheel transaction).
|
||||
if (block && !block->ShouldAcceptNewEvent(aEvent)) {
|
||||
if (block &&
|
||||
(!block->ShouldAcceptNewEvent() ||
|
||||
block->MaybeTimeout(aEvent)))
|
||||
{
|
||||
block = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +190,8 @@ InputQueue::ReceiveScrollWheelInput(const nsRefPtr<AsyncPanZoomController>& aTar
|
|||
*aOutInputBlockId = block->GetBlockId();
|
||||
}
|
||||
|
||||
block->Update(aEvent);
|
||||
|
||||
// Note that the |aTarget| the APZCTM sent us may contradict the confirmed
|
||||
// target set on the block. In this case the confirmed target (which may be
|
||||
// null) should take priority. This is equivalent to just always using the
|
||||
|
|
|
@ -337,6 +337,7 @@ private:
|
|||
// This affects whether events will be routed through APZ or not.
|
||||
DECL_GFX_PREF(Once, "mousewheel.system_scroll_override_on_root_content.enabled",
|
||||
MouseWheelHasScrollDeltaOverride, bool, false);
|
||||
DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
|
||||
DECL_GFX_PREF(Live, "mousewheel.transaction.timeout", MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);
|
||||
|
||||
|
||||
|
|
|
@ -30,7 +30,12 @@ enum class PixelCastJustification : uint8_t {
|
|||
// The transform that is usually used to convert between two coordinate
|
||||
// systems is not available (for example, because the object that stores it
|
||||
// is being destroyed), so fall back to the identity.
|
||||
TransformNotAvailable
|
||||
TransformNotAvailable,
|
||||
// When an OS event is initially constructed, its reference point is
|
||||
// technically in screen pixels, as it has not yet accounted for any
|
||||
// asynchronous transforms. This justification is for viewing the initial
|
||||
// reference point as a screen point.
|
||||
LayoutDeviceToScreenForUntransformedEvent
|
||||
};
|
||||
|
||||
template <class TargetUnits, class SourceUnits>
|
||||
|
@ -45,6 +50,10 @@ template <class TargetUnits, class SourceUnits>
|
|||
gfx::PointTyped<TargetUnits> ViewAs(const gfx::PointTyped<SourceUnits>& aPoint, PixelCastJustification) {
|
||||
return gfx::PointTyped<TargetUnits>(aPoint.x, aPoint.y);
|
||||
}
|
||||
template <class TargetUnits, class SourceUnits>
|
||||
gfx::IntPointTyped<TargetUnits> ViewAs(const gfx::IntPointTyped<SourceUnits>& aPoint, PixelCastJustification) {
|
||||
return gfx::IntPointTyped<TargetUnits>(aPoint.x, aPoint.y);
|
||||
}
|
||||
template <class NewTargetUnits, class OldTargetUnits, class SourceUnits>
|
||||
gfx::ScaleFactor<SourceUnits, NewTargetUnits> ViewTargetAs(
|
||||
const gfx::ScaleFactor<SourceUnits, OldTargetUnits>& aScaleFactor,
|
||||
|
|
Загрузка…
Ссылка в новой задаче