Bug 745250 - Prevent crash from NPE in TouchEventHandler. r=wesj
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 06 Jun 2012 13:50:01 -0400
changeset 98715 eaa56496dcf3340a18a052da989022eb65f60a41
parent 98714 d6ec24d6185819dc8244e318fd6bc37b3daaa1ff
child 98716 2b0ec0df160ca6c61592f9c113f3ad05fe524baf
push id1729
push userlsblakk@mozilla.com
push dateMon, 16 Jul 2012 20:02:43 +0000
treeherdermozilla-aurora@f4e75e148951 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswesj
bugs745250
milestone16.0a1
Bug 745250 - Prevent crash from NPE in TouchEventHandler. r=wesj
mobile/android/base/gfx/TouchEventHandler.java
--- a/mobile/android/base/gfx/TouchEventHandler.java
+++ b/mobile/android/base/gfx/TouchEventHandler.java
@@ -160,26 +160,27 @@ public final class TouchEventHandler imp
             mDispatchEvents = true;
             if (mHoldInQueue) {
                 // if the new block we are starting is the current block (i.e. there are no
                 // other blocks waiting in the queue, then we should let the pan/zoom controller
                 // know we are waiting for the touch listeners to run
                 if (mEventQueue.isEmpty()) {
                     mPanZoomController.waitingForTouchListeners(event);
                 }
-                // if we're holding the events in the queue, set the timeout so that
-                // we dispatch these events if we don't get a default-prevented notification
-                mView.postDelayed(mListenerTimeoutProcessor, EVENT_LISTENER_TIMEOUT);
             } else {
-                // if we're not holding these events, then we still need to pretend like
-                // we did and had a ListenerTimeoutProcessor fire so that when we get
-                // the default-prevented notification for this block, it doesn't accidentally
-                // act upon some other block
-                mProcessingBalance++;
+                // we're not going to be holding this block of events in the queue, but we need
+                // a marker of some sort so that the processEventBlock loop deals with the blocks
+                // in the right order as notifications come in. we use a single null event in
+                // the queue as a placeholder for a block of events that has already been dispatched.
+                mEventQueue.add(null);
             }
+
+            // set the timeout so that we dispatch these events and update mProcessingBalance
+            // if we don't get a default-prevented notification
+            mView.postDelayed(mListenerTimeoutProcessor, EVENT_LISTENER_TIMEOUT);
         }
 
         // if we need to hold the events, add it to the queue. if we need to dispatch
         // it directly, do that. it is possible that both mHoldInQueue and mDispatchEvents
         // are false, in which case we are processing a block of events that we know
         // has been default-prevented. in that case we don't keep the events as we don't
         // need them (but we still pass them to the gecko listener).
         if (mHoldInQueue) {
@@ -271,45 +272,57 @@ public final class TouchEventHandler imp
     private void processEventBlock(boolean allowDefaultAction) {
         if (!allowDefaultAction) {
             // if the block has been default-prevented, cancel whatever stuff we had in
             // progress in the gesture detector and pan zoom controller
             long now = SystemClock.uptimeMillis();
             dispatchEvent(MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0));
         }
 
+        if (mEventQueue.isEmpty()) {
+            Log.e(LOGTAG, "Unexpected empty event queue in processEventBlock!", new Exception());
+            return;
+        }
+
         // the odd loop condition is because the first event in the queue will
         // always be a DOWN or POINTER_DOWN event, and we want to process all
         // the events in the queue starting at that one, up to but not including
         // the next DOWN or POINTER_DOWN event.
 
         MotionEvent event = mEventQueue.poll();
         while (true) {
-            // for each event we process, only dispatch it if the block hasn't been
-            // default-prevented.
-            if (allowDefaultAction) {
-                dispatchEvent(event);
-            } else if (touchFinished(event)) {
-                mPanZoomController.preventedTouchFinished();
+            // event being null here is valid and represents a block of events
+            // that has already been dispatched.
+
+            if (event != null) {
+                // for each event we process, only dispatch it if the block hasn't been
+                // default-prevented.
+                if (allowDefaultAction) {
+                    dispatchEvent(event);
+                } else if (touchFinished(event)) {
+                    mPanZoomController.preventedTouchFinished();
+                }
             }
-            event = mEventQueue.peek();
-            if (event == null) {
+            if (mEventQueue.isEmpty()) {
                 // we have processed the backlog of events, and are all caught up.
                 // now we can set clear the hold flag and set the dispatch flag so
                 // that the handleEvent() function can do the right thing for all
                 // remaining events in this block (which is still ongoing) without
                 // having to put them in the queue.
                 mHoldInQueue = false;
                 mDispatchEvents = allowDefaultAction;
                 break;
             }
-            if (isDownEvent(event)) {
+            event = mEventQueue.peek();
+            if (event == null || isDownEvent(event)) {
                 // we have finished processing the block we were interested in.
                 // now we wait for the next call to processEventBlock
-                mPanZoomController.waitingForTouchListeners(event);
+                if (event != null) {
+                    mPanZoomController.waitingForTouchListeners(event);
+                }
                 break;
             }
             // pop the event we peeked above, as it is still part of the block and
             // we want to keep processing
             mEventQueue.remove();
         }
     }