Bug 1416319 - 4. Store DynamicToolbarAnimator in LayerSession; r?rbarker draft
authorJim Chen <nchen@mozilla.com>
Wed, 22 Nov 2017 14:12:22 -0500
changeset 702127 85d42387419012bbe66a9e5d70b890333e8f0923
parent 702126 ba491771124baaea743acbf22908d122bcc6ce41
child 702128 af5a55d7398c896d74e0f361cbcfac43e095799d
push id90386
push userbmo:nchen@mozilla.com
push dateWed, 22 Nov 2017 19:12:37 +0000
reviewersrbarker
bugs1416319
milestone59.0a1
Bug 1416319 - 4. Store DynamicToolbarAnimator in LayerSession; r?rbarker Add getter/setter for DynamicToolbarAnimator in LayerSession in lieu of LayerView. It is then LayerSession's responsibility to keep track of how changes in the toolbar affects other things like the window and viewport bounds. MozReview-Commit-ID: HmRqxZ7EUCZ
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
widget/android/GeneratedJNIWrappers.h
--- 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
@@ -5,33 +5,59 @@
 
 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.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;
+    //
+    // NOTE: These values are also defined in
+    // gfx/layers/ipc/UiCompositorControllerMessageTypes.h and must be kept in sync. Any
+    // new AnimatorMessageType added here must also be added there.
+    //
+    // Sent from compositor when the static toolbar wants to hide.
+    /* package */ final static int STATIC_TOOLBAR_NEEDS_UPDATE      = 0;
+    // 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 to compositor when the real toolbar has been hidden.
+    /* package */ final static int TOOLBAR_HIDDEN                   = 2;
+    // Sent to compositor when the real toolbar is visible.
+    /* package */ final static int TOOLBAR_VISIBLE                  = 3;
+    // 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;
+    // Sent from compositor after first paint
+    /* package */ final static int FIRST_PAINT                      = 5;
+    // Sent to compositor requesting toolbar be shown immediately
+    /* package */ final static int REQUEST_SHOW_TOOLBAR_IMMEDIATELY = 6;
+    // Sent to compositor requesting toolbar be shown animated
+    /* package */ final static int REQUEST_SHOW_TOOLBAR_ANIMATED    = 7;
+    // Sent to compositor requesting toolbar be hidden immediately
+    /* package */ final static int REQUEST_HIDE_TOOLBAR_IMMEDIATELY = 8;
+    // Sent to compositor requesting toolbar be hidden animated
+    /* package */ final static int REQUEST_HIDE_TOOLBAR_ANIMATED    = 9;
+    // Sent from compositor when a layer has been updated
+    /* package */ final static int LAYERS_UPDATED                   = 10;
+    // Sent to compositor when the toolbar snapshot fails.
+    /* package */ final static int TOOLBAR_SNAPSHOT_FAILED          = 11;
+    // 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;
+    /* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN    = 21;
 
     protected class Compositor extends JNIObject {
         public LayerView layerView;
 
         public boolean isReady() {
             return LayerSession.this.isCompositorReady();
         }
 
@@ -70,46 +96,25 @@ public class LayerSession {
 
         // UI thread resumes compositor and notifies Gecko thread; does not block UI thread.
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void syncResumeResizeCompositor(int width, int height, Object surface);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void setMaxToolbarHeight(int height);
 
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void setPinned(boolean pinned, int reason);
 
-        @WrapForJNI(calledFrom = "any", dispatchTo = "current")
+        @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void sendToolbarAnimatorMessage(int message);
 
         @WrapForJNI(calledFrom = "ui")
         private void recvToolbarAnimatorMessage(int message) {
-            if (message == COMPOSITOR_CONTROLLER_OPEN) {
-                if (LayerSession.this.isCompositorReady()) {
-                    return;
-                }
-
-                // Delay calling onCompositorReady to avoid deadlock due
-                // to synchronous call to the compositor.
-                ThreadUtils.postToUiThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        LayerSession.this.onCompositorReady();
-                    }
-                });
-            }
-
-            if (layerView != null) {
-                layerView.handleToolbarAnimatorMessage(message);
-            }
-
-            if (message == STATIC_TOOLBAR_READY || message == TOOLBAR_SHOW) {
-                LayerSession.this.onWindowBoundsChanged();
-            }
+            LayerSession.this.handleCompositorMessage(message);
         }
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void setDefaultClearColor(int color);
 
         @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
         public native void requestScreenPixels();
 
@@ -127,43 +132,62 @@ public class LayerSession {
         public native void sendToolbarPixelsToCompositor(final int width, final int height,
                                                          final int[] pixels);
 
         // The compositor invokes this function just before compositing a frame where the
         // document is different from the document composited on the last frame. In these
         // cases, the viewport information we have in Java is no longer valid and needs to
         // be replaced with the new viewport information provided.
         @WrapForJNI(calledFrom = "ui")
-        public void updateRootFrameMetrics(float scrollX, float scrollY, float zoom) {
-            if (layerView != null) {
-                layerView.onMetricsChanged(scrollX, scrollY, zoom);
-            }
+        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.
     private GeckoDisplay mDisplay;
+    private DynamicToolbarAnimator mToolbar;
+    private ImmutableViewportMetrics mViewportMetrics = new ImmutableViewportMetrics();
     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;
     }
 
+    /**
+     * Get the DynamicToolbarAnimator instance for this session.
+     *
+     * @return DynamicToolbarAnimator instance.
+     */
+    public @NonNull DynamicToolbarAnimator getDynamicToolbarAnimator() {
+        ThreadUtils.assertOnUiThread();
+
+        if (mToolbar == null) {
+            mToolbar = new DynamicToolbarAnimator(this);
+        }
+        return mToolbar;
+    }
+
+    public ImmutableViewportMetrics getViewportMetrics() {
+        ThreadUtils.assertOnUiThread();
+        return mViewportMetrics;
+    }
+
     /* package */ void onCompositorAttached() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         mAttachedCompositor = true;
 
         if (mSurface != null) {
@@ -178,16 +202,76 @@ public class LayerSession {
             ThreadUtils.assertOnUiThread();
         }
 
         mAttachedCompositor = false;
         mCalledCreateCompositor = false;
         mCompositorReady = false;
     }
 
