Merge "Inline HTML5 Video support" into honeycomb-mr1
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2960cbd..18f92a4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -6033,6 +6033,7 @@
         if (shouldForwardTouchEvent()) {
             if (removeEvents) {
                 mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
+                mTouchEventQueue.ignoreCurrentlyMissingEvents();
             }
             TouchEventData ted = new TouchEventData();
             ted.mIds = new int[1];
@@ -7171,11 +7172,15 @@
     private class TouchEventQueue {
         private long mNextTouchSequence = Long.MIN_VALUE + 1;
         private long mLastHandledTouchSequence = Long.MIN_VALUE;
+        private long mIgnoreUntilSequence = Long.MIN_VALUE;
         private QueuedTouch mTouchEventQueue;
         private QueuedTouch mQueuedTouchRecycleBin;
         private int mQueuedTouchRecycleCount;
         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 QueuedTouch obtainQueuedTouch() {
             if (mQueuedTouchRecycleBin != null) {
                 QueuedTouch result = mQueuedTouchRecycleBin;
@@ -7186,6 +7191,13 @@
             return new QueuedTouch();
         }
 
+        /**
+         * Allow events with any currently missing sequence numbers to be skipped in processing.
+         */
+        public void ignoreCurrentlyMissingEvents() {
+            mIgnoreUntilSequence = mNextTouchSequence;
+        }
+
         private void recycleQueuedTouch(QueuedTouch qd) {
             if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) {
                 qd.mNext = mQueuedTouchRecycleBin;
@@ -7201,6 +7213,7 @@
         public void reset() {
             mNextTouchSequence = Long.MIN_VALUE + 1;
             mLastHandledTouchSequence = Long.MIN_VALUE;
+            mIgnoreUntilSequence = Long.MIN_VALUE;
             while (mTouchEventQueue != null) {
                 QueuedTouch recycleMe = mTouchEventQueue;
                 mTouchEventQueue = mTouchEventQueue.mNext;
@@ -7227,6 +7240,15 @@
          * @param ted Touch data to be processed in order.
          */
         public void enqueueTouchEvent(TouchEventData ted) {
+            if (ted.mSequence < mLastHandledTouchSequence) {
+                // Stale event and we already moved on; drop it. (Should not be common.)
+                Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) +
+                        " received from webcore; ignoring");
+                return;
+            }
+
+            dropStaleGestures(ted.mMotionEvent, ted.mSequence);
+
             if (mLastHandledTouchSequence + 1 == ted.mSequence) {
                 handleQueuedTouchEventData(ted);
 
@@ -7259,6 +7281,9 @@
          */
         public void enqueueTouchEvent(MotionEvent ev) {
             final long sequence = nextTouchSequence();
+
+            dropStaleGestures(ev, sequence);
+
             if (mLastHandledTouchSequence + 1 == sequence) {
                 handleQueuedMotionEvent(ev);
 
@@ -7280,6 +7305,40 @@
             }
         }
 
+        private void dropStaleGestures(MotionEvent ev, long sequence) {
+            if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN &&
+                    mTouchEventQueue != null) {
+                long eventTime = ev.getEventTime();
+                long nextQueueTime = mTouchEventQueue.mTed != null ?
+                        mTouchEventQueue.mTed.mMotionEvent.getEventTime() :
+                        mTouchEventQueue.mEvent.getEventTime();
+                if (eventTime > nextQueueTime + QUEUED_GESTURE_TIMEOUT) {
+                    Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " +
+                            "Ignoring previous queued events.");
+                    QueuedTouch qd = mTouchEventQueue;
+                    while (qd != null && qd.mSequence < sequence) {
+                        QueuedTouch recycleMe = qd;
+                        qd = qd.mNext;
+                        recycleQueuedTouch(recycleMe);
+                    }
+                    mTouchEventQueue = qd;
+                    mLastHandledTouchSequence = sequence - 1;
+                }
+            }
+
+            if (mIgnoreUntilSequence > mLastHandledTouchSequence) {
+                QueuedTouch qd = mTouchEventQueue;
+                while (qd != null && qd.mSequence < mIgnoreUntilSequence &&
+                        qd.mSequence < sequence) {
+                    mLastHandledTouchSequence = qd.mSequence;
+                    QueuedTouch recycleMe = qd;
+                    qd = qd.mNext;
+                    recycleQueuedTouch(recycleMe);
+                }
+                mTouchEventQueue = qd;
+            }
+        }
+
         private void handleQueuedTouch(QueuedTouch qt) {
             if (qt.mTed != null) {
                 handleQueuedTouchEventData(qt.mTed);