Bug 777075 - Extract a PanZoomTarget interface for functions that PanZoomController depends upon. r=mbrubeck
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 07 Aug 2012 10:39:03 -0400
changeset 101673 e3e51a7d8f08761d962b38b3858a1a6f2dd3fa4d
parent 101672 0e86f20539bb51d0a6480c095e010cc22bc0699a
child 101674 a0cc76df8f3b0ebc9271cd769b01e8730bef6267
push id23250
push useremorley@mozilla.com
push dateWed, 08 Aug 2012 16:23:03 +0000
treeherderautoland@b99a81e70b06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck
bugs777075
milestone17.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 777075 - Extract a PanZoomTarget interface for functions that PanZoomController depends upon. r=mbrubeck
mobile/android/base/Makefile.in
mobile/android/base/gfx/LayerController.java
mobile/android/base/ui/PanZoomController.java
mobile/android/base/ui/PanZoomTarget.java
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -147,16 +147,17 @@ FENNEC_JAVA_FILES = \
   gfx/TextureReaper.java \
   gfx/TileLayer.java \
   gfx/TouchEventHandler.java \
   gfx/ViewTransform.java \
   gfx/ViewportMetrics.java \
   gfx/VirtualLayer.java \
   ui/Axis.java \
   ui/PanZoomController.java \
+  ui/PanZoomTarget.java \
   ui/SimpleScaleGestureDetector.java \
   ui/SubdocumentScrollHelper.java \
   GeckoNetworkManager.java \
   GeckoScreenOrientationListener.java \
   $(MOZGLUE_JAVA_FILES) \
   $(UTIL_JAVA_FILES) \
   $(NULL)
 
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -2,16 +2,17 @@
  * 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 org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.ui.PanZoomController;
+import org.mozilla.gecko.ui.PanZoomTarget;
 import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.PointF;
@@ -21,17 +22,17 @@ import android.view.GestureDetector;
 
 /**
  * The layer controller manages a tile that represents the visible page. It does panning and
  * zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
  * to a higher-level view.
  *
  * Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
  */
