Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d0197f36 authored by Adam Powell's avatar Adam Powell
Browse files

Fix bug 4111271 and bug 4077526 - WebView touch event handling when

WebCore is too slow

Make sure that we can recover properly from a bad gesture with missing
events that never come back from webcore. Lower timeout to 1 second.

Confirm movement on touch event enqueue so that we don't get phantom
taps or long presses when webcore is slow to respond.

Add sanity check in ScaleGestureDetector to end a gesture early on a
bad MotionEvent stream rather than throwing up.

Change-Id: I69690409d7edd6b4320dbcf3b052aba4023360fe
parent f40e638e
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ public class ScaleGestureDetector {
    private float mRightSlopEdge;
    private float mBottomSlopEdge;
    private boolean mSloppyGesture;
    private boolean mInvalidGesture;

    // Pointer IDs currently responsible for the two fingers controlling the gesture
    private int mActiveId0;
@@ -177,6 +178,8 @@ public class ScaleGestureDetector {
            reset(); // Start fresh
        }

        if (mInvalidGesture) return false;

        if (!mGestureInProgress) {
            switch (action) {
            case MotionEvent.ACTION_DOWN: {
@@ -518,6 +521,15 @@ public class ScaleGestureDetector {
        final int currIndex0 = curr.findPointerIndex(mActiveId0);
        final int currIndex1 = curr.findPointerIndex(mActiveId1);

        if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) {
            mInvalidGesture = true;
            Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
            if (mGestureInProgress) {
                mListener.onScaleEnd(this);
            }
            return;
        }

        final float px0 = prev.getX(prevIndex0);
        final float py0 = prev.getY(prevIndex0);
        final float px1 = prev.getX(prevIndex1);
@@ -556,6 +568,7 @@ public class ScaleGestureDetector {
        mGestureInProgress = false;
        mActiveId0 = -1;
        mActiveId1 = -1;
        mInvalidGesture = false;
    }

    /**
+40 −16
Original line number Diff line number Diff line
@@ -7185,14 +7185,15 @@ public class WebView extends AbsoluteLayout
    private class TouchEventQueue {
        private long mNextTouchSequence = Long.MIN_VALUE + 1;
        private long mLastHandledTouchSequence = Long.MIN_VALUE;
        private long mIgnoreUntilSequence = Long.MIN_VALUE;
        private long mIgnoreUntilSequence = Long.MIN_VALUE + 1;
        private QueuedTouch mTouchEventQueue;
        private QueuedTouch mQueuedTouchRecycleBin;
        private int mQueuedTouchRecycleCount;
        private long mLastEventTime = Long.MAX_VALUE;
        private static final int MAX_RECYCLED_QUEUED_TOUCH = 15;

        // milliseconds until we abandon hope of getting all of a previous gesture
        private static final int QUEUED_GESTURE_TIMEOUT = 2000;
        private static final int QUEUED_GESTURE_TIMEOUT = 1000;

        private QueuedTouch obtainQueuedTouch() {
            if (mQueuedTouchRecycleBin != null) {
@@ -7226,7 +7227,7 @@ public class WebView extends AbsoluteLayout
        public void reset() {
            mNextTouchSequence = Long.MIN_VALUE + 1;
            mLastHandledTouchSequence = Long.MIN_VALUE;
            mIgnoreUntilSequence = Long.MIN_VALUE;
            mIgnoreUntilSequence = Long.MIN_VALUE + 1;
            while (mTouchEventQueue != null) {
                QueuedTouch recycleMe = mTouchEventQueue;
                mTouchEventQueue = mTouchEventQueue.mNext;
@@ -7260,7 +7261,9 @@ public class WebView extends AbsoluteLayout
                return;
            }

            dropStaleGestures(ted.mMotionEvent, ted.mSequence);
            if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) {
                return;
            }

            if (mLastHandledTouchSequence + 1 == ted.mSequence) {
                handleQueuedTouchEventData(ted);
@@ -7295,7 +7298,9 @@ public class WebView extends AbsoluteLayout
        public void enqueueTouchEvent(MotionEvent ev) {
            final long sequence = nextTouchSequence();

            dropStaleGestures(ev, sequence);
            if (dropStaleGestures(ev, sequence)) {
                return;
            }

            if (mLastHandledTouchSequence + 1 == sequence) {
                handleQueuedMotionEvent(ev);
@@ -7318,16 +7323,30 @@ public class WebView extends AbsoluteLayout
            }
        }

        private void dropStaleGestures(MotionEvent ev, long sequence) {
            if (mTouchEventQueue == null) return;
        private boolean dropStaleGestures(MotionEvent ev, long sequence) {
            if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && !mConfirmMove) {
                // This is to make sure that we don't attempt to process a tap
                // or long press when webkit takes too long to get back to us.
                // The movement will be properly confirmed when we process the
                // enqueued event later.
                final int dx = Math.round(ev.getX()) - mLastTouchX;
                final int dy = Math.round(ev.getY()) - mLastTouchY;
                if (dx * dx + dy * dy > mTouchSlopSquare) {
                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
                }
            }

            MotionEvent nextQueueEvent = mTouchEventQueue.mTed != null ?
                    mTouchEventQueue.mTed.mMotionEvent : mTouchEventQueue.mEvent;
            if (mTouchEventQueue == null) {
                return sequence <= mLastHandledTouchSequence;
            }

            if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN && nextQueueEvent != null) {
            // If we have a new down event and it's been a while since the last event
            // we saw, just reset and keep going.
            if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN) {
                long eventTime = ev.getEventTime();
                long nextQueueTime = nextQueueEvent.getEventTime();
                if (eventTime > nextQueueTime + QUEUED_GESTURE_TIMEOUT) {
                long lastHandledEventTime = mLastEventTime;
                if (eventTime > lastHandledEventTime + QUEUED_GESTURE_TIMEOUT) {
                    Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " +
                            "Ignoring previous queued events.");
                    QueuedTouch qd = mTouchEventQueue;
@@ -7341,17 +7360,18 @@ public class WebView extends AbsoluteLayout
                }
            }

            if (mIgnoreUntilSequence > mLastHandledTouchSequence) {
            if (mIgnoreUntilSequence - 1 > mLastHandledTouchSequence) {
                QueuedTouch qd = mTouchEventQueue;
                while (qd != null && qd.mSequence < mIgnoreUntilSequence &&
                        qd.mSequence < sequence) {
                    mLastHandledTouchSequence = qd.mSequence;
                while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
                    QueuedTouch recycleMe = qd;
                    qd = qd.mNext;
                    recycleQueuedTouch(recycleMe);
                }
                mTouchEventQueue = qd;
                mLastHandledTouchSequence = mIgnoreUntilSequence - 1;
            }

            return sequence <= mLastHandledTouchSequence;
        }

        private void handleQueuedTouch(QueuedTouch qt) {
@@ -7364,6 +7384,7 @@ public class WebView extends AbsoluteLayout
        }

        private void handleQueuedMotionEvent(MotionEvent ev) {
            mLastEventTime = ev.getEventTime();
            int action = ev.getActionMasked();
            if (ev.getPointerCount() > 1) {  // Multi-touch
                handleMultiTouchInWebView(ev);
@@ -7381,6 +7402,9 @@ public class WebView extends AbsoluteLayout
        }

        private void handleQueuedTouchEventData(TouchEventData ted) {
            if (ted.mMotionEvent != null) {
                mLastEventTime = ted.mMotionEvent.getEventTime();
            }
            if (!ted.mReprocess) {
                if (ted.mAction == MotionEvent.ACTION_DOWN
                        && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {