Merge "Fix bug 4111271 and bug 4077526 - WebView touch event handling when WebCore is too slow" into honeycomb-mr1
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 5521e92..d638e70 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -156,6 +156,7 @@
     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 @@
             reset(); // Start fresh
         }
 
+        if (mInvalidGesture) return false;
+
         if (!mGestureInProgress) {
             switch (action) {
             case MotionEvent.ACTION_DOWN: {
@@ -518,6 +521,15 @@
         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 @@
         mGestureInProgress = false;
         mActiveId0 = -1;
         mActiveId1 = -1;
+        mInvalidGesture = false;
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index e20dc81..01c73a8 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -7185,14 +7185,15 @@
     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 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 @@
                 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 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 @@
             }
         }
 
-        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 @@
                 }
             }
 
-            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 @@
         }
 
         private void handleQueuedMotionEvent(MotionEvent ev) {
+            mLastEventTime = ev.getEventTime();
             int action = ev.getActionMasked();
             if (ev.getPointerCount() > 1) {  // Multi-touch
                 handleMultiTouchInWebView(ev);
@@ -7381,6 +7402,9 @@
         }
 
         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) {