Bug 1416316 - 3. Update NPZC usages; r?rbarker draft
authorJim Chen <nchen@mozilla.com>
Thu, 30 Nov 2017 13:25:50 -0500
changeset 705812 3ec173cee30ffdbedf6ca408cb8f4ff52f8a3974
parent 705811 0e3da7bfd2f6b3e64c5cc1f0ba659cadc7ba5aa7
child 705813 6121d4cde87acd9025ccca28fbbc2cb814d53b34
push id91595
push userbmo:nchen@mozilla.com
push dateThu, 30 Nov 2017 18:26:05 +0000
reviewersrbarker
bugs1416316
milestone59.0a1
Bug 1416316 - 3. Update NPZC usages; r?rbarker Remove NPZC references from LayerView, and add an NPZC getter in LayerSession. Use the new APIs in GeckoView to forward events to NPZC. MozReview-Commit-ID: 1UcJkpW0XuM
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java
widget/android/nsWindow.cpp
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -15,29 +15,36 @@ import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.InputDevice;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 
 public class GeckoView extends LayerView {
     private static final String LOGTAG = "GeckoView";
     private static final boolean DEBUG = false;
 
-    private final Display mDisplay = new Display();
+    private static AccessibilityManager sAccessibilityManager;
+
+    protected final Display mDisplay = new Display();
     protected GeckoSession mSession;
     private boolean mStateSaved;
 
     protected SurfaceView mSurfaceView;
 
     private InputConnectionListener mInputConnectionListener;
     private boolean mIsResettingFocus;
 
@@ -145,18 +152,16 @@ public class GeckoView extends LayerView
     }
 
     public GeckoView(final Context context, final AttributeSet attrs) {
         super(context, attrs);
         init();
     }
 
     private void init() {
-        initializeView();
-
         setFocusable(true);
         setFocusableInTouchMode(true);
 
         // We are adding descendants to this LayerView, but we don't want the
         // descendants to affect the way LayerView retains its focus.
         setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
 
         // This will stop PropertyAnimator from creating a drawing cache (i.e. a
@@ -200,16 +205,25 @@ public class GeckoView extends LayerView
                 if (Build.VERSION.SDK_INT >= 16) {
                     GeckoView.this.postInvalidateOnAnimation();
                 } else {
                     GeckoView.this.postInvalidateDelayed(10);
                 }
             }
         });
 
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final TypedValue outValue = new TypedValue();
+        if (context.getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight,
+                                                outValue, true)) {
+            session.getPanZoomController().setScrollFactor(outValue.getDimension(metrics));
+        } else {
+            session.getPanZoomController().setScrollFactor(0.075f * metrics.densityDpi);
+        }
+
         mSession = session;
     }
 
     public GeckoSession getSession() {
         return mSession;
     }
 
     public EventDispatcher getEventDispatcher() {
@@ -237,17 +251,16 @@ public class GeckoView extends LayerView
         attachCompositor(mSession);
 
         super.onAttachedToWindow();
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        super.destroy();
 
         if (mStateSaved) {
             // If we saved state earlier, we don't want to close the window.
             return;
         }
 
         if (mSession != null && mSession.isOpen()) {
             mSession.closeWindow();
@@ -406,9 +419,53 @@ public class GeckoView extends LayerView
     @Override
     public void dispatchDraw(final Canvas canvas) {
         super.dispatchDraw(canvas);
 
         if (mSession != null) {
             mSession.getOverscrollEdgeEffect().draw(canvas);
         }
     }
+
+    @Override
+    public boolean onTouchEvent(final MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            requestFocus();
+        }
+
+        // NOTE: Treat mouse events as "touch" rather than as "mouse", so mouse can be
+        // used to pan/zoom. Call onMouseEvent() instead for behavior similar to desktop.
+        return mSession != null &&
+               mSession.getPanZoomController().onTouchEvent(event);
+    }
+
+    protected static boolean isAccessibilityEnabled(final Context context) {
+        if (sAccessibilityManager == null) {
+            sAccessibilityManager = (AccessibilityManager)
+                    context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        }
+        return sAccessibilityManager.isEnabled() &&
+               sAccessibilityManager.isTouchExplorationEnabled();
+    }
+
+    @Override
+    public boolean onHoverEvent(final MotionEvent event) {
+        // If we get a touchscreen hover event, and accessibility is not enabled, don't
+        // send it to Gecko.
+        if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
+            !isAccessibilityEnabled(getContext())) {
+            return false;
+        }
+
+        return mSession != null &&
+               mSession.getPanZoomController().onMotionEvent(event);
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(final MotionEvent event) {
+        if (AndroidGamepadManager.handleMotionEvent(event)) {
+            return true;
+        }
+
+        return mSession != null &&
+               mSession.getPanZoomController().onMotionEvent(event);
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
@@ -80,17 +80,17 @@ public class LayerSession {
             LayerSession.this.onCompositorDetached();
             disposeNative();
         }
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
         @Override protected native void disposeNative();
 
         @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
-        public native void attachToJava(NativePanZoomController npzc);
+        public native void attachNPZC(NativePanZoomController npzc);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
         public native void onBoundsChanged(int left, int top, int width, int height);
 
         // Gecko thread creates compositor; blocks UI thread.
         @WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
         public native void createCompositor(int width, int height, Object surface);
 
@@ -162,16 +162,17 @@ public class LayerSession {
             LayerSession.this.onSelectionCaretDrag(dragging);
         }
     }
 
     protected final Compositor mCompositor = new Compositor();
 
     // All fields are accessed on UI thread only.
     private GeckoDisplay mDisplay;
+    private NativePanZoomController mNPZC;
     private OverscrollEdgeEffect mOverscroll;
     private DynamicToolbarAnimator mToolbar;
 
     private boolean mAttachedCompositor;
     private boolean mCalledCreateCompositor;
     private boolean mCompositorReady;
     private Surface mSurface;
 
@@ -189,16 +190,33 @@ public class LayerSession {
     /* package */ GeckoDisplay getDisplay() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
         return mDisplay;
     }
 
     /**
+     * Get the NativePanZoomController instance for this session.
+     *
+     * @return NativePanZoomController instance.
+     */
+    public NativePanZoomController getPanZoomController() {
+        ThreadUtils.assertOnUiThread();
+
+        if (mNPZC == null) {
+            mNPZC = new NativePanZoomController(this);
+            if (mAttachedCompositor) {
+                mCompositor.attachNPZC(mNPZC);
+            }
+        }
+        return mNPZC;
+    }
+
+    /**
      * Get the OverscrollEdgeEffect instance for this session.
      *
      * @return OverscrollEdgeEffect instance.
      */
     public OverscrollEdgeEffect getOverscrollEdgeEffect() {
         ThreadUtils.assertOnUiThread();
 
         if (mOverscroll == null) {
@@ -315,16 +333,20 @@ public class LayerSession {
 
     /* package */ void onCompositorAttached() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         mAttachedCompositor = true;
 
+        if (mNPZC != null) {
+            mCompositor.attachNPZC(mNPZC);
+        }
+
         if (mSurface != null) {
             // If we have a valid surface, create the compositor now that we're attached.
             // Leave mSurface alone because we'll need it later for onCompositorReady.
             onSurfaceChanged(mSurface, mWidth, mHeight);
         }
     }
 
     /* package */ void onCompositorDetached() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -30,31 +30,27 @@ import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.InputDevice;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * A view rendered by the layer compositor.
  */
 public class LayerView extends FrameLayout {
     private static final String LOGTAG = "GeckoLayerView";
 
-    private static AccessibilityManager sAccessibilityManager;
-
-    private PanZoomController mPanZoomController;
     private FullScreenState mFullScreenState;
 
     private int mDefaultClearColor = Color.WHITE;
     /* package */ GetPixelsResult mGetPixelsResult;
     private final List<DrawListener> mDrawListeners;
 
     private void postCompositorMessage(final int message) {
         ThreadUtils.postToUiThread(new Runnable() {
@@ -85,110 +81,21 @@ public class LayerView extends FrameLayo
 
         mDrawListeners = new ArrayList<DrawListener>();
     }
 
     public LayerView(Context context) {
         this(context, null);
     }
 
-    public void initializeView() {
-        mPanZoomController = PanZoomController.Factory.create(this);
-    }
-
-    /**
-     * MotionEventHelper dragAsync() robocop tests can instruct
-     * PanZoomController not to generate longpress events.
-     * This call comes in from a thread other than the UI thread.
-     * So dispatch to UI thread first to prevent assert in nsWindow.
-     */
-    public void setIsLongpressEnabled(final boolean isLongpressEnabled) {
-        ThreadUtils.postToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mPanZoomController.setIsLongpressEnabled(isLongpressEnabled);
-            }
-        });
-    }
-
     private static Point getEventRadius(MotionEvent event) {
         return new Point((int)event.getToolMajor() / 2,
                          (int)event.getToolMinor() / 2);
     }
 
-    protected void destroy() {
-        if (mPanZoomController != null) {
-            mPanZoomController.destroy();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            requestFocus();
-        }
-
-        if (!isCompositorReady()) {
-            // If gecko isn't loaded yet, don't try sending events to the
-            // native code because it's just going to crash
-            return true;
-        }
-        if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean isAccessibilityEnabled() {
-        if (sAccessibilityManager == null) {
-            sAccessibilityManager = (AccessibilityManager)
-                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        }
-        return sAccessibilityManager.isEnabled() &&
-               sAccessibilityManager.isTouchExplorationEnabled();
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        // If we get a touchscreen hover event, and accessibility is not enabled,
-        // don't send it to gecko.
-        if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
-            !isAccessibilityEnabled()) {
-            return false;
-        }
-
-        if (!isCompositorReady()) {
-            // If gecko isn't loaded yet, don't try sending events to the
-            // native code because it's just going to crash
-            return true;
-        } else if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if (AndroidGamepadManager.handleMotionEvent(event)) {
-            return true;
-        }
-        if (!isCompositorReady()) {
-            // If gecko isn't loaded yet, don't try sending events to the
-            // native code because it's just going to crash
-            return true;
-        }
-        if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
-            return true;
-        }
-        return false;
-    }
-
-    public PanZoomController getPanZoomController() { return mPanZoomController; }
-
     public void setSurfaceBackgroundColor(int newColor) {
     }
 
     public interface GetPixelsResult {
         public void onPixelsResult(int width, int height, IntBuffer pixels);
     }
 
     @RobocopTarget
@@ -217,26 +124,16 @@ public class LayerView extends FrameLayo
             mGetPixelsResult = null;
         }
     }
 
     protected void attachCompositor(final LayerSession session) {
         mSession = session;
         mCompositor = session.mCompositor;
         mCompositor.layerView = this;
-
-        final NativePanZoomController npzc = (NativePanZoomController) mPanZoomController;
-
-        if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            mCompositor.attachToJava(npzc);
-        } else {
-            GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
-                    mCompositor, "attachToJava",
-                    NativePanZoomController.class, npzc);
-        }
     }
 
     @WrapForJNI(calledFrom = "ui")
     private Object getCompositor() {
         return isCompositorReady() ? mCompositor : null;
     }
 
     @RobocopTarget
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java
@@ -1,45 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
 import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoView;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.R;
