Bug 1441982 - Queue input events while waiting for APZ to attach r=jchen
authorJames Willcox <snorp@snorp.net>
Tue, 27 Feb 2018 18:13:25 -0500
changeset 461860 64a19db957b2291978c899a41eacf6234b0dc746
parent 461859 b71f6a4e18cb33cc6490963b75ff515533d67cd9
child 461861 f8bc3fd98796c2aa205e90001eb75b3c90b160c3
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen
bugs1441982
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1441982 - Queue input events while waiting for APZ to attach r=jchen This is necessary to avoid races when sending input immediately after opening a GeckoSession MozReview-Commit-ID: HWkmxTmYJHa
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -4,36 +4,41 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.ThreadUtils;
 
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.util.Log;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.InputDevice;
 
 import java.util.ArrayList;
 
 public final class NativePanZoomController extends JNIObject {
     private static final String LOGTAG = "GeckoNPZC";
+    private static final int EVENT_SOURCE_SCROLL = 0;
+    private static final int EVENT_SOURCE_MOTION = 1;
+    private static final int EVENT_SOURCE_MOUSE = 2;
 
     private final LayerSession mSession;
     private final Rect mTempRect = new Rect();
     private boolean mAttached;
     private float mPointerScrollFactor = 64.0f;
     private long mLastDownTime;
 
     private SynthesizedEventState mPointerState;
 
+    private ArrayList<Pair<Integer, MotionEvent>> mQueuedEvents;
+
     @WrapForJNI(calledFrom = "ui")
     private native boolean handleMotionEvent(
             int action, int actionIndex, long time, int metaState,
             int pointerId[], float x[], float y[], float orientation[], float pressure[],
             float toolMajor[], float toolMinor[]);
 
     @WrapForJNI(calledFrom = "ui")
     private native boolean handleScrollEvent(
@@ -43,16 +48,17 @@ public final class NativePanZoomControll
 
     @WrapForJNI(calledFrom = "ui")
     private native boolean handleMouseEvent(
             int action, long time, int metaState,
             float x, float y, int buttons);
 
     private boolean handleMotionEvent(MotionEvent event) {
         if (!mAttached) {
+            mQueuedEvents.add(new Pair(EVENT_SOURCE_MOTION, event));
             return false;
         }
 
         final int action = event.getActionMasked();
         final int count = event.getPointerCount();
 
         if (action == MotionEvent.ACTION_DOWN) {
             mLastDownTime = event.getDownTime();
@@ -87,16 +93,17 @@ public final class NativePanZoomControll
 
         return handleMotionEvent(action, event.getActionIndex(), event.getEventTime(),
                 event.getMetaState(), pointerId, x, y, orientation, pressure,
                 toolMajor, toolMinor);
     }
 
     private boolean handleScrollEvent(MotionEvent event) {
         if (!mAttached) {
+            mQueuedEvents.add(new Pair(EVENT_SOURCE_SCROLL, event));
             return false;
         }
 
         final int count = event.getPointerCount();
 
         if (count <= 0) {
             return false;
         }
@@ -115,16 +122,17 @@ public final class NativePanZoomControll
                               mPointerScrollFactor;
 
         return handleScrollEvent(event.getEventTime(), event.getMetaState(), x, y,
                                  hScroll, vScroll);
     }
 
     private boolean handleMouseEvent(MotionEvent event) {
         if (!mAttached) {
+            mQueuedEvents.add(new Pair(EVENT_SOURCE_MOUSE, event));
             return false;
         }
 
         final int count = event.getPointerCount();
 
         if (count <= 0) {
             return false;
         }
@@ -138,16 +146,17 @@ public final class NativePanZoomControll
         final float y = coords.y - mTempRect.top;
 
         return handleMouseEvent(event.getActionMasked(), event.getEventTime(),
                                 event.getMetaState(), x, y, event.getButtonState());
     }
 
     /* package */ NativePanZoomController(final LayerSession session) {
         mSession = session;
+        enableEventQueue();
     }
 
     /**
      * Set the current scroll factor. The scroll factor is the maximum scroll amount that
      * one scroll event may generate, in device pixels.
      *
      * @param factor Scroll factor.
      */
@@ -220,23 +229,54 @@ public final class NativePanZoomControll
                    (action == MotionEvent.ACTION_HOVER_ENTER) ||
                    (action == MotionEvent.ACTION_HOVER_EXIT)) {
             return handleMouseEvent(event);
         } else {
             return false;
         }
     }
 
+    private void enableEventQueue() {
+        if (mQueuedEvents != null) {
+            throw new IllegalStateException("Already have an event queue");
+        }
+        mQueuedEvents = new ArrayList<>();
+    }
+
+    private void flushEventQueue() {
+        if (mQueuedEvents == null) {
+            return;
+        }
+
+        ArrayList<Pair<Integer, MotionEvent>> events = mQueuedEvents;
+        mQueuedEvents = null;
+        for (Pair<Integer, MotionEvent> pair : events) {
+            switch (pair.first) {
+                case EVENT_SOURCE_MOTION:
+                    handleMotionEvent(pair.second);
+                    break;
+                case EVENT_SOURCE_SCROLL:
+                    handleScrollEvent(pair.second);
+                    break;
+                case EVENT_SOURCE_MOUSE:
+                    handleMouseEvent(pair.second);
+                    break;
+            }
+        }
+    }
+
     @WrapForJNI(calledFrom = "ui")
     private void setAttached(final boolean attached) {
         if (attached) {
             mAttached = true;
+            flushEventQueue();
         } else if (mAttached) {
             mAttached = false;
             disposeNative();
+            enableEventQueue();
         }
     }
 
     @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") @Override // JNIObject
     protected native void disposeNative();
 
     @WrapForJNI(stubName = "SetIsLongpressEnabled") // Called from test thread.
     private native void nativeSetIsLongpressEnabled(boolean isLongpressEnabled);
@@ -454,17 +494,17 @@ public final class NativePanZoomControll
             eventType == MotionEvent.ACTION_HOVER_MOVE)
         {
             mPointerState.pointers.remove(pointerIndex);
         }
     }
 
     @WrapForJNI(calledFrom = "ui")
     private void synthesizeNativeTouchPoint(int pointerId, int eventType, int clientX,
-                                           int clientY, double pressure, int orientation) {
+                                            int clientY, double pressure, int orientation) {
         if (pointerId == PointerInfo.RESERVED_MOUSE_POINTER_ID) {
             throw new IllegalArgumentException("Pointer ID reserved for mouse");
         }
         synthesizeNativePointer(InputDevice.SOURCE_TOUCHSCREEN, pointerId,
                                 eventType, clientX, clientY, pressure, orientation);
     }
 
     @WrapForJNI(calledFrom = "ui")