Bug 1415994 - 6b. Track GeckoDisplay origin changes; r=snorp
authorJim Chen <nchen@mozilla.com>
Tue, 14 Nov 2017 18:18:35 -0500
changeset 443559 f0e9749cfce5b15fa56acff72178901c2c3c36d8
parent 443558 44d0e1e39f89f44767b07f480f620cd1daf207f1
child 443560 e90da05fbff4d1e741845f70b65dce2ba955b89a
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1415994
milestone59.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 1415994 - 6b. Track GeckoDisplay origin changes; r=snorp Add a `screenOriginChanged` callback to GeckoDisplay.Listener, which informs Gecko of changes in the origin of the display. The origin translates to coordinates for web APIs like screenX/screenY and certain other calculations. Also, make GeckoDisplay listen to layout changes in the view tree (by overriding gatherTransparentRegion as an optimization), and call `screenOriginChanged` accordingly. MozReview-Commit-ID: C72EHCkbV3T
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.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
@@ -7,16 +7,17 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.GeckoDisplay;
 import org.mozilla.gecko.gfx.LayerView;
 
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
@@ -66,16 +67,18 @@ public class GeckoView extends LayerView
             public SavedState[] newArray(final int size) {
                 return new SavedState[size];
             }
         };
     }
 
     private class Display implements GeckoDisplay,
                                      SurfaceHolder.Callback {
+        private final int[] mOrigin = new int[2];
+
         private Listener mListener;
         private boolean mValid;
 
         @Override // GeckoDisplay
         public Listener getListener() {
             return mListener;
         }
 
@@ -88,16 +91,17 @@ public class GeckoView extends LayerView
 
             mListener = listener;
 
             if (!mValid || listener == null) {
                 return;
             }
 
             // Tell new listener there is already a surface.
+            onGlobalLayout();
             if (GeckoView.this.mSurfaceView != null) {
                 final SurfaceHolder holder = GeckoView.this.mSurfaceView.getHolder();
                 final Rect frame = holder.getSurfaceFrame();
                 listener.surfaceChanged(holder.getSurface(), frame.right, frame.bottom);
             }
         }
 
         @Override // SurfaceHolder.Callback
@@ -115,16 +119,26 @@ public class GeckoView extends LayerView
 
         @Override // SurfaceHolder.Callback
         public void surfaceDestroyed(final SurfaceHolder holder) {
             if (mListener != null) {
                 mListener.surfaceDestroyed();
             }
             mValid = false;
         }
+
+        public void onGlobalLayout() {
+            if (mListener == null) {
+                return;
+            }
+            if (GeckoView.this.mSurfaceView != null) {
+                GeckoView.this.mSurfaceView.getLocationOnScreen(mOrigin);
+                mListener.screenOriginChanged(mOrigin[0], mOrigin[1]);
+            }
+        }
     }
 
     public GeckoView(final Context context) {
         super(context);
         init();
     }
 
     public GeckoView(final Context context, final AttributeSet attrs) {
@@ -216,16 +230,27 @@ public class GeckoView extends LayerView
         }
 
         if (mSession != null && mSession.isOpen()) {
             mSession.closeWindow();
         }
     }
 
     @Override