-public class LayerController {
+public class LayerController implements PanZoomTarget {
     private static final String LOGTAG = "GeckoLayerController";
 
     private Layer mRootLayer;                   /* The root layer. */
     private LayerView mView;                    /* The main rendering view. */
     private Context mContext;                   /* The current context. */
 
     /* This is volatile so that we can read and write to it from different threads.
      * We avoid synchronization to make getting the viewport metrics from
@@ -91,16 +92,17 @@ public class LayerController {
     public void setForceRedraw() {
         mForceRedraw = true;
     }
 
     public Layer getRoot()                        { return mRootLayer; }
     public LayerView getView()                    { return mView; }
     public Context getContext()                   { return mContext; }
     public ImmutableViewportMetrics getViewportMetrics()   { return mViewportMetrics; }
+    public Object getLock()                       { return this; }
 
     public FloatSize getViewportSize() {
         return mViewportMetrics.getSize();
     }
 
     public Bitmap getBackgroundPattern()    { return getDrawable("tabs_tray_selected_bg"); }
     public Bitmap getShadowPattern()        { return getDrawable("shadow"); }
 
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -6,17 +6,16 @@
 package org.mozilla.gecko.ui;
 
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoEventListener;
 import org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
-import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.PointUtils;
 import org.mozilla.gecko.gfx.ViewportMetrics;
 import org.mozilla.gecko.util.FloatUtils;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -102,17 +101,17 @@ public class PanZoomController
         ANIMATED_ZOOM,  /* animated zoom to a new rect */
         BOUNCE,         /* in a bounce animation */
 
         WAITING_LISTENERS, /* a state halfway between NOTHING and TOUCHING - the user has
                         put a finger down, but we don't yet know if a touch listener has
                         prevented the default actions yet. we still need to abort animations. */
     }
 
-    private final LayerController mController;
+    private final PanZoomTarget mTarget;
     private final SubdocumentScrollHelper mSubscroller;
     private final Axis mX;
     private final Axis mY;
 
     private Thread mMainThread;
 
     /* The timer that handles flings or bounces. */
     private Timer mAnimationTimer;
@@ -120,18 +119,18 @@ public class PanZoomController
     private AnimationRunnable mAnimationRunnable;
     /* The zoom focus at the first zoom event (in page coordinates). */
     private PointF mLastZoomFocus;
     /* The time the last motion event took place. */
     private long mLastEventTime;
     /* Current state the pan/zoom UI is in. */
     private PanZoomState mState;
 
-    public PanZoomController(LayerController controller) {
-        mController = controller;
+    public PanZoomController(PanZoomTarget target) {
+        mTarget = target;
         mSubscroller = new SubdocumentScrollHelper(this);
         mX = new AxisX(mSubscroller);
         mY = new AxisY(mSubscroller);
 
         mMainThread = GeckoApp.mAppContext.getMainLooper().getThread();
         checkMainThread();
 
         setState(PanZoomState.NOTHING);
@@ -153,17 +152,17 @@ public class PanZoomController
         mSubscroller.destroy();
     }
 
     private void setState(PanZoomState state) {
         mState = state;
     }
 
     private ImmutableViewportMetrics getMetrics() {
-        return mController.getViewportMetrics();
+        return mTarget.getViewportMetrics();
     }
 
     // for debugging bug 713011; it can be taken out once that is resolved.
     private void checkMainThread() {
         if (mMainThread != Thread.currentThread()) {
             // log with full stack trace
             Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
         }
@@ -172,17 +171,17 @@ public class PanZoomController
     public void handleMessage(String event, JSONObject message) {
         try {
             if (MESSAGE_ZOOM_RECT.equals(event)) {
                 float x = (float)message.getDouble("x");
                 float y = (float)message.getDouble("y");
                 final RectF zoomRect = new RectF(x, y,
                                      x + (float)message.getDouble("w"),
                                      y + (float)message.getDouble("h"));
-                mController.post(new Runnable() {
+                mTarget.post(new Runnable() {
                     public void run() {
                         animatedZoomTo(zoomRect);
                     }
                 });
             } else if (MESSAGE_ZOOM_PAGE.equals(event)) {
                 ImmutableViewportMetrics metrics = getMetrics();
                 RectF cssPageRect = metrics.getCssPageRect();
 
@@ -190,17 +189,17 @@ public class PanZoomController
                 float y = viewableRect.top;
                 // attempt to keep zoom keep focused on the center of the viewport
                 float newHeight = viewableRect.height() * cssPageRect.width() / viewableRect.width();
                 float dh = viewableRect.height() - newHeight; // increase in the height
                 final RectF r = new RectF(0.0f,
                                     y + dh/2,
                                     cssPageRect.width(),
                                     y + dh/2 + newHeight);
-                mController.post(new Runnable() {
+                mTarget.post(new Runnable() {
                     public void run() {
                         animatedZoomTo(r);
                     }
                 });
             } else if (MESSAGE_PREFS_DATA.equals(event)) {
                 JSONArray jsonPrefs = message.getJSONArray("preferences");
                 Map<String, Integer> axisPrefs = new HashMap<String, Integer>();
                 String zoomAnimationFrames = null;
@@ -275,19 +274,19 @@ public class PanZoomController
         case ANIMATED_ZOOM:
             // the zoom that's in progress likely makes no sense any more (such as if
             // the screen orientation changed) so abort it
             setState(PanZoomState.NOTHING);
             // fall through
         case NOTHING:
             // Don't do animations here; they're distracting and can cause flashes on page
             // transitions.
-            synchronized (mController) {
-                mController.setViewportMetrics(getValidViewportMetrics());
-                mController.notifyLayerClientOfGeometryChange();
+            synchronized (mTarget.getLock()) {
+                mTarget.setViewportMetrics(getValidViewportMetrics());
+                mTarget.notifyLayerClientOfGeometryChange();
             }
             break;
         }
     }
 
     /** This function must be called on the UI thread. */
     public void startingNewEventBlock(MotionEvent event, boolean waitingForTouchListeners) {
         checkMainThread();
@@ -309,23 +308,23 @@ public class PanZoomController
             // we need to reset our state and re-bounce because we might be in overscroll
             bounce();
         }
     }
 
     /** This must be called on the UI thread. */
     public void pageRectUpdated() {
         if (mState == PanZoomState.NOTHING) {
-            synchronized (mController) {
+            synchronized (mTarget.getLock()) {
                 ViewportMetrics validated = getValidViewportMetrics();
                 if (! (new ViewportMetrics(getMetrics())).fuzzyEquals(validated)) {
                     // page size changed such that we are now in overscroll. snap to the
                     // the nearest valid viewport
-                    mController.setViewportMetrics(validated);
-                    mController.notifyLayerClientOfGeometryChange();
+                    mTarget.setViewportMetrics(validated);
+                    mTarget.notifyLayerClientOfGeometryChange();
                 }
             }
         }
     }
 
     /*
      * Panning/scrolling
      */
@@ -335,18 +334,18 @@ public class PanZoomController
         // any auto-movement we have going
         stopAnimationTimer();
 
         switch (mState) {
         case ANIMATED_ZOOM:
             // We just interrupted a double-tap animation, so force a redraw in
             // case this touchstart is just a tap that doesn't end up triggering
             // a redraw
-            mController.setForceRedraw();
-            mController.notifyLayerClientOfGeometryChange();
+            mTarget.setForceRedraw();
+            mTarget.notifyLayerClientOfGeometryChange();
             // fall through
         case FLING:
         case BOUNCE:
         case NOTHING:
         case WAITING_LISTENERS:
             startTouch(event.getX(0), event.getY(0), event.getEventTime());
             return false;
         case TOUCHING:
@@ -561,17 +560,17 @@ public class PanZoomController
             setState(PanZoomState.NOTHING);
             return;
         }
 
         // At this point we have already set mState to BOUNCE or ANIMATED_ZOOM, so
         // getRedrawHint() is returning false. This means we can safely call
         // setAnimationTarget to set the new final display port and not have it get
         // clobbered by display ports from intermediate animation frames.
-        mController.setAnimationTarget(metrics);
+        mTarget.setAnimationTarget(metrics);
         startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
     }
 
     /* Performs a bounce-back animation to the nearest valid viewport metrics. */
     private void bounce() {
         setState(PanZoomState.BOUNCE);
         bounce(getValidViewportMetrics());
     }
@@ -582,17 +581,17 @@ public class PanZoomController
             Log.e(LOGTAG, "Attempted to start a new fling without canceling the old one!");
             stopAnimationTimer();
         }
 
         mAnimationTimer = new Timer("Animation Timer");
         mAnimationRunnable = runnable;
         mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
             @Override
-            public void run() { mController.post(runnable); }
+            public void run() { mTarget.post(runnable); }
         }, 0, 1000L/60L);
     }
 
     /* Stops the fling or bounce animation. */
     private void stopAnimationTimer() {
         if (mAnimationTimer != null) {
             mAnimationTimer.cancel();
             mAnimationTimer = null;
@@ -624,18 +623,18 @@ public class PanZoomController
     private void updatePosition() {
         mX.displace();
         mY.displace();
         PointF displacement = resetDisplacement();
         if (FloatUtils.fuzzyEquals(displacement.x, 0.0f) && FloatUtils.fuzzyEquals(displacement.y, 0.0f)) {
             return;
         }
         if (! mSubscroller.scrollBy(displacement)) {
-            synchronized (mController) {
-                mController.scrollBy(displacement);
+            synchronized (mTarget.getLock()) {
+                mTarget.scrollBy(displacement);
             }
         }
     }
 
     private abstract class AnimationRunnable implements Runnable {
         private boolean mAnimationTerminated;
 
         /* This should always run on the UI thread */
@@ -696,30 +695,30 @@ public class PanZoomController
             /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
             finishBounce();
             finishAnimation();
             setState(PanZoomState.NOTHING);
         }
 
         /* Performs one frame of a bounce animation. */
         private void advanceBounce() {
-            synchronized (mController) {
+            synchronized (mTarget.getLock()) {
                 float t = ZOOM_ANIMATION_FRAMES[mBounceFrame];
                 ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
-                mController.setViewportMetrics(newMetrics);
-                mController.notifyLayerClientOfGeometryChange();
+                mTarget.setViewportMetrics(newMetrics);
+                mTarget.notifyLayerClientOfGeometryChange();
                 mBounceFrame++;
             }
         }
 
         /* Concludes a bounce animation and snaps the viewport into place. */
         private void finishBounce() {
-            synchronized (mController) {
-                mController.setViewportMetrics(mBounceEndMetrics);
-                mController.notifyLayerClientOfGeometryChange();
+            synchronized (mTarget.getLock()) {
+                mTarget.setViewportMetrics(mBounceEndMetrics);
+                mTarget.notifyLayerClientOfGeometryChange();
                 mBounceFrame = -1;
             }
         }
     }
 
     // The callback that performs the fling animation.
     private class FlingRunnable extends AnimationRunnable {
         protected void animateFrame() {
@@ -770,18 +769,18 @@ public class PanZoomController
     }
 
     private void finishAnimation() {
         checkMainThread();
 
         stopAnimationTimer();
 
         // Force a viewport synchronisation
-        mController.setForceRedraw();
-        mController.notifyLayerClientOfGeometryChange();
+        mTarget.setForceRedraw();
+        mTarget.notifyLayerClientOfGeometryChange();
     }
 
     /* Returns the nearest viewport metrics with no overscroll visible. */
     private ViewportMetrics getValidViewportMetrics() {
         return getValidViewportMetrics(new ViewportMetrics(getMetrics()));
     }
 
     private ViewportMetrics getValidViewportMetrics(ViewportMetrics viewportMetrics) {
@@ -791,17 +790,17 @@ public class PanZoomController
         RectF viewport = viewportMetrics.getViewport();
 
         float focusX = viewport.width() / 2.0f;
         float focusY = viewport.height() / 2.0f;
 
         float minZoomFactor = 0.0f;
         float maxZoomFactor = MAX_ZOOM;
 
-        ZoomConstraints constraints = mController.getZoomConstraints();
+        ZoomConstraints constraints = mTarget.getZoomConstraints();
 
         if (constraints.getMinZoom() > 0)
             minZoomFactor = constraints.getMinZoom();
         if (constraints.getMaxZoom() > 0)
             maxZoomFactor = constraints.getMaxZoom();
 
         if (!constraints.getAllowZoom()) {
             // If allowZoom is false, clamp to the default zoom level.
@@ -870,17 +869,17 @@ public class PanZoomController
     /*
      * Zooming
      */
     @Override
     public boolean onScaleBegin(SimpleScaleGestureDetector detector) {
         if (mState == PanZoomState.ANIMATED_ZOOM)
             return false;
 
-        if (!mController.getZoomConstraints().getAllowZoom())
+        if (!mTarget.getZoomConstraints().getAllowZoom())
             return false;
 
         setState(PanZoomState.PINCHING);
         mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
         cancelTouch();
 
         return true;
     }
@@ -906,22 +905,22 @@ public class PanZoomController
          * factor toward 1.0.
          */
         float resistance = Math.min(mX.getEdgeResistance(), mY.getEdgeResistance());
         if (spanRatio > 1.0f)
             spanRatio = 1.0f + (spanRatio - 1.0f) * resistance;
         else
             spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
 
-        synchronized (mController) {
+        synchronized (mTarget.getLock()) {
             float newZoomFactor = getMetrics().zoomFactor * spanRatio;
             float minZoomFactor = 0.0f;
             float maxZoomFactor = MAX_ZOOM;
 
-            ZoomConstraints constraints = mController.getZoomConstraints();
+            ZoomConstraints constraints = mTarget.getZoomConstraints();
 
             if (constraints.getMinZoom() > 0)
                 minZoomFactor = constraints.getMinZoom();
             if (constraints.getMaxZoom() > 0)
                 maxZoomFactor = constraints.getMaxZoom();
 
             if (newZoomFactor < minZoomFactor) {
                 // apply resistance when zooming past minZoomFactor,
@@ -937,38 +936,38 @@ public class PanZoomController
                 // apply resistance when zooming past maxZoomFactor,
                 // such that it asymptotically reaches maxZoomFactor + 1.0
                 // but never exceeds that
                 float excessZoom = newZoomFactor - maxZoomFactor;
                 excessZoom = 1.0f - (float)Math.exp(-excessZoom);
                 newZoomFactor = maxZoomFactor + excessZoom;
             }
 
-            mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
-                                            mLastZoomFocus.y - detector.getFocusY()));
+            mTarget.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
+                    mLastZoomFocus.y - detector.getFocusY()));
             PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
-            mController.scaleWithFocus(newZoomFactor, focus);
+            mTarget.scaleWithFocus(newZoomFactor, focus);
         }
 
         mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
 
         return true;
     }
 
     @Override
     public void onScaleEnd(SimpleScaleGestureDetector detector) {
         if (mState == PanZoomState.ANIMATED_ZOOM)
             return;
 
         // switch back to the touching state
         startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime());
 
         // Force a viewport synchronisation
-        mController.setForceRedraw();
-        mController.notifyLayerClientOfGeometryChange();
+        mTarget.setForceRedraw();
+        mTarget.notifyLayerClientOfGeometryChange();
     }
 
     public boolean getRedrawHint() {
         switch (mState) {
             case PINCHING:
             case ANIMATED_ZOOM:
             case BOUNCE:
                 // don't redraw during these because the zoom is (or might be, in the case
@@ -980,17 +979,17 @@ public class PanZoomController
                 return true;
         }
     }
 
     private void sendPointToGecko(String event, MotionEvent motionEvent) {
         String json;
         try {
             PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
-            point = mController.convertViewPointToLayerPoint(point);
+            point = mTarget.convertViewPointToLayerPoint(point);
             if (point == null) {
                 return;
             }
             json = PointUtils.toJSON(point).toString();
         } catch (Exception e) {
             Log.e(LOGTAG, "Unable to convert point to JSON for " + event, e);
             return;
         }
@@ -1001,35 +1000,35 @@ public class PanZoomController
     @Override
     public void onLongPress(MotionEvent motionEvent) {
         sendPointToGecko("Gesture:LongPress", motionEvent);
     }
 
     @Override
     public boolean onSingleTapUp(MotionEvent motionEvent) {
         // When zooming is enabled, wait to see if there's a double-tap.
-        if (!mController.getZoomConstraints().getAllowZoom()) {
+        if (!mTarget.getZoomConstraints().getAllowZoom()) {
             sendPointToGecko("Gesture:SingleTap", motionEvent);
         }
         // return false because we still want to get the ACTION_UP event that triggers this
         return false;
     }
 
     @Override
     public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
         // When zooming is disabled, we handle this in onSingleTapUp.
-        if (mController.getZoomConstraints().getAllowZoom()) {
+        if (mTarget.getZoomConstraints().getAllowZoom()) {
             sendPointToGecko("Gesture:SingleTap", motionEvent);
         }
         return true;
     }
 
     @Override
     public boolean onDoubleTap(MotionEvent motionEvent) {
-        if (mController.getZoomConstraints().getAllowZoom()) {
+        if (mTarget.getZoomConstraints().getAllowZoom()) {
             sendPointToGecko("Gesture:DoubleTap", motionEvent);
         }
         return true;
     }
 
     private void cancelTouch() {
         GeckoEvent e = GeckoEvent.createBroadcastEvent("Gesture:CancelTouch", "");
         GeckoAppShell.sendEventToGecko(e);
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/ui/PanZoomTarget.java
@@ -0,0 +1,29 @@
+/* -*- 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.ui;
+
+import org.mozilla.gecko.ZoomConstraints;
+import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
+import org.mozilla.gecko.gfx.ViewportMetrics;
+
+import android.graphics.PointF;
+
+public interface PanZoomTarget {
+    public ImmutableViewportMetrics getViewportMetrics();
+    public ZoomConstraints getZoomConstraints();
+
+    public void setAnimationTarget(ViewportMetrics viewport);
+    public void setViewportMetrics(ViewportMetrics viewport);
+    public void scrollBy(PointF point);
+    public void scaleWithFocus(float zoomFactor, PointF focus);
+
+    public void notifyLayerClientOfGeometryChange();
+    public void setForceRedraw();
+
+    public boolean post(Runnable action);
+    public Object getLock();
+    public PointF convertViewPointToLayerPoint(PointF viewPoint);
+}