Bug 1416319 - 7. Don't expose viewport metrics from LayerSession; r?rbarker draft
authorJim Chen <nchen@mozilla.com>
Wed, 22 Nov 2017 14:12:23 -0500
changeset 702130 5d94c5608a7c6cd5e96799ade2a29b1bcc6b998d
parent 702129 81e834ef25038fd735281def9d7780c517595cea
child 702131 d2ec431b2f3bf776fc215fd223099c609940a49e
push id90386
push userbmo:nchen@mozilla.com
push dateWed, 22 Nov 2017 19:12:37 +0000
reviewersrbarker
bugs1416319
milestone59.0a1
Bug 1416319 - 7. Don't expose viewport metrics from LayerSession; r?rbarker Provide a set of coordinates APIs in LayerSession instead of exposing the raw viewport metrics, which are hard to use. This also lets us remove ImmutableViewportMetrics completely. The new APIs provide rectangular bounds in client or surface coordinates, and matrices to transform those coordinates to screen coordinates. This is done because the transformation to screen coordinates could (in the future) involve skew, rotation, etc, so it's up to the application to decide how to handle non-rectangular screen bounds. MozReview-Commit-ID: 8Yw8L63TmrQ
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -395,17 +395,16 @@ gvjar.sources += [geckoview_source_dir +
     'gfx/BufferedImage.java',
     'gfx/BufferedImageGLInfo.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
     'gfx/GeckoDisplay.java',
     'gfx/GeckoSurface.java',
     'gfx/GeckoSurfaceTexture.java',
-    'gfx/ImmutableViewportMetrics.java',
     'gfx/IntSize.java',
     'gfx/LayerSession.java',
     'gfx/LayerView.java',
     'gfx/NativePanZoomController.java',
     'gfx/Overscroll.java',
     'gfx/OverscrollEdgeEffect.java',
     'gfx/PanningPerfAPI.java',
     'gfx/PanZoomController.java',
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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.gfx;
-
-import android.graphics.PointF;
-import android.graphics.RectF;
-
-/**
- * ImmutableViewportMetrics are used to store the viewport metrics
- * in way that we can access a version of them from multiple threads
- * without having to take a lock
- */
-public class ImmutableViewportMetrics {
-
-    // We need to flatten the RectF and FloatSize structures
-    // because Java doesn't have the concept of const classes
-    public final float viewportRectLeft;
-    public final float viewportRectTop;
-    public final int viewportRectWidth;
-    public final int viewportRectHeight;
-
-    public final float zoomFactor;
-
-    public ImmutableViewportMetrics() {
-        viewportRectLeft = 0;
-        viewportRectTop = 0;
-        viewportRectWidth = 0;
-        viewportRectHeight = 0;
-        zoomFactor = 1.0f;
-    }
-
-    private ImmutableViewportMetrics(
-        float aViewportRectLeft, float aViewportRectTop, int aViewportRectWidth,
-        int aViewportRectHeight, float aZoomFactor)
-    {
-        viewportRectLeft = aViewportRectLeft;
-        viewportRectTop = aViewportRectTop;
-        viewportRectWidth = aViewportRectWidth;
-        viewportRectHeight = aViewportRectHeight;
-        zoomFactor = aZoomFactor;
-    }
-
-    public float getWidth() {
-        return viewportRectWidth;
-    }
-
-    public float getHeight() {
-        return viewportRectHeight;
-    }
-
-    public float viewportRectRight() {
-        return viewportRectLeft + viewportRectWidth;
-    }
-
-    public float viewportRectBottom() {
-        return viewportRectTop + viewportRectHeight;
-    }
-
-    public PointF getOrigin() {
-        return new PointF(viewportRectLeft, viewportRectTop);
-    }
-
-    public FloatSize getSize() {
-        return new FloatSize(viewportRectWidth, viewportRectHeight);
-    }
-
-    public RectF getViewport() {
-        return new RectF(viewportRectLeft,
-                         viewportRectTop,
-                         viewportRectRight(),
-                         viewportRectBottom());
-    }
-
-    public ImmutableViewportMetrics setViewportSize(int width, int height) {
-        if (width == viewportRectWidth && height == viewportRectHeight) {
-            return this;
-        }
-
-        return new ImmutableViewportMetrics(
-            viewportRectLeft, viewportRectTop, width, height,
-            zoomFactor);
-    }
-
-    public ImmutableViewportMetrics setViewportOrigin(float newOriginX, float newOriginY) {
-        return new ImmutableViewportMetrics(
-            newOriginX, newOriginY, viewportRectWidth, viewportRectHeight,
-            zoomFactor);
-    }
-
-    public ImmutableViewportMetrics setZoomFactor(float newZoomFactor) {
-        return new ImmutableViewportMetrics(
-            viewportRectLeft, viewportRectTop, viewportRectWidth, viewportRectHeight,
-            newZoomFactor);
-    }
-
-    @Override
-    public String toString() {
-        return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + ","
-                + viewportRectWidth + "x" + viewportRectHeight + ") z=" + zoomFactor;
-    }
-}
--- 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
@@ -6,16 +6,20 @@
 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.content.Context;
 import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.support.annotation.NonNull;
 import android.util.Log;
 import android.view.Surface;
 
 public class LayerSession {
     private static final String LOGTAG = "GeckoLayerSession";
     private static final boolean DEBUG = false;
 
     //
@@ -139,28 +143,35 @@ public class LayerSession {
         @WrapForJNI(calledFrom = "ui")
         private void updateRootFrameMetrics(float scrollX, float scrollY, float zoom) {
             LayerSession.this.onMetricsChanged(scrollX, scrollY, zoom);
         }
     }
 
     protected final Compositor mCompositor = new Compositor();
 
-    // Following fields are accessed on UI thread.
+    // All fields are accessed on UI thread only.
     private GeckoDisplay mDisplay;
     private DynamicToolbarAnimator mToolbar;
-    private ImmutableViewportMetrics mViewportMetrics = new ImmutableViewportMetrics();
+
     private boolean mAttachedCompositor;
     private boolean mCalledCreateCompositor;
     private boolean mCompositorReady;
     private Surface mSurface;
+
+    // All fields of coordinates are in screen units.
     private int mLeft;
-    private int mTop;
+    private int mTop; // Top of the surface (including toolbar);
+    private int mClientTop; // Top of the client area (i.e. excluding toolbar);
     private int mWidth;
-    private int mHeight;
+    private int mHeight; // Height of the surface (including toolbar);
+    private int mClientHeight; // Height of the client area (i.e. excluding toolbar);
+    private float mViewportLeft;
+    private float mViewportTop;
+    private float mViewportZoom = 1.0f;
 
     /* package */ GeckoDisplay getDisplay() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
         return mDisplay;
     }
 
@@ -173,19 +184,106 @@ public class LayerSession {
         ThreadUtils.assertOnUiThread();
 
         if (mToolbar == null) {
             mToolbar = new DynamicToolbarAnimator(this);
         }
         return mToolbar;
     }
 
-    public ImmutableViewportMetrics getViewportMetrics() {
+    /**
+     * Get a matrix for transforming from client coordinates to screen coordinates. The
+     * client coordinates are in CSS pixels and are relative to the viewport origin; their
+     * relation to screen coordinates does not depend on the current scroll position.
+     *
+     * @param matrix Matrix to be replaced by the transformation matrix.
+     * @see #getClientToSurfaceMatrix(Matrix)
+     * @see #getPageToScreenMatrix(Matrix)
+     */
+    public void getClientToScreenMatrix(@NonNull final Matrix matrix) {
+        ThreadUtils.assertOnUiThread();
+
+        getClientToSurfaceMatrix(matrix);
+        matrix.postTranslate(mLeft, mTop);
+    }
+
+    /**
+     * Get a matrix for transforming from client coordinates to surface coordinates.
+     *
+     * @param matrix Matrix to be replaced by the transformation matrix.
+     * @see #getClientToScreenMatrix(Matrix)
+     * @see #getPageToSurfaceMatrix(Matrix)
+     */
+    public void getClientToSurfaceMatrix(@NonNull final Matrix matrix) {
+        ThreadUtils.assertOnUiThread();
+
+        matrix.setScale(mViewportZoom, mViewportZoom);
+        if (mClientTop != mTop) {
+            matrix.postTranslate(0, mClientTop - mTop);
+        }
+    }
+
+    /**
+     * Get a matrix for transforming from page coordinates to screen coordinates. The page
+     * coordinates are in CSS pixels and are relative to the page origin; their relation
+     * to screen coordinates depends on the current scroll position of the outermost
+     * frame.
+     *
+     * @param matrix Matrix to be replaced by the transformation matrix.
+     * @see #getPageToSurfaceMatrix(Matrix)
+     * @see #getClientToScreenMatrix(Matrix)
+     */
+    public void getPageToScreenMatrix(@NonNull final Matrix matrix) {
         ThreadUtils.assertOnUiThread();
-        return mViewportMetrics;
+
+        getPageToSurfaceMatrix(matrix);
+        matrix.postTranslate(mLeft, mTop);
+    }
+
+    /**
+     * Get a matrix for transforming from page coordinates to surface coordinates.
+     *
+     * @param matrix Matrix to be replaced by the transformation matrix.
+     * @see #getPageToScreenMatrix(Matrix)
+     * @see #getClientToSurfaceMatrix(Matrix)
+     */
+    public void getPageToSurfaceMatrix(@NonNull final Matrix matrix) {
+        ThreadUtils.assertOnUiThread();
+
+        getClientToSurfaceMatrix(matrix);
+        matrix.postTranslate(-mViewportLeft, -mViewportTop);
+    }
+
+    /**
+     * Get the bounds of the client area in client coordinates. The returned top-left
+     * coordinates are always (0, 0). Use the matrix from
+     * #getClientToSurfaceMatrix(Matrix) or #getClientToScreenMatrix(Matrix) to map
+     * these bounds to surface or screen coordinates, respectively.
+     *
+     * @param rect RectF to be replaced by the client bounds in client coordinates.
+     * @see getSurfaceBounds(Rect)
+     */
+    public void getClientBounds(@NonNull final RectF rect) {
+        ThreadUtils.assertOnUiThread();
+
+        rect.set(0.0f, 0.0f, (float) mWidth / mViewportZoom,
+                             (float) mClientHeight / mViewportZoom);
+    }
+
+    /**
+     * Get the bounds of the client area in surface coordinates. This is equivalent to
+     * mapping the bounds returned by #getClientBounds(RectF) with the matrix returned by
+     * #getClientToSurfaceMatrix(Matrix).
+     *
+     * @param rect Rect to be replaced by the client bounds in surface coordinates.
+     */
+    public void getSurfaceBounds(@NonNull final Rect rect) {
+        ThreadUtils.assertOnUiThread();
+
+        rect.set(0, mClientTop - mTop, mWidth, mHeight);
     }
 
     /* package */ void onCompositorAttached() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         mAttachedCompositor = true;
@@ -291,81 +389,70 @@ public class LayerSession {
     }
 
     /* package */ void onMetricsChanged(final float scrollX, final float scrollY,
                                         final float zoom) {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
-        if (mViewportMetrics.viewportRectLeft != scrollX ||
-            mViewportMetrics.viewportRectTop != scrollY) {
-            mViewportMetrics = mViewportMetrics.setViewportOrigin(scrollX, scrollY);
-        }
-
-        if (mViewportMetrics.zoomFactor != zoom) {
-            mViewportMetrics = mViewportMetrics.setZoomFactor(zoom);
-        }
+        mViewportLeft = scrollX;
+        mViewportTop = scrollY;
+        mViewportZoom = zoom;
     }
 
     /* protected */ void onWindowBoundsChanged() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         final int toolbarHeight;
         if (mToolbar != null) {
             toolbarHeight = mToolbar.getCurrentToolbarHeight();
         } else {
             toolbarHeight = 0;
         }
 
-        if (mAttachedCompositor) {
-            mCompositor.onBoundsChanged(mLeft, mTop + toolbarHeight,
-                                        mWidth, mHeight - toolbarHeight);
-        }
+        mClientTop = mTop + toolbarHeight;
+        mClientHeight = mHeight - toolbarHeight;
 
-        if (mViewportMetrics.viewportRectWidth != mWidth ||
-            mViewportMetrics.viewportRectHeight != (mHeight - toolbarHeight)) {
-            mViewportMetrics = mViewportMetrics.setViewportSize(mWidth,
-                                                                mHeight - toolbarHeight);
+        if (mAttachedCompositor) {
+            mCompositor.onBoundsChanged(mLeft, mClientTop, mWidth, mClientHeight);
         }
 
         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;
 
-        if (mViewportMetrics.viewportRectWidth == 0 ||
-            mViewportMetrics.viewportRectHeight == 0) {
-            mViewportMetrics = mViewportMetrics.setViewportSize(width, height);
-        }
-
         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;
         }
 
         // We have a valid surface but we're not attached or the compositor
         // is not ready; save the surface for later when we're ready.
         mSurface = surface;
+
+        // Adjust bounds as the last step.
+        onWindowBoundsChanged();
     }
 
     /* package */ void onSurfaceDestroyed() {
         ThreadUtils.assertOnUiThread();
 
         if (mCompositorReady) {
             mCompositor.syncPauseCompositor();
             return;