+    /* package */ void handleCompositorMessage(final int message) {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        switch (message) {
+            case COMPOSITOR_CONTROLLER_OPEN: {
+                if (isCompositorReady()) {
+                    return;
+                }
+
+                // Delay calling onCompositorReady to avoid deadlock due
+                // to synchronous call to the compositor.
+                ThreadUtils.postToUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        onCompositorReady();
+                        if (mCompositor.layerView != null) {
+                            mCompositor.layerView.onCompositorReady();
+                        }
+                    }
+                });
+                break;
+            }
+
+            case FIRST_PAINT: {
+                if (mCompositor.layerView != null) {
+                    mCompositor.layerView.setSurfaceBackgroundColor(Color.TRANSPARENT);
+                }
+                break;
+            }
+
+            case LAYERS_UPDATED: {
+                if (mCompositor.layerView != null) {
+                    mCompositor.layerView.notifyDrawListeners();
+                }
+                break;
+            }
+
+            case STATIC_TOOLBAR_READY:
+            case TOOLBAR_SHOW: {
+                if (mToolbar != null) {
+                    mToolbar.handleToolbarAnimatorMessage(message);
+                    // Update window bounds due to toolbar visibility change.
+                    onWindowBoundsChanged();
+                }
+                break;
+            }
+
+            default: {
+                if (mToolbar != null) {
+                    mToolbar.handleToolbarAnimatorMessage(message);
+                } else {
+                    Log.w(LOGTAG, "Unexpected message: " + message);
+                }
+                break;
+            }
+        }
+    }
+
     /* package */ boolean isCompositorReady() {
         return mCompositorReady;
     }
 
     /* package */ void onCompositorReady() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
@@ -195,46 +279,78 @@ public class LayerSession {
         mCompositorReady = true;
 
         if (mSurface != null) {
             // If we have a valid surface, resume the
             // compositor now that the compositor is ready.
             onSurfaceChanged(mSurface, mWidth, mHeight);
             mSurface = null;
         }
+
+        if (mToolbar != null) {
+            mToolbar.onCompositorReady();
+        }
+    }
+
+    /* 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);
+        }
     }
 
     /* protected */ void onWindowBoundsChanged() {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
+        final int toolbarHeight;
+        if (mToolbar != null) {
+            toolbarHeight = mToolbar.getCurrentToolbarHeight();
+        } else {
+            toolbarHeight = 0;
+        }
+
         if (mAttachedCompositor) {
-            final int toolbarHeight;
-            if (mCompositor.layerView != null) {
-                toolbarHeight = mCompositor.layerView.getCurrentToolbarHeight();
-            } else {
-                toolbarHeight = 0;
-            }
             mCompositor.onBoundsChanged(mLeft, mTop + toolbarHeight,
                                         mWidth, mHeight - toolbarHeight);
+        }
 
-            if (mCompositor.layerView != null) {
-                mCompositor.layerView.onSizeChanged(mWidth, mHeight);
-            }
+        if (mViewportMetrics.viewportRectWidth != mWidth ||
+            mViewportMetrics.viewportRectHeight != (mHeight - toolbarHeight)) {
+            mViewportMetrics = mViewportMetrics.setViewportSize(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;
 
+        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);
--- 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
@@ -145,16 +145,21 @@ public class LayerView extends FrameLayo
                 });
                 break;
             default:
                 Log.e(LOGTAG, "Unhandled Toolbar Animator Message: " + message);
                 break;
         }
     }
 
+    /* package */ void onCompositorReady() {
+        mCompositor.setDefaultClearColor(mDefaultClearColor);
+        mCompositor.enableLayerUpdateNotifications(!mDrawListeners.isEmpty());
+    }
+
     private LayerSession.Compositor mCompositor;
 
     public LayerView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         mFullScreenState = FullScreenState.NONE;
 
         mOverscroll = new OverscrollEdgeEffect(this);
@@ -401,16 +406,22 @@ public class LayerView extends FrameLayo
             mCompositor.enableLayerUpdateNotifications(false);
         }
     }
 
     /* package */ void clearDrawListeners() {
         mDrawListeners.clear();
     }
 
+    /* package */ void notifyDrawListeners() {
+        for (final DrawListener listener : mDrawListeners) {
+            listener.drawFinished();
+        }
+    }
+
     @RobocopTarget
     public static interface DrawListener {
         public void drawFinished();
     }
 
     public float getZoomFactor() {
         return getViewportMetrics().zoomFactor;
     }
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -3993,17 +3993,17 @@ public:
                 int32_t> Args;
         static constexpr char name[] = "sendToolbarAnimatorMessage";
         static constexpr char signature[] =
                 "(I)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
+                mozilla::jni::CallingThread::UI;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     struct SendToolbarPixelsToCompositor_t {
         typedef Compositor Owner;
         typedef void ReturnType;
         typedef void SetterType;
@@ -4068,17 +4068,17 @@ public:
                 int32_t> Args;
         static constexpr char name[] = "setPinned";
         static constexpr char signature[] =
                 "(ZI)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::ANY;
+                mozilla::jni::CallingThread::UI;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     struct SyncPauseCompositor_t {
         typedef Compositor Owner;
         typedef void ReturnType;
         typedef void SetterType;