+    public boolean gatherTransparentRegion(final Region region) {
+        // For detecting changes in SurfaceView layout, we take a shortcut here and
+        // override gatherTransparentRegion, instead of registering a layout listener,
+        // which is more expensive.
+        if (mSurfaceView != null) {
+            mDisplay.onGlobalLayout();
+        }
+        return super.gatherTransparentRegion(region);
+    }
+
+    @Override
     protected Parcelable onSaveInstanceState() {
         mStateSaved = true;
         return new SavedState(super.onSaveInstanceState(), mSession);
     }
 
     @Override
     protected void onRestoreInstanceState(final Parcelable state) {
         mStateSaved = false;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
@@ -33,16 +33,25 @@ public interface GeckoDisplay {
         void surfaceChanged(Surface surface, int width, int height);
 
         /**
          * The display's Surface has been destroyed. Must be called on the application
          * main thread. GeckoSession may block this call to ensure the Surface is valid
          * while pausing drawing.
          */
         void surfaceDestroyed();
+
+        /**
+         * The display's coordinates on the screen has changed. Must be called on the
+         * application main thread.
+         *
+         * @param left The X coordinate of the display on the screen, in screen pixels.
+         * @param top The Y coordinate of the display on the screen, in screen pixels.
+         */
+        void screenOriginChanged(int left, int top);
     }
 
     /**
      * Get the current listener attached to this display. Must be called on the
      * application main thread.
      *
      * @return Current listener or null if there is no listener.
      */
--- 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
@@ -12,16 +12,22 @@ import org.mozilla.gecko.util.ThreadUtil
 import android.content.Context;
 import android.util.Log;
 import android.view.Surface;
 
 public class LayerSession {
     private static final String LOGTAG = "GeckoLayerSession";
     private static final boolean DEBUG = false;
 
+    // Sent from compositor when the static toolbar image has been updated and
+    // is ready to animate.
+    /* package */ final static int STATIC_TOOLBAR_READY = 1;
+    // Sent from compositor when the static toolbar has been made visible so
+    // the real toolbar should be shown.
+    /* package */ final static int TOOLBAR_SHOW = 4;
     // Special message sent from UiCompositorControllerChild once it is open.
     /* package */ final static int COMPOSITOR_CONTROLLER_OPEN = 20;
     // Special message sent from controller to query if the compositor controller is open.
     /* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN = 21;
 
     protected class Compositor extends JNIObject {
         public LayerView layerView;
 
@@ -48,17 +54,17 @@ public class LayerSession {
         @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
         @Override protected native void disposeNative();
 
         @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
         public native void attachToJava(GeckoLayerClient layerClient,
                                         NativePanZoomController npzc);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
-        public native void onSizeChanged(int windowWidth, int windowHeight);
+        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);
 
         // Gecko thread pauses compositor; blocks UI thread.
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void syncPauseCompositor();
@@ -91,16 +97,20 @@ public class LayerSession {
                         LayerSession.this.onCompositorReady();
                     }
                 });
             }
 
             if (layerView != null) {
                 layerView.handleToolbarAnimatorMessage(message);
             }
+
+            if (message == STATIC_TOOLBAR_READY || message == TOOLBAR_SHOW) {
+                LayerSession.this.onWindowBoundsChanged();
+            }
         }
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void setDefaultClearColor(int color);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void requestScreenPixels();
 
@@ -122,16 +132,18 @@ public class LayerSession {
     protected final Compositor mCompositor = new Compositor();
 
     // Following fields are accessed on UI thread.
     private GeckoDisplay mDisplay;
     private boolean mAttachedCompositor;
     private boolean mCalledCreateCompositor;
     private boolean mCompositorReady;
     private Surface mSurface;
+    private int mLeft;
+    private int mTop;
     private int mWidth;
     private int mHeight;
 
     /* package */ GeckoDisplay getDisplay() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
         return mDisplay;
@@ -175,46 +187,47 @@ public class LayerSession {
         if (mSurface != null) {
             // If we have a valid surface, resume the
             // compositor now that the compositor is ready.
             onSurfaceChanged(mSurface, mWidth, mHeight);
             mSurface = null;
         }
     }
 
-    private void onWindowResize() {
+    /* protected */ void onWindowBoundsChanged() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         if (mAttachedCompositor) {
-            final int viewportHeight;
+            final int toolbarHeight;
             if (mCompositor.layerView != null) {
-                viewportHeight = mHeight - mCompositor.layerView.getCurrentToolbarHeight();
+                toolbarHeight = mCompositor.layerView.getCurrentToolbarHeight();
             } else {
-                viewportHeight = mHeight;
+                toolbarHeight = 0;
             }
-            mCompositor.onSizeChanged(mWidth, viewportHeight);
+            mCompositor.onBoundsChanged(mLeft, mTop + toolbarHeight,
+                                        mWidth, mHeight - toolbarHeight);
 
             if (mCompositor.layerView != null) {
                 mCompositor.layerView.onSizeChanged(mWidth, mHeight);
             }
         }
     }
 
     /* package */ void onSurfaceChanged(final Surface surface, final int width,
                                         final int height) {
         ThreadUtils.assertOnUiThread();
 
         mWidth = width;
         mHeight = height;
-        onWindowResize();
 
         if (mCompositorReady) {
             mCompositor.syncResumeResizeCompositor(width, height, surface);
+            onWindowBoundsChanged();
             return;
         }
 
         if (mAttachedCompositor && !mCalledCreateCompositor) {
             mCompositor.createCompositor(width, height, surface);
             mCompositor.sendToolbarAnimatorMessage(IS_COMPOSITOR_CONTROLLER_OPEN);
             mCalledCreateCompositor = true;
         }
@@ -232,16 +245,28 @@ public class LayerSession {
             return;
         }
 
         // While the surface was valid, we never became attached or the
         // compositor never became ready; clear the saved surface.
         mSurface = null;
     }
 
+    /* package */ void onScreenOriginChanged(final int left, final int top) {
+        ThreadUtils.assertOnUiThread();
+
+        if (mLeft == left && mTop == top) {
+            return;
+        }
+
+        mLeft = left;
+        mTop = top;
+        onWindowBoundsChanged();
+    }
+
     public void addDisplay(final GeckoDisplay display) {
         ThreadUtils.assertOnUiThread();
 
         if (display.getListener() != null) {
             throw new IllegalArgumentException("Display already attached");
         } else if (mDisplay != null) {
             throw new IllegalArgumentException("Only one display supported");
         }
@@ -253,16 +278,21 @@ public class LayerSession {
                                        final int height) {
                 onSurfaceChanged(surface, width, height);
             }
 
             @Override
             public void surfaceDestroyed() {
                 onSurfaceDestroyed();
             }
+
+            @Override
+            public void screenOriginChanged(final int left, final int top) {
+                onScreenOriginChanged(left, top);
+            }
         });
     }
 
     public void removeDisplay(final GeckoDisplay display) {
         ThreadUtils.assertOnUiThread();
 
         if (mDisplay != display) {
             throw new IllegalArgumentException("Display not attached");
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -925,28 +925,25 @@ public:
 
         // 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 OnSizeChanged(int32_t aWindowWidth, int32_t aWindowHeight)
+    void OnBoundsChanged(int32_t aLeft, int32_t aTop,
+                         int32_t aWidth, int32_t aHeight)
     {
         MOZ_ASSERT(NS_IsMainThread());
         if (!mWindow) {
             return; // Already shut down.
         }
 
-        if (aWindowWidth != mWindow->mBounds.width ||
-            aWindowHeight != mWindow->mBounds.height) {
-
-            mWindow->Resize(aWindowWidth, aWindowHeight, /* repaint */ false);
-        }
+        mWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
     }
 
     void CreateCompositor(int32_t aWidth, int32_t aHeight,
                           jni::Object::Param aSurface)
     {
         MOZ_ASSERT(NS_IsMainThread());
         if (!mWindow) {
             return; // Already shut down.
@@ -1874,25 +1871,25 @@ nsWindow::GetScreenBounds()
 {
     return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
 }
 
 LayoutDeviceIntPoint
 nsWindow::WidgetToScreenOffset()
 {
     LayoutDeviceIntPoint p(0, 0);
-    nsWindow *w = this;
 
-    while (w && !w->IsTopLevel()) {
+    for (nsWindow *w = this; !!w; w = w->mParent) {
         p.x += w->mBounds.x;
         p.y += w->mBounds.y;
 
-        w = w->mParent;
+        if (w->IsTopLevel()) {
+            break;
+        }
     }
-
     return p;
 }
 
 nsresult
 nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
                         nsEventStatus& aStatus)
 {
     aStatus = DispatchEvent(aEvent);