-import org.mozilla.gecko.gfx.LayerView;
 
 import com.robotium.solo.Solo;
 
 import android.app.Instrumentation;
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.MotionEvent;
 
 class MotionEventHelper {
     private static final String LOGTAG = "RobocopMotionEventHelper";
 
     private static final long DRAG_EVENTS_PER_SECOND = 20; // 20 move events per second when doing a drag
 
     private final Instrumentation mInstrumentation;
     private final int mSurfaceOffsetX;
     private final int mSurfaceOffsetY;
-    private final LayerView layerView;
+    private final GeckoView layerView;
     private boolean mApzEnabled;
     private float mTouchStartTolerance;
     private final int mDpi;
 
     public MotionEventHelper(Instrumentation inst, Solo solo,
                              int surfaceOffsetX, int surfaceOffsetY) {
         mInstrumentation = inst;
         mSurfaceOffsetX = surfaceOffsetX;
         mSurfaceOffsetY = surfaceOffsetY;
-        layerView = (LayerView) solo.getCurrentActivity().findViewById(R.id.layer_view);
+        layerView = (GeckoView) solo.getCurrentActivity().findViewById(R.id.layer_view);
         mApzEnabled = false;
         mTouchStartTolerance = 0.0f;
         mDpi = GeckoAppShell.getDpi();
         Log.i(LOGTAG, "Initialized using offset (" + mSurfaceOffsetX + "," + mSurfaceOffsetY + ")");
         PrefsHelper.getPref("layers.async-pan-zoom.enabled", new PrefsHelper.PrefHandlerBase() {
             @Override public void prefValue(String pref, boolean value) {
                 mApzEnabled = value;
             }
@@ -118,17 +118,17 @@ class MotionEventHelper {
         return downTime;
     }
 
     public Thread dragAsync(final float startX, final float startY, final float endX, final float endY, final long durationMillis) {
         Thread t = new Thread() {
             @Override
             public void run() {
                 if (layerView != null) {
-                    layerView.setIsLongpressEnabled(false);
+                    layerView.getSession().getPanZoomController().setIsLongpressEnabled(false);
                 }
 
                 int numEvents = (int)(durationMillis * DRAG_EVENTS_PER_SECOND / 1000);
                 float eventDx = (endX - startX) / numEvents;
                 float eventDy = (endY - startY) / numEvents;
                 long downTime = movePastTouchStartTolerance(startX, startY, endX, endY);
                 for (int i = 0; i < numEvents - 1; i++) {
                     downTime = move(downTime, startX + (eventDx * i), startY + (eventDy * i));
@@ -145,17 +145,17 @@ class MotionEventHelper {
                 } catch (InterruptedException ie) {
                     ie.printStackTrace();
                 }
                 // do the last one using endX/endY directly to avoid rounding errors
                 downTime = move(downTime, endX, endY);
                 downTime = up(downTime, endX, endY);
 
                 if (layerView != null) {
-                    layerView.setIsLongpressEnabled(true);
+                    layerView.getSession().getPanZoomController().setIsLongpressEnabled(true);
                 }
             }
         };
         t.start();
         return t;
     }
 
     public void dragSync(float startX, float startY, float endX, float endY, long durationMillis) {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -892,17 +892,17 @@ private:
         }
         return child.forget();
     }
 
     /**
      * Compositor methods
      */
 public:
-    void AttachToJava(jni::Object::Param aNPZC)
+    void AttachNPZC(jni::Object::Param aNPZC)
     {
         MOZ_ASSERT(NS_IsMainThread());
         if (!mWindow) {
             return; // Already shut down.
         }
 
         MOZ_ASSERT(aNPZC);
         MOZ_ASSERT(!mWindow->mNPZCSupport);
@@ -911,31 +911,16 @@ public:
                 jni::GetGeckoThreadEnv(),
                 NativePanZoomController::Ref::From(aNPZC));
         mWindow->mNPZCSupport.Attach(npzc, mWindow, npzc);
 
         DispatchToUiThread("LayerViewSupport::AttachNPZC",
                            [npzc = NativePanZoomController::GlobalRef(npzc)] {
                                 npzc->SetAttached(true);
                            });
-
-        if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
-            LayerSession::Compositor::GlobalRef compositor(mCompositor);
-            uiThread->Dispatch(NS_NewRunnableFunction(
-                    "LayerViewSupport::AttachToJava",
-                    [compositor] {
-                        compositor->OnCompositorAttached();
-                    }));
-        }
-
-        // Set the first-paint flag so that we (re-)link any new Java objects
-        // to Gecko, co-ordinate viewports, etc.
-        if (RefPtr<CompositorBridgeChild> bridge = mWindow->GetCompositorBridgeChild()) {
-            bridge->SendForceIsFirstPaint();
-        }
     }
 
     void OnBoundsChanged(int32_t aLeft, int32_t aTop,
                          int32_t aWidth, int32_t aHeight)
     {
         MOZ_ASSERT(NS_IsMainThread());
         if (!mWindow) {
             return; // Already shut down.
@@ -1372,23 +1357,33 @@ nsWindow::GeckoViewSupport::Transfer(con
     if (window.mLayerViewSupport) {
         window.mLayerViewSupport.Detach();
     }
 
     auto compositor = LayerSession::Compositor::LocalRef(
             inst.Env(), LayerSession::Compositor::Ref::From(aCompositor));
     window.mLayerViewSupport.Attach(compositor, &window, compositor);
 
-    if (window.mAndroidView) {
-        window.mAndroidView->mEventDispatcher->Attach(
-                java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
-        window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
-    }
+    MOZ_ASSERT(window.mAndroidView);
+    window.mAndroidView->mEventDispatcher->Attach(
+            java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
+    window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
 
     inst->OnTransfer(aDispatcher);
+
+    DispatchToUiThread(
+            "GeckoViewSupport::Transfer",
+            [compositor = LayerSession::Compositor::GlobalRef(compositor)] {
+                compositor->OnCompositorAttached();
+            });
+
+    // Set the first-paint flag so that we refresh viewports, etc.
+    if (RefPtr<CompositorBridgeChild> bridge = window.GetCompositorBridgeChild()) {
+        bridge->SendForceIsFirstPaint();
+    }
 }
 
 void
 nsWindow::GeckoViewSupport::Attach(const GeckoSession::Window::LocalRef& inst,
                                    jni::Object::Param aView)
 {
     // Associate our previous GeckoEditable with the new GeckoView.
     MOZ_ASSERT(window.mEditable);