Bug 1258470 - Part 1: Assume MOZ_ANDROID_APZ. DO NOT LAND. draft
authorNick Alexander <nalexander@mozilla.com>
Thu, 23 Jun 2016 16:24:53 -0700
changeset 380993 ac67aa8d5d7f5fd3e0125dc1a7aadecad1000536
parent 380992 3693e5d75b64bb448c643bedb54ba6a21e4f274d
child 380994 5ba4e0d2b6bfe10df53ee30966ca419c00220d3c
push id21383
push usernalexander@mozilla.com
push dateFri, 24 Jun 2016 00:16:43 +0000
bugs1258470
milestone50.0a1
Bug 1258470 - Part 1: Assume MOZ_ANDROID_APZ. DO NOT LAND. MozReview-Commit-ID: BiFdiE7rO5F
mobile/android/base/java/org/mozilla/gecko/gfx/Axis.java
mobile/android/base/java/org/mozilla/gecko/gfx/BufferedImage.java
mobile/android/base/java/org/mozilla/gecko/gfx/BufferedImageGLInfo.java
mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
mobile/android/base/java/org/mozilla/gecko/gfx/LayerRenderer.java
mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java
mobile/android/base/java/org/mozilla/gecko/gfx/ScrollbarLayer.java
mobile/android/base/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java
mobile/android/base/java/org/mozilla/gecko/gfx/SubdocumentScrollHelper.java
mobile/android/base/java/org/mozilla/gecko/gfx/TouchEventHandler.java
mobile/android/base/moz.build
mobile/android/base/resources/drawable/scrollbar.png
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/Axis.java
+++ /dev/null
@@ -1,532 +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 java.util.HashMap;
-import java.util.Map;
-
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.PrefsHelper;
-import org.mozilla.gecko.util.FloatUtils;
-
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-
-/**
- * This class represents the physics for one axis of movement (i.e. either
- * horizontal or vertical). It tracks the different properties of movement
- * like displacement, velocity, viewport dimensions, etc. pertaining to
- * a particular axis.
- */
-abstract class Axis {
-    private static final String LOGTAG = "GeckoAxis";
-
-    private static final String PREF_SCROLLING_FRICTION_SLOW = "ui.scrolling.friction_slow";
-    private static final String PREF_SCROLLING_FRICTION_FAST = "ui.scrolling.friction_fast";
-    private static final String PREF_SCROLLING_MAX_EVENT_ACCELERATION = "ui.scrolling.max_event_acceleration";
-    private static final String PREF_SCROLLING_OVERSCROLL_DECEL_RATE = "ui.scrolling.overscroll_decel_rate";
-    private static final String PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT = "ui.scrolling.overscroll_snap_limit";
-    private static final String PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE = "ui.scrolling.min_scrollable_distance";
-    private static final String PREF_FLING_ACCEL_INTERVAL = "ui.scrolling.fling_accel_interval";
-    private static final String PREF_FLING_ACCEL_BASE_MULTIPLIER = "ui.scrolling.fling_accel_base_multiplier";
-    private static final String PREF_FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER = "ui.scrolling.fling_accel_supplemental_multiplier";
-    private static final String PREF_FLING_CURVE_FUNCTION_X1 = "ui.scrolling.fling_curve_function_x1";
-    private static final String PREF_FLING_CURVE_FUNCTION_Y1 = "ui.scrolling.fling_curve_function_y1";
-    private static final String PREF_FLING_CURVE_FUNCTION_X2 = "ui.scrolling.fling_curve_function_x2";
-    private static final String PREF_FLING_CURVE_FUNCTION_Y2 = "ui.scrolling.fling_curve_function_y2";
-    private static final String PREF_FLING_CURVE_THRESHOLD_VELOCITY = "ui.scrolling.fling_curve_threshold_velocity";
-    private static final String PREF_FLING_CURVE_MAXIMUM_VELOCITY = "ui.scrolling.fling_curve_max_velocity";
-    private static final String PREF_FLING_CURVE_NEWTON_ITERATIONS = "ui.scrolling.fling_curve_newton_iterations";
-
-    // This fraction of velocity remains after every animation frame when the velocity is low.
-    private static float FRICTION_SLOW;
-    // This fraction of velocity remains after every animation frame when the velocity is high.
-    private static float FRICTION_FAST;
-    // Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST
-    // to FRICTION_SLOW.
-    private static float VELOCITY_THRESHOLD;
-    // The maximum velocity change factor between events, per ms, in %.
-    // Direction changes are excluded.
-    private static float MAX_EVENT_ACCELERATION;
-
-    // The rate of deceleration when the surface has overscrolled.
-    private static float OVERSCROLL_DECEL_RATE;
-    // The percentage of the surface which can be overscrolled before it must snap back.
-    private static float SNAP_LIMIT;
-
-    // The minimum amount of space that must be present for an axis to be considered scrollable,
-    // in pixels.
-    private static float MIN_SCROLLABLE_DISTANCE;
-
-    // The interval within which if two flings are done then scrolling effect is accelerated.
-    private static long FLING_ACCEL_INTERVAL;
-
-    // The multiplication constant of the base velocity in case of accelerated scrolling.
-    private static float FLING_ACCEL_BASE_MULTIPLIER;
-
-    // The multiplication constant of the supplemental velocity in case of accelerated scrolling.
-    private static float FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER;
-
-    // x co-ordinate of the second bezier control point
-    private static float FLING_CURVE_FUNCTION_X1;
-
-    // y co-ordinate of the second bezier control point
-    private static float FLING_CURVE_FUNCTION_Y1;
-
-    // x co-ordinate of the third bezier control point
-    private static float FLING_CURVE_FUNCTION_X2;
-
-    // y co-ordinate of the third bezier control point
-    private static float FLING_CURVE_FUNCTION_Y2;
-
-    // Minimum velocity for curve to be implemented i.e fling curving
-    private static float FLING_CURVE_THRESHOLD_VELOCITY;
-
-    // Maximum permitted velocity
-    private static float FLING_CURVE_MAXIMUM_VELOCITY;
-
-    // Number of iterations in the Newton-Raphson method
-    private static int FLING_CURVE_NEWTON_ITERATIONS;
-
-    private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
-        Integer value = (prefs == null ? null : prefs.get(prefName));
-        return (value == null || value < 0 ? defaultValue : value) / 1000f;
-    }
-
-    private static int getIntPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
-        Integer value = (prefs == null ? null : prefs.get(prefName));
-        return (value == null || value < 0 ? defaultValue : value);
-    }
-
-    static void initPrefs() {
-        final String[] prefs = { PREF_SCROLLING_FRICTION_FAST,
-                                 PREF_SCROLLING_FRICTION_SLOW,
-                                 PREF_SCROLLING_MAX_EVENT_ACCELERATION,
-                                 PREF_SCROLLING_OVERSCROLL_DECEL_RATE,
-                                 PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT,
-                                 PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE,
-                                 PREF_FLING_ACCEL_INTERVAL,
-                                 PREF_FLING_ACCEL_BASE_MULTIPLIER,
-                                 PREF_FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER,
-                                 PREF_FLING_CURVE_FUNCTION_X1,
-                                 PREF_FLING_CURVE_FUNCTION_Y1,
-                                 PREF_FLING_CURVE_FUNCTION_X2,
-                                 PREF_FLING_CURVE_FUNCTION_Y2,
-                                 PREF_FLING_CURVE_THRESHOLD_VELOCITY,
-                                 PREF_FLING_CURVE_MAXIMUM_VELOCITY,
-                                 PREF_FLING_CURVE_NEWTON_ITERATIONS };
-
-        PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
-            Map<String, Integer> mPrefs = new HashMap<String, Integer>();
-
-            @Override public void prefValue(String name, int value) {
-                mPrefs.put(name, value);
-            }
-
-            @Override public void finish() {
-                setPrefs(mPrefs);
-            }
-        });
-    }
-
-    static final float MS_PER_FRAME = 1000.0f / 60.0f;
-    static final long NS_PER_FRAME = Math.round(1000000000f / 60f);
-    private static final float FRAMERATE_MULTIPLIER = (1000f / 60f) / MS_PER_FRAME;
-    private static final int FLING_VELOCITY_POINTS = 8;
-
-    //  The values we use for friction are based on a 16.6ms frame, adjust them to currentNsPerFrame:
-    static float getFrameAdjustedFriction(float baseFriction, long currentNsPerFrame) {
-        float framerateMultiplier = (float)currentNsPerFrame / NS_PER_FRAME;
-        return (float)Math.pow(Math.E, (Math.log(baseFriction) / framerateMultiplier));
-    }
-
-    static void setPrefs(Map<String, Integer> prefs) {
-        FRICTION_SLOW = getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850);
-        FRICTION_FAST = getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970);
-        VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER;
-        MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, GeckoAppShell.getDpi() > 300 ? 100 : 40);
-        OVERSCROLL_DECEL_RATE = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40);
-        SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300);
-        MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500);
-        FLING_ACCEL_INTERVAL = getIntPref(prefs, PREF_FLING_ACCEL_INTERVAL, 500);
-        FLING_ACCEL_BASE_MULTIPLIER = getFloatPref(prefs, PREF_FLING_ACCEL_BASE_MULTIPLIER, 1000);
-        FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER = getFloatPref(prefs, PREF_FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER, 1000);
-        FLING_CURVE_FUNCTION_X1 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_X1, 410);
-        FLING_CURVE_FUNCTION_Y1 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_Y1, 0);
-        FLING_CURVE_FUNCTION_X2 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_X2, 800);
-        FLING_CURVE_FUNCTION_Y2 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_Y2, 1000);
-        FLING_CURVE_THRESHOLD_VELOCITY = getFloatPref(prefs, PREF_FLING_CURVE_THRESHOLD_VELOCITY, 30);
-        FLING_CURVE_MAXIMUM_VELOCITY = getFloatPref(prefs, PREF_FLING_CURVE_MAXIMUM_VELOCITY, 70);
-        FLING_CURVE_NEWTON_ITERATIONS = getIntPref(prefs, PREF_FLING_CURVE_NEWTON_ITERATIONS, 5);
-
-        Log.i(LOGTAG, "Prefs: " + FRICTION_SLOW + "," + FRICTION_FAST + "," + VELOCITY_THRESHOLD + ","
-                + MAX_EVENT_ACCELERATION + "," + OVERSCROLL_DECEL_RATE + "," + SNAP_LIMIT + "," + MIN_SCROLLABLE_DISTANCE);
-    }
-
-    static {
-        // set the scrolling parameters to default values on startup
-        setPrefs(null);
-    }
-
-    private enum FlingStates {
-        STOPPED,
-        PANNING,
-        FLINGING,
-    }
-
-    private enum Overscroll {
-        NONE,
-        MINUS,      // Overscrolled in the negative direction
-        PLUS,       // Overscrolled in the positive direction
-        BOTH,       // Overscrolled in both directions (page is zoomed to smaller than screen)
-    }
-
-    private final SubdocumentScrollHelper mSubscroller;
-
-    private int mOverscrollMode; /* Default to only overscrolling if we're allowed to scroll in a direction */
-    private float mFirstTouchPos;            /* Position of the first touch event on the current drag. */
-    private float mTouchPos;                 /* Position of the most recent touch event on the current drag. */
-    private float mLastTouchPos;             /* Position of the touch event before touchPos. */
-    private float mVelocity;                 /* Velocity in this direction; pixels per animation frame. */
-    private final float[] mRecentVelocities; /* Circular buffer of recent velocities since last touch start. */
-    private int mRecentVelocityCount;        /* Number of values put into mRecentVelocities (unbounded). */
-    private boolean mScrollingDisabled;      /* Whether movement on this axis is locked. */
-    private boolean mDisableSnap;            /* Whether overscroll snapping is disabled. */
-    private float mDisplacement;
-    private long mLastFlingTime;
-    private float mLastFlingVelocity;
-
-    private FlingStates mFlingState = FlingStates.STOPPED; /* The fling state we're in on this axis. */
-
-    protected abstract float getOrigin();
-    protected abstract float getViewportLength();
-    protected abstract float getPageStart();
-    protected abstract float getPageLength();
-    protected abstract float getVisibleEndOfLayerView();
-
-    Axis(SubdocumentScrollHelper subscroller) {
-        mSubscroller = subscroller;
-        mOverscrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS;
-        mRecentVelocities = new float[FLING_VELOCITY_POINTS];
-    }
-
-    // Implementors can override these to show effects when the axis overscrolls
-    protected void overscrollFling(float velocity) { }
-    protected void overscrollPan(float displacement) { }
-
-    public void setOverScrollMode(int overscrollMode) {
-        mOverscrollMode = overscrollMode;
-    }
-
-    public int getOverScrollMode() {
-        return mOverscrollMode;
-    }
-
-    private float getViewportEnd() {
-        return getOrigin() + getViewportLength();
-    }
-
-    private float getPageEnd() {
-        return getPageStart() + getPageLength();
-    }
-
-    void startTouch(float pos) {
-        mVelocity = 0.0f;
-        mScrollingDisabled = false;
-        mFirstTouchPos = mTouchPos = mLastTouchPos = pos;
-        mRecentVelocityCount = 0;
-    }
-
-    float panDistance(float currentPos) {
-        return currentPos - mFirstTouchPos;
-    }
-
-    void setScrollingDisabled(boolean disabled) {
-        mScrollingDisabled = disabled;
-    }
-
-    void saveTouchPos() {
-        mLastTouchPos = mTouchPos;
-    }
-
-    // Calculates and return the slope of the curve at given parameter t
-    float getSlope(float t) {
-        float y1 = FLING_CURVE_FUNCTION_Y1;
-        float y2 = FLING_CURVE_FUNCTION_Y2;
-
-        return (3 * y1)
-             + t * (6 * y2 - 12 * y1)
-             + t * t * (9 * y1 - 9 * y2 + 3);
-    }
-
-    // Calculates and returns the value of the bezier curve with the given parameter t and control points p1 and p2
-    float cubicBezier(float p1, float p2, float t) {
-        return (3 * t * (1 - t) * (1 - t) * p1)
-             + (3 * t * t * (1 - t) * p2)
-             + (t * t * t);
-    }
-
-    // Responsible for mapping the physical velocity to a the velocity obtained after applying bezier curve (with control points (X1,Y1) and (X2,Y2))
-    float flingCurve(float By) {
-        int ni = FLING_CURVE_NEWTON_ITERATIONS;
-        float[] guess = new float[ni];
-        float y1 = FLING_CURVE_FUNCTION_Y1;
-        float y2 = FLING_CURVE_FUNCTION_Y2;
-        guess[0] = By;
-
-        for (int i = 1; i < ni; i++) {
-            guess[i] = guess[i - 1] - (cubicBezier(y1, y2, guess[i - 1]) - By) / getSlope(guess[i - 1]);
-        }
-        // guess[4] is the final approximate root the cubic equation.
-        float t = guess[4];
-
-        float x1 = FLING_CURVE_FUNCTION_X1;
-        float x2 = FLING_CURVE_FUNCTION_X2;
-        return cubicBezier(x1, x2, t);
-    }
-
-    void updateWithTouchAt(float pos, float timeDelta) {
-        float curveVelocityThreshold = FLING_CURVE_THRESHOLD_VELOCITY * GeckoAppShell.getDpi() * MS_PER_FRAME;
-        float maxVelocity = FLING_CURVE_MAXIMUM_VELOCITY * GeckoAppShell.getDpi() * MS_PER_FRAME;
-
-        float newVelocity = (mTouchPos - pos) / timeDelta * MS_PER_FRAME;
-
-        if (Math.abs(newVelocity) > curveVelocityThreshold && Math.abs(newVelocity) < maxVelocity) {
-            float sign = Math.signum(newVelocity);
-            newVelocity = newVelocity * sign;
-            float scale = maxVelocity - curveVelocityThreshold;
-            float functInp = (newVelocity - curveVelocityThreshold) / scale;
-            float functOut = flingCurve(functInp);
-            newVelocity = functOut * scale + curveVelocityThreshold;
-            newVelocity = newVelocity * sign;
-        }
-
-        mRecentVelocities[mRecentVelocityCount % FLING_VELOCITY_POINTS] = newVelocity;
-        mRecentVelocityCount++;
-
-        // If there's a direction change, or current velocity is very low,
-        // allow setting of the velocity outright. Otherwise, use the current
-        // velocity and a maximum change factor to set the new velocity.
-        boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f / FRAMERATE_MULTIPLIER;
-        boolean directionChange = (mVelocity > 0) != (newVelocity > 0);
-        if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
-            mVelocity = newVelocity;
-        } else {
-            float maxChange = Math.abs(mVelocity * timeDelta * MAX_EVENT_ACCELERATION);
-            mVelocity = Math.min(mVelocity + maxChange, Math.max(mVelocity - maxChange, newVelocity));
-        }
-
-        mTouchPos = pos;
-    }
-
-    boolean overscrolled() {
-        return getOverscroll() != Overscroll.NONE;
-    }
-
-    private Overscroll getOverscroll() {
-        boolean minus = (getOrigin() < getPageStart());
-        boolean plus = (getViewportEnd() > getPageEnd());
-        if (minus && plus) {
-            return Overscroll.BOTH;
-        } else if (minus) {
-            return Overscroll.MINUS;
-        } else if (plus) {
-            return Overscroll.PLUS;
-        } else {
-            return Overscroll.NONE;
-        }
-    }
-
-    // Returns the amount that the page has been overscrolled. If the page hasn't been
-    // overscrolled on this axis, returns 0.
-    private float getExcess() {
-        switch (getOverscroll()) {
-        case MINUS:     return getPageStart() - getOrigin();
-        case PLUS:      return getViewportEnd() - getPageEnd();
-        case BOTH:      return (getViewportEnd() - getPageEnd()) + (getPageStart() - getOrigin());
-        default:        return 0.0f;
-        }
-    }
-
-    /*
-     * Returns true if the page is zoomed in to some degree along this axis such that scrolling is
-     * possible and this axis has not been scroll locked while panning. Otherwise, returns false.
-     */
-    boolean scrollable() {
-        // If we're scrolling a subdocument, ignore the viewport length restrictions (since those
-        // apply to the top-level document) and only take into account axis locking.
-        if (mSubscroller.scrolling()) {
-            return !mScrollingDisabled;
-        }
-
-        // if we are axis locked, return false
-        if (mScrollingDisabled) {
-            return false;
-        }
-
-        // there is scrollable space, and we're not disabled, or the document fits the viewport
-        // but we always allow overscroll anyway
-        return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE ||
-               getOverScrollMode() == View.OVER_SCROLL_ALWAYS;
-    }
-
-    /*
-     * Returns the resistance, as a multiplier, that should be taken into account when
-     * tracking or pinching.
-     */
-    float getEdgeResistance(boolean forPinching) {
-        float excess = getExcess();
-        if (excess > 0.0f && (getOverscroll() == Overscroll.BOTH || !forPinching)) {
-            // excess can be greater than viewport length, but the resistance
-            // must never drop below 0.0
-            return Math.max(0.0f, SNAP_LIMIT - excess / getViewportLength());
-        }
-        return 1.0f;
-    }
-
-    /* Returns the velocity. If the axis is locked, returns 0. */
-    float getRealVelocity() {
-        return scrollable() ? mVelocity : 0f;
-    }
-
-    void startPan() {
-        mFlingState = FlingStates.PANNING;
-    }
-
-    private float calculateFlingVelocity() {
-        int usablePoints = Math.min(mRecentVelocityCount, FLING_VELOCITY_POINTS);
-        if (usablePoints <= 1) {
-            return mVelocity;
-        }
-        float average = 0;
-        for (int i = 0; i < usablePoints; i++) {
-            average += mRecentVelocities[i];
-        }
-        return average / usablePoints;
-    }
-
-    float accelerate(float velocity, float lastFlingVelocity) {
-        return (FLING_ACCEL_BASE_MULTIPLIER * velocity + FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER * lastFlingVelocity);
-    }
-
-    void startFling(boolean stopped) {
-        mDisableSnap = mSubscroller.scrolling();
-
-        if (stopped) {
-            mFlingState = FlingStates.STOPPED;
-        } else {
-            long now = SystemClock.uptimeMillis();
-            mVelocity = calculateFlingVelocity();
-
-            if ((now - mLastFlingTime < FLING_ACCEL_INTERVAL) && Math.signum(mVelocity) == Math.signum(mLastFlingVelocity)) {
-                mVelocity = accelerate(mVelocity, mLastFlingVelocity);
-            }
-            mFlingState = FlingStates.FLINGING;
-            mLastFlingVelocity = mVelocity;
-            mLastFlingTime = now;
-        }
-    }
-
-    /* Advances a fling animation by one step. */
-    boolean advanceFling(long realNsPerFrame) {
-        if (mFlingState != FlingStates.FLINGING) {
-            return false;
-        }
-        if (mSubscroller.scrolling() && !mSubscroller.lastScrollSucceeded()) {
-            // if the subdocument stopped scrolling, it's because it reached the end
-            // of the subdocument. we don't do overscroll on subdocuments, so there's
-            // no point in continuing this fling.
-            return false;
-        }
-
-        float excess = getExcess();
-        Overscroll overscroll = getOverscroll();
-        boolean decreasingOverscroll = false;
-        if ((overscroll == Overscroll.MINUS && mVelocity > 0) ||
-            (overscroll == Overscroll.PLUS && mVelocity < 0))
-        {
-            decreasingOverscroll = true;
-        }
-
-        if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) {
-            // If we aren't overscrolled, just apply friction.
-            if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
-                mVelocity *= getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame);
-            } else {
-                float t = mVelocity / VELOCITY_THRESHOLD;
-                mVelocity *= FloatUtils.interpolate(getFrameAdjustedFriction(FRICTION_SLOW, realNsPerFrame),
-                                                    getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame), t);
-            }
-        } else {
-            // Otherwise, decrease the velocity linearly.
-            float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
-            float overscrollDecelRate = getFrameAdjustedFriction(OVERSCROLL_DECEL_RATE, realNsPerFrame);
-            if (overscroll == Overscroll.MINUS) {
-                mVelocity = Math.min((mVelocity + overscrollDecelRate) * elasticity, 0.0f);
-            } else { // must be Overscroll.PLUS
-                mVelocity = Math.max((mVelocity - overscrollDecelRate) * elasticity, 0.0f);
-            }
-        }
-
-        return true;
-    }
-
-    void stopFling() {
-        mVelocity = 0.0f;
-        mFlingState = FlingStates.STOPPED;
-    }
-
-    // Performs displacement of the viewport position according to the current velocity.
-    void displace() {
-        // if this isn't scrollable just return
-        if (!scrollable())
-            return;
-
-        if (mFlingState == FlingStates.PANNING)
-            mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(false);
-        else
-            mDisplacement += mVelocity * getEdgeResistance(false);
-
-        // if overscroll is disabled and we're trying to overscroll, reset the displacement
-        // to remove any excess. Using getExcess alone isn't enough here since it relies on
-        // getOverscroll which doesn't take into account any new displacment being applied.
-        // If we using a subscroller, we don't want to alter the scrolling being done
-        if (getOverScrollMode() == View.OVER_SCROLL_NEVER && !mSubscroller.scrolling()) {
-            float originalDisplacement = mDisplacement;
-
-            if (mDisplacement + getOrigin() < getPageStart()) {
-                mDisplacement = getPageStart() - getOrigin();
-            } else if (mDisplacement + getOrigin() + getVisibleEndOfLayerView() > getPageEnd()) {
-                mDisplacement = getPageEnd() - getOrigin() - getVisibleEndOfLayerView();
-            }
-
-            // Return the amount of overscroll so that the overscroll controller can draw it for us
-            if (originalDisplacement != mDisplacement) {
-                if (mFlingState == FlingStates.FLINGING) {
-                    overscrollFling(mVelocity / MS_PER_FRAME * 1000);
-                    stopFling();
-                } else if (mFlingState == FlingStates.PANNING) {
-                    overscrollPan(originalDisplacement - mDisplacement);
-                }
-            }
-        }
-    }
-
-    float resetDisplacement() {
-        float d = mDisplacement;
-        mDisplacement = 0.0f;
-        return d;
-    }
-
-    void setAutoscrollVelocity(float velocity) {
-        if (mFlingState != FlingStates.STOPPED) {
-            Log.e(LOGTAG, "Setting autoscroll velocity while in a fling is not allowed!");
-            return;
-        }
-        mVelocity = velocity;
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/BufferedImage.java
+++ /dev/null
@@ -1,94 +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 org.mozilla.gecko.mozglue.DirectBufferAllocator;
-
-import android.graphics.Bitmap;
-import android.util.Log;
-
-import java.nio.ByteBuffer;
-
-/** A buffered image that simply saves a buffer of pixel data. */
-public class BufferedImage {
-    private ByteBuffer mBuffer;
-    private Bitmap mBitmap;
-    private IntSize mSize;
-    private int mFormat;
-
-    private static final String LOGTAG = "GeckoBufferedImage";
-
-    /** Creates an empty buffered image */
-    public BufferedImage() {
-        mSize = new IntSize(0, 0);
-    }
-
-    /** Creates a buffered image from an Android bitmap. */
-    public BufferedImage(Bitmap bitmap) {
-        mFormat = bitmapConfigToFormat(bitmap.getConfig());
-        mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
-        mBitmap = bitmap;
-    }
-
-    private synchronized void freeBuffer() {
-        if (mBuffer != null) {
-            mBuffer = DirectBufferAllocator.free(mBuffer);
-        }
-    }
-
-    public void destroy() {
-        try {
-            freeBuffer();
-        } catch (Exception ex) {
-            Log.e(LOGTAG, "error clearing buffer: ", ex);
-        }
-    }
-
-    public ByteBuffer getBuffer() {
-        if (mBuffer == null) {
-            int bpp = bitsPerPixelForFormat(mFormat);
-            mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp);
-            mBitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
-            mBitmap = null;
-        }
-        return mBuffer;
-    }
-
-    public IntSize getSize() { return mSize; }
-    public int getFormat() { return mFormat; }
-
-    public static final int FORMAT_INVALID = -1;
-    public static final int FORMAT_ARGB32 = 0;
-    public static final int FORMAT_RGB24 = 1;
-    public static final int FORMAT_A8 = 2;
-    public static final int FORMAT_A1 = 3;
-    public static final int FORMAT_RGB16_565 = 4;
-
-    private static int bitsPerPixelForFormat(int format) {
-        switch (format) {
-        case FORMAT_A1:          return 1;
-        case FORMAT_A8:          return 8;
-        case FORMAT_RGB16_565:   return 16;
-        case FORMAT_RGB24:       return 24;
-        case FORMAT_ARGB32:      return 32;
-        default:
-            throw new RuntimeException("Unknown Cairo format");
-        }
-    }
-
-    private static int bitmapConfigToFormat(Bitmap.Config config) {
-        if (config == null)
-            return FORMAT_ARGB32;    /* Droid Pro fix. */
-
-        switch (config) {
-        case ALPHA_8:   return FORMAT_A8;
-        case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported");
-        case ARGB_8888: return FORMAT_ARGB32;
-        case RGB_565:   return FORMAT_RGB16_565;
-        default:        throw new RuntimeException("Unknown Skia bitmap config");
-        }
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/BufferedImageGLInfo.java
+++ /dev/null
@@ -1,35 +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 javax.microedition.khronos.opengles.GL10;
-
-/** Information needed to render buffered bitmaps using OpenGL ES. */
-public class BufferedImageGLInfo {
-    public final int internalFormat;
-    public final int format;
-    public final int type;
-
-    public BufferedImageGLInfo(int bufferedImageFormat) {
-        switch (bufferedImageFormat) {
-        case BufferedImage.FORMAT_ARGB32:
-            internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE;
-            break;
-        case BufferedImage.FORMAT_RGB24:
-            internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE;
-            break;
-        case BufferedImage.FORMAT_RGB16_565:
-            internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5;
-            break;
-        case BufferedImage.FORMAT_A8:
-        case BufferedImage.FORMAT_A1:
-            throw new RuntimeException("BufferedImage FORMAT_A1 and FORMAT_A8 unsupported");
-        default:
-            throw new RuntimeException("Unknown BufferedImage format");
-        }
-    }
-}
-
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
+++ /dev/null
@@ -1,1473 +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 org.json.JSONObject;
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoEvent;
-import org.mozilla.gecko.PrefsHelper;
-import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
-import org.mozilla.gecko.ZoomConstraints;
-import org.mozilla.gecko.util.FloatUtils;
-import org.mozilla.gecko.util.GamepadUtils;
-import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-
-/*
- * Handles the kinetic scrolling and zooming physics for a layer controller.
- *
- * Many ideas are from Joe Hewitt's Scrollability:
- *   https://github.com/joehewitt/scrollability/
- */
-class JavaPanZoomController
-    extends GestureDetector.SimpleOnGestureListener
-    implements PanZoomController, SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener
-{
-    private static final String LOGTAG = "GeckoPanZoomController";
-
-    private static final String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
-    private static final String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
-    private static final String MESSAGE_TOUCH_LISTENER = "Tab:HasTouchListener";
-
-    // Animation stops if the velocity is below this value when overscrolled or panning.
-    private static final float STOPPED_THRESHOLD = 4.0f;
-
-    // Animation stops is the velocity is below this threshold when flinging.
-    private static final float FLING_STOPPED_THRESHOLD = 0.1f;
-
-    // Angle from axis within which we stay axis-locked
-    private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
-
-    // Axis-lock breakout angle
-    private static final double AXIS_BREAKOUT_ANGLE = Math.PI / 8.0;
-
-    // The distance the user has to pan before we consider breaking out of a locked axis
-    public static final float AXIS_BREAKOUT_THRESHOLD = 1 / 32f * GeckoAppShell.getDpi();
-
-    // The maximum amount we allow you to zoom into a page
-    private static final float MAX_ZOOM = 4.0f;
-
-    // The maximum amount we would like to scroll with the mouse
-    private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi();
-
-    // The maximum zoom factor adjustment per frame of the AUTONAV animation
-    private static final float MAX_ZOOM_DELTA = 0.125f;
-
-    // The duration of the bounce animation in ns
-    private static final int BOUNCE_ANIMATION_DURATION = 250000000;
-
-    private enum PanZoomState {
-        NOTHING,                /* no touch-start events received */
-        FLING,                  /* all touches removed, but we're still scrolling page */
-        TOUCHING,               /* one touch-start event received */
-        PANNING_LOCKED_X,       /* touch-start followed by move (i.e. panning with axis lock) X axis */
-        PANNING_LOCKED_Y,       /* as above for Y axis */
-        PANNING,                /* panning without axis lock */
-        PANNING_HOLD,           /* in panning, but not moving.
-                                 * similar to TOUCHING but after starting a pan */
-        PANNING_HOLD_LOCKED_X,  /* like PANNING_HOLD, but axis lock still in effect for X axis */
-        PANNING_HOLD_LOCKED_Y,  /* as above but for Y axis */
-        PINCHING,               /* nth touch-start, where n > 1. this mode allows pan and zoom */
-        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. */
-        AUTONAV,                /* We are scrolling using an AutonavRunnable animation. This is similar
-                                   to the FLING state except that it must be stopped manually by the code that
-                                   started it, and it's velocity can be updated while it's running. */
-    }
-
-    private enum AxisLockMode {
-        STANDARD,       /* Default axis locking mode that doesn't break out until finger release */
-        FREE,           /* No locking at all */
-        STICKY          /* Break out with hysteresis so that it feels as free as possible whilst locking */
-    }
-
-    private final PanZoomTarget mTarget;
-    private final SubdocumentScrollHelper mSubscroller;
-    private final Axis mX;
-    private final Axis mY;
-    private final TouchEventHandler mTouchEventHandler;
-    private final EventDispatcher mEventDispatcher;
-
-    /* The task that handles flings, autonav or bounces. */
-    private PanZoomRenderTask mAnimationRenderTask;
-    /* 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;
-    /* The per-frame zoom delta for the currently-running AUTONAV animation. */
-    private float mAutonavZoomDelta;
-    /* The user selected panning mode */
-    private AxisLockMode mMode;
-    /* Whether or not to wait for a double-tap before dispatching a single-tap */
-    private boolean mWaitForDoubleTap;
-    /* Used to change the scroll direction */
-    private boolean mNegateWheelScroll;
-    /* Whether the current event has been default-prevented. */
-    private boolean mDefaultPrevented;
-    /* Whether longpress events are enabled, or suppressed by robocop tests. */
-    private boolean isLongpressEnabled;
-    /* Whether longpress detection should be ignored */
-    private boolean mIgnoreLongPress;
-    /* Pointer scrolling delta, scaled by the preferred list item height which matches Android platform behavior */
-    private float mPointerScrollFactor;
-
-    // Handler to be notified when overscroll occurs
-    private Overscroll mOverscroll;
-
-    private final PrefsHelper.PrefHandler mPrefsObserver;
-
-    public JavaPanZoomController(PanZoomTarget target, View view, EventDispatcher eventDispatcher) {
-        mTarget = target;
-        mSubscroller = new SubdocumentScrollHelper(eventDispatcher);
-        mX = new AxisX(mSubscroller);
-        mY = new AxisY(mSubscroller);
-        mTouchEventHandler = new TouchEventHandler(view.getContext(), view, this);
-        isLongpressEnabled = true;
-
-        checkMainThread();
-
-        setState(PanZoomState.NOTHING);
-
-        mEventDispatcher = eventDispatcher;
-        mEventDispatcher.registerGeckoThreadListener(this,
-            MESSAGE_ZOOM_RECT,
-            MESSAGE_ZOOM_PAGE,
-            MESSAGE_TOUCH_LISTENER);
-
-        mMode = AxisLockMode.STANDARD;
-
-        String[] prefs = { "ui.scrolling.axis_lock_mode",
-                           "ui.scrolling.negate_wheel_scroll",
-                           "ui.scrolling.gamepad_dead_zone" };
-        mPrefsObserver = new PrefsHelper.PrefHandlerBase() {
-            @Override public void prefValue(String pref, String value) {
-                if (pref.equals("ui.scrolling.axis_lock_mode")) {
-                    if (value.equals("standard")) {
-                        mMode = AxisLockMode.STANDARD;
-                    } else if (value.equals("free")) {
-                        mMode = AxisLockMode.FREE;
-                    } else {
-                        mMode = AxisLockMode.STICKY;
-                    }
-                }
-            }
-
-            @Override public void prefValue(String pref, int value) {
-                if (pref.equals("ui.scrolling.gamepad_dead_zone")) {
-                    GamepadUtils.overrideDeadZoneThreshold(value / 1000f);
-                }
-            }
-
-            @Override public void prefValue(String pref, boolean value) {
-                if (pref.equals("ui.scrolling.negate_wheel_scroll")) {
-                    mNegateWheelScroll = value;
-                }
-            }
-        };
-        PrefsHelper.addObserver(prefs, mPrefsObserver);
-
-        Axis.initPrefs();
-
-        TypedValue outValue = new TypedValue();
-        if (view.getContext().getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, outValue, true)) {
-            mPointerScrollFactor = outValue.getDimension(view.getContext().getResources().getDisplayMetrics());
-        } else {
-            mPointerScrollFactor = MAX_SCROLL;
-        }
-    }
-
-    @Override
-    public void destroy() {
-        PrefsHelper.removeObserver(mPrefsObserver);
-        mEventDispatcher.unregisterGeckoThreadListener(this,
-            MESSAGE_ZOOM_RECT,
-            MESSAGE_ZOOM_PAGE,
-            MESSAGE_TOUCH_LISTENER);
-        mSubscroller.destroy();
-        mTouchEventHandler.destroy();
-    }
-
-    private final static float easeOut(float t) {
-        // ease-out approx.
-        // -(t-1)^2+1
-        t = t - 1;
-        return -t * t + 1;
-    }
-
-    private void setState(PanZoomState state) {
-        if (state != mState) {
-            GeckoAppShell.notifyObservers("PanZoom:StateChange", state.toString());
-            mState = state;
-
-            // Let the target know we've finished with it (for now)
-            if (state == PanZoomState.NOTHING) {
-                mTarget.panZoomStopped();
-            }
-        }
-    }
-
-    private ImmutableViewportMetrics getMetrics() {
-        return mTarget.getViewportMetrics();
-    }
-
-    private void checkMainThread() {
-        if (!ThreadUtils.isOnUiThread()) {
-            // log with full stack trace
-            Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
-        }
-    }
-
-    @Override
-    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"));
-                if (message.optBoolean("animate", true)) {
-                    mTarget.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            animatedZoomTo(zoomRect);
-                        }
-                    });
-                } else {
-                    mTarget.setViewportMetrics(getMetricsToZoomTo(zoomRect));
-                }
-            } else if (MESSAGE_ZOOM_PAGE.equals(event)) {
-                ImmutableViewportMetrics metrics = getMetrics();
-                RectF cssPageRect = metrics.getCssPageRect();
-
-                RectF viewableRect = metrics.getCssViewport();
-                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);
-                if (message.optBoolean("animate", true)) {
-                    mTarget.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            animatedZoomTo(r);
-                        }
-                    });
-                } else {
-                    mTarget.setViewportMetrics(getMetricsToZoomTo(r));
-                }
-            } else if (MESSAGE_TOUCH_LISTENER.equals(event)) {
-                int tabId = message.getInt("tabID");
-                final Tab tab = Tabs.getInstance().getTab(tabId);
-                // Make sure we still have a Tab
-                if (tab == null) {
-                    return;
-                }
-
-                tab.setHasTouchListeners(true);
-                mTarget.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (Tabs.getInstance().isSelectedTab(tab))
-                            mTouchEventHandler.setWaitForTouchListeners(true);
-                    }
-                });
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
-        }
-    }
-
-    /** This function MUST be called on the UI thread */
-    @Override
-    public boolean onKeyEvent(KeyEvent event) {
-        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
-            && event.getAction() == KeyEvent.ACTION_DOWN) {
-
-            switch (event.getKeyCode()) {
-            case KeyEvent.KEYCODE_ZOOM_IN:
-                return animatedScale(0.2f);
-            case KeyEvent.KEYCODE_ZOOM_OUT:
-                return animatedScale(-0.2f);
-            }
-        }
-        return false;
-    }
-
-    // Ignore MontionEvent velocity. Needed for C++APZ
-    public void onMotionEventVelocity(final long aEventTime, final float aSpeedY) {}
-
-    /** This function MUST be called on the UI thread */
-    @Override
-    public boolean onMotionEvent(MotionEvent event) {
-        switch (event.getSource() & InputDevice.SOURCE_CLASS_MASK) {
-        case InputDevice.SOURCE_CLASS_POINTER:
-            switch (event.getAction() & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_SCROLL: return handlePointerScroll(event);
-            }
-            break;
-        case InputDevice.SOURCE_CLASS_JOYSTICK:
-            switch (event.getAction() & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_MOVE: return handleJoystickNav(event);
-            }
-            break;
-        }
-        return false;
-    }
-
-    /** This function MUST be called on the UI thread */
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        return mTouchEventHandler.handleEvent(event);
-    }
-
-    boolean handleEvent(MotionEvent event, boolean defaultPrevented) {
-        mDefaultPrevented = defaultPrevented;
-
-        switch (event.getAction() & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN:   return handleTouchStart(event);
-        case MotionEvent.ACTION_MOVE:   return handleTouchMove(event);
-        case MotionEvent.ACTION_UP:     return handleTouchEnd(event);
-        case MotionEvent.ACTION_CANCEL: return handleTouchCancel(event);
-        }
-        return false;
-    }
-
-    /** This function MUST be called on the UI thread */
-    @Override
-    public void notifyDefaultActionPrevented(boolean prevented) {
-        mTouchEventHandler.handleEventListenerAction(!prevented);
-    }
-
-    /** This function must be called from the UI thread. */
-    @Override
-    public void abortAnimation() {
-        checkMainThread();
-        // this happens when gecko changes the viewport on us or if the device is rotated.
-        // if that's the case, abort any animation in progress and re-zoom so that the page
-        // snaps to edges. for other cases (where the user's finger(s) are down) don't do
-        // anything special.
-        switch (mState) {
-        case FLING:
-            mX.stopFling();
-            mY.stopFling();
-            // fall through
-        case BOUNCE:
-        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 (mTarget.getLock()) {
-                mTarget.setViewportMetrics(getValidViewportMetrics());
-                mTarget.forceRedraw(null);
-            }
-            break;
-        }
-    }
-
-    /** This function must be called on the UI thread. */
-    public void startingNewEventBlock(MotionEvent event, boolean waitingForTouchListeners) {
-        checkMainThread();
-        mSubscroller.cancel();
-        mIgnoreLongPress = false;
-        if (waitingForTouchListeners) {
-            if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
-                // this is the first touch point going down, so we enter the pending state
-                // setting the state will kill any animations in progress, possibly leaving
-                // the page in overscroll
-                setState(PanZoomState.WAITING_LISTENERS);
-            } else if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
-                // this is a second (or more) touch point going down, and we're waiting for
-                // the content listeners to respond. while we're waiting though we might end
-                // up triggering a long-press from the first touch point, which would be bad
-                // because from the user's point of view they are already in a multi-touch
-                // gesture. to prevent this from happening we set a flag that discards long-press
-                // gesture detections.
-                mIgnoreLongPress = true;
-            }
-        }
-    }
-
-    /** This must be called on the UI thread. */
-    @Override
-    public void pageRectUpdated() {
-        if (mState == PanZoomState.NOTHING) {
-            synchronized (mTarget.getLock()) {
-                ImmutableViewportMetrics validated = getValidViewportMetrics();
-                if (!getMetrics().fuzzyEquals(validated)) {
-                    // page size changed such that we are now in overscroll. snap to the
-                    // the nearest valid viewport
-                    mTarget.setViewportMetrics(validated);
-                }
-            }
-        }
-    }
-
-    /*
-     * Panning/scrolling
-     */
-
-    private boolean handleTouchStart(MotionEvent event) {
-        // user is taking control of movement, so stop
-        // any auto-movement we have going
-        stopAnimationTask();
-
-        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
-            mTarget.forceRedraw(null);
-            // fall through
-        case FLING:
-        case AUTONAV:
-        case BOUNCE:
-        case NOTHING:
-        case WAITING_LISTENERS:
-            startTouch(event.getX(0), event.getY(0), event.getEventTime());
-            return false;
-        case TOUCHING:
-        case PANNING:
-        case PANNING_LOCKED_X:
-        case PANNING_LOCKED_Y:
-        case PANNING_HOLD:
-        case PANNING_HOLD_LOCKED_X:
-        case PANNING_HOLD_LOCKED_Y:
-        case PINCHING:
-            Log.e(LOGTAG, "Received impossible touch down while in " + mState);
-            return false;
-        }
-        Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchStart");
-        return false;
-    }
-
-    private boolean handleTouchMove(MotionEvent event) {
-
-        switch (mState) {
-        case FLING:
-        case AUTONAV:
-        case BOUNCE:
-        case WAITING_LISTENERS:
-            // should never happen
-            Log.e(LOGTAG, "Received impossible touch move while in " + mState);
-            // fall through
-        case ANIMATED_ZOOM:
-        case NOTHING:
-            // may happen if user double-taps and drags without lifting after the
-            // second tap. ignore the move if this happens.
-            return false;
-
-        case TOUCHING:
-            // Don't allow panning if there is a non-root element in full-screen mode. See bug 775511 and bug 859683.
-            if (mTarget.getFullScreenState() == FullScreenState.NON_ROOT_ELEMENT && !mSubscroller.scrolling()) {
-                return false;
-            }
-            if (panDistance(event) < PanZoomController.PAN_THRESHOLD) {
-                return false;
-            }
-            cancelTouch();
-            startPanning(event.getX(0), event.getY(0), event.getEventTime());
-            track(event);
-            return true;
-
-        case PANNING_HOLD_LOCKED_X:
-            setState(PanZoomState.PANNING_LOCKED_X);
-            track(event);
-            return true;
-        case PANNING_HOLD_LOCKED_Y:
-            setState(PanZoomState.PANNING_LOCKED_Y);
-            // fall through
-        case PANNING_LOCKED_X:
-        case PANNING_LOCKED_Y:
-            track(event);
-            return true;
-
-        case PANNING_HOLD:
-            setState(PanZoomState.PANNING);
-            // fall through
-        case PANNING:
-            track(event);
-            return true;
-
-        case PINCHING:
-            // scale gesture listener will handle this
-            return false;
-        }
-        Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchMove");
-        return false;
-    }
-
-    private boolean handleTouchEnd(MotionEvent event) {
-
-        switch (mState) {
-        case FLING:
-        case AUTONAV:
-        case BOUNCE:
-        case ANIMATED_ZOOM:
-        case NOTHING:
-            // may happen if user double-taps and drags without lifting after the
-            // second tap. ignore if this happens.
-            return false;
-
-        case WAITING_LISTENERS:
-            if (!mDefaultPrevented) {
-              // should never happen
-              Log.e(LOGTAG, "Received impossible touch end while in " + mState);
-            }
-            // fall through
-        case TOUCHING:
-            // the switch into TOUCHING might have happened while the page was
-            // snapping back after overscroll. we need to finish the snap if that
-            // was the case
-            bounce();
-            return false;
-
-        case PANNING:
-        case PANNING_LOCKED_X:
-        case PANNING_LOCKED_Y:
-        case PANNING_HOLD:
-        case PANNING_HOLD_LOCKED_X:
-        case PANNING_HOLD_LOCKED_Y:
-            setState(PanZoomState.FLING);
-            fling();
-            return true;
-
-        case PINCHING:
-            setState(PanZoomState.NOTHING);
-            return true;
-        }
-        Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchEnd");
-        return false;
-    }
-
-    private boolean handleTouchCancel(MotionEvent event) {
-        cancelTouch();
-
-        // ensure we snap back if we're overscrolled
-        bounce();
-        return false;
-    }
-
-    private boolean handlePointerScroll(MotionEvent event) {
-        if (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING) {
-            float scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
-            float scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-            if (mNegateWheelScroll) {
-                scrollX *= -1.0;
-                scrollY *= -1.0;
-            }
-            scrollBy(scrollX * mPointerScrollFactor, scrollY * mPointerScrollFactor);
-            bounce();
-            return true;
-        }
-        return false;
-    }
-
-    private float filterDeadZone(MotionEvent event, int axis) {
-        return (GamepadUtils.isValueInDeadZone(event, axis) ? 0 : event.getAxisValue(axis));
-    }
-
-    private float normalizeJoystickScroll(MotionEvent event, int axis) {
-        return filterDeadZone(event, axis) * MAX_SCROLL;
-    }
-
-    private float normalizeJoystickZoom(MotionEvent event, int axis) {
-        // negate MAX_ZOOM_DELTA so that pushing up on the stick zooms in
-        return filterDeadZone(event, axis) * -MAX_ZOOM_DELTA;
-    }
-
-    // Since this event is a position-based event rather than a motion-based event, we need to
-    // set up an AUTONAV animation to keep scrolling even while we don't get events.
-    private boolean handleJoystickNav(MotionEvent event) {
-        float velocityX = normalizeJoystickScroll(event, MotionEvent.AXIS_X);
-        float velocityY = normalizeJoystickScroll(event, MotionEvent.AXIS_Y);
-        float zoomDelta = normalizeJoystickZoom(event, MotionEvent.AXIS_RZ);
-
-        if (velocityX == 0 && velocityY == 0 && zoomDelta == 0) {
-            if (mState == PanZoomState.AUTONAV) {
-                bounce(); // if not needed, this will automatically go to state NOTHING
-                return true;
-            }
-            return false;
-        }
-
-        if (mState == PanZoomState.NOTHING) {
-            setState(PanZoomState.AUTONAV);
-            startAnimationRenderTask(new AutonavRenderTask());
-        }
-        if (mState == PanZoomState.AUTONAV) {
-            mX.setAutoscrollVelocity(velocityX);
-            mY.setAutoscrollVelocity(velocityY);
-            mAutonavZoomDelta = zoomDelta;
-            return true;
-        }
-        return false;
-    }
-
-    private void startTouch(float x, float y, long time) {
-        mX.startTouch(x);
-        mY.startTouch(y);
-        setState(PanZoomState.TOUCHING);
-        mLastEventTime = time;
-    }
-
-    private void startPanning(float x, float y, long time) {
-        float dx = mX.panDistance(x);
-        float dy = mY.panDistance(y);
-        double angle = Math.atan2(dy, dx); // range [-pi, pi]
-        angle = Math.abs(angle); // range [0, pi]
-
-        // When the touch move breaks through the pan threshold, reposition the touch down origin
-        // so the page won't jump when we start panning.
-        mX.startTouch(x);
-        mY.startTouch(y);
-        mLastEventTime = time;
-
-        if (mMode == AxisLockMode.STANDARD || mMode == AxisLockMode.STICKY) {
-            if (!mX.scrollable() || !mY.scrollable()) {
-                setState(PanZoomState.PANNING);
-            } else if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) {
-                mY.setScrollingDisabled(true);
-                setState(PanZoomState.PANNING_LOCKED_X);
-            } else if (Math.abs(angle - (Math.PI / 2)) < AXIS_LOCK_ANGLE) {
-                mX.setScrollingDisabled(true);
-                setState(PanZoomState.PANNING_LOCKED_Y);
-            } else {
-                setState(PanZoomState.PANNING);
-            }
-        } else if (mMode == AxisLockMode.FREE) {
-            setState(PanZoomState.PANNING);
-        }
-    }
-
-    private float panDistance(MotionEvent move) {
-        float dx = mX.panDistance(move.getX(0));
-        float dy = mY.panDistance(move.getY(0));
-        return (float) Math.sqrt(dx * dx + dy * dy);
-    }
-
-    private void track(float x, float y, long time) {
-        float timeDelta = (time - mLastEventTime);
-        if (FloatUtils.fuzzyEquals(timeDelta, 0)) {
-            // probably a duplicate event, ignore it. using a zero timeDelta will mess
-            // up our velocity
-            return;
-        }
-        mLastEventTime = time;
-
-
-        // if we're axis-locked check if the user is trying to scroll away from the lock
-        if (mMode == AxisLockMode.STICKY) {
-            float dx = mX.panDistance(x);
-            float dy = mY.panDistance(y);
-            double angle = Math.atan2(dy, dx); // range [-pi, pi]
-            angle = Math.abs(angle); // range [0, pi]
-
-            if (Math.abs(dx) > AXIS_BREAKOUT_THRESHOLD || Math.abs(dy) > AXIS_BREAKOUT_THRESHOLD) {
-                if (mState == PanZoomState.PANNING_LOCKED_X) {
-                    if (angle > AXIS_BREAKOUT_ANGLE && angle < (Math.PI - AXIS_BREAKOUT_ANGLE)) {
-                        mY.setScrollingDisabled(false);
-                        setState(PanZoomState.PANNING);
-                    }
-                 } else if (mState == PanZoomState.PANNING_LOCKED_Y) {
-                    if (Math.abs(angle - (Math.PI / 2)) > AXIS_BREAKOUT_ANGLE) {
-                        mX.setScrollingDisabled(false);
-                        setState(PanZoomState.PANNING);
-                    }
-                }
-            }
-        }
-
-        mX.updateWithTouchAt(x, timeDelta);
-        mY.updateWithTouchAt(y, timeDelta);
-    }
-
-    private void track(MotionEvent event) {
-        mX.saveTouchPos();
-        mY.saveTouchPos();
-
-        for (int i = 0; i < event.getHistorySize(); i++) {
-            track(event.getHistoricalX(0, i),
-                  event.getHistoricalY(0, i),
-                  event.getHistoricalEventTime(i));
-        }
-        track(event.getX(0), event.getY(0), event.getEventTime());
-
-        if (stopped()) {
-            if (mState == PanZoomState.PANNING) {
-                setState(PanZoomState.PANNING_HOLD);
-            } else if (mState == PanZoomState.PANNING_LOCKED_X) {
-                setState(PanZoomState.PANNING_HOLD_LOCKED_X);
-            } else if (mState == PanZoomState.PANNING_LOCKED_Y) {
-                setState(PanZoomState.PANNING_HOLD_LOCKED_Y);
-            } else {
-                // should never happen, but handle anyway for robustness
-                Log.e(LOGTAG, "Impossible case " + mState + " when stopped in track");
-                setState(PanZoomState.PANNING_HOLD);
-            }
-        }
-
-        mX.startPan();
-        mY.startPan();
-        updatePosition();
-    }
-
-    private void scrollBy(float dx, float dy) {
-        mTarget.scrollBy(dx, dy);
-    }
-
-    private void fling() {
-        updatePosition();
-
-        stopAnimationTask();
-
-        boolean stopped = stopped();
-        mX.startFling(stopped);
-        mY.startFling(stopped);
-
-        startAnimationRenderTask(new FlingRenderTask());
-    }
-
-    /* Performs a bounce-back animation to the given viewport metrics. */
-    private void bounce(ImmutableViewportMetrics metrics, PanZoomState state) {
-        stopAnimationTask();
-
-        ImmutableViewportMetrics bounceStartMetrics = getMetrics();
-        if (bounceStartMetrics.fuzzyEquals(metrics)) {
-            setState(PanZoomState.NOTHING);
-            return;
-        }
-
-        setState(state);
-
-        // 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.
-        mTarget.setAnimationTarget(metrics);
-        startAnimationRenderTask(new BounceRenderTask(bounceStartMetrics, metrics));
-    }
-
-    /* Performs a bounce-back animation to the nearest valid viewport metrics. */
-    private void bounce() {
-        bounce(getValidViewportMetrics(), PanZoomState.BOUNCE);
-    }
-
-    /* Starts the fling or bounce animation. */
-    private void startAnimationRenderTask(final PanZoomRenderTask task) {
-        if (mAnimationRenderTask != null) {
-            Log.e(LOGTAG, "Attempted to start a new task without canceling the old one!");
-            stopAnimationTask();
-        }
-
-        mAnimationRenderTask = task;
-        mTarget.postRenderTask(mAnimationRenderTask);
-    }
-
-    /* Stops the fling or bounce animation. */
-    private void stopAnimationTask() {
-        if (mAnimationRenderTask != null) {
-            mAnimationRenderTask.terminate();
-            mTarget.removeRenderTask(mAnimationRenderTask);
-            mAnimationRenderTask = null;
-        }
-    }
-
-    private float getVelocity() {
-        float xvel = mX.getRealVelocity();
-        float yvel = mY.getRealVelocity();
-        return (float) Math.sqrt(xvel * xvel + yvel * yvel);
-    }
-
-    @Override
-    public PointF getVelocityVector() {
-        return new PointF(mX.getRealVelocity(), mY.getRealVelocity());
-    }
-
-    private boolean stopped() {
-        return getVelocity() < STOPPED_THRESHOLD;
-    }
-
-    PointF resetDisplacement() {
-        return new PointF(mX.resetDisplacement(), mY.resetDisplacement());
-    }
-
-    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 (!mDefaultPrevented && !mSubscroller.scrollBy(displacement)) {
-            synchronized (mTarget.getLock()) {
-                scrollBy(displacement.x, displacement.y);
-            }
-        }
-    }
-
-    /**
-     * This class is an implementation of RenderTask which enforces its implementor to run in the UI thread.
-     *
-     */
-    private abstract class PanZoomRenderTask extends RenderTask {
-
-        /**
-         * the time when the current frame was started in ns.
-         */
-        protected long mCurrentFrameStartTime;
-        /**
-         * The current frame duration in ns.
-         */
-        protected long mLastFrameTimeDelta;
-
-        private final Runnable mRunnable = new Runnable() {
-            @Override
-            public final void run() {
-                if (mContinueAnimation) {
-                    animateFrame();
-                }
-            }
-        };
-
-        private boolean mContinueAnimation = true;
-
-        public PanZoomRenderTask() {
-            super(false);
-        }
-
-        @Override
-        protected final boolean internalRun(long timeDelta, long currentFrameStartTime) {
-
-            mCurrentFrameStartTime = currentFrameStartTime;
-            mLastFrameTimeDelta = timeDelta;
-
-            mTarget.post(mRunnable);
-            return mContinueAnimation;
-        }
-
-        /**
-         * The method subclasses must override. This method is run on the UI thread thanks to internalRun
-         */
-        protected abstract void animateFrame();
-
-        /**
-         * Terminate the animation.
-         */
-        public void terminate() {
-            mContinueAnimation = false;
-        }
-    }
-
-    private class AutonavRenderTask extends PanZoomRenderTask {
-        public AutonavRenderTask() {
-            super();
-        }
-
-        @Override
-        protected void animateFrame() {
-            if (mState != PanZoomState.AUTONAV) {
-                finishAnimation();
-                return;
-            }
-
-            updatePosition();
-            synchronized (mTarget.getLock()) {
-                mTarget.setViewportMetrics(applyZoomDelta(getMetrics(), mAutonavZoomDelta));
-            }
-        }
-    }
-
-    /* The task that performs the bounce animation. */
-    private class BounceRenderTask extends PanZoomRenderTask {
-
-        /*
-         * The viewport metrics that represent the start and end of the bounce-back animation,
-         * respectively.
-         */
-        private final ImmutableViewportMetrics mBounceStartMetrics;
-        private final ImmutableViewportMetrics mBounceEndMetrics;
-        // How long ago this bounce was started in ns.
-        private long mBounceDuration;
-
-        BounceRenderTask(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) {
-            super();
-            mBounceStartMetrics = startMetrics;
-            mBounceEndMetrics = endMetrics;
-        }
-
-        @Override
-        protected void animateFrame() {
-            /*
-             * The pan/zoom controller might have signaled to us that it wants to abort the
-             * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
-             * out.
-             */
-            if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) {
-                finishAnimation();
-                return;
-            }
-
-            /* Perform the next frame of the bounce-back animation. */
-            mBounceDuration = mCurrentFrameStartTime - getStartTime();
-            if (mBounceDuration < BOUNCE_ANIMATION_DURATION) {
-                advanceBounce();
-                return;
-            }
-
-            /* 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 (mTarget.getLock()) {
-                float t = easeOut((float)mBounceDuration / BOUNCE_ANIMATION_DURATION);
-                ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
-                mTarget.setViewportMetrics(newMetrics.setPageRectFrom(getMetrics()));
-            }
-        }
-
-        /* Concludes a bounce animation and snaps the viewport into place. */
-        private void finishBounce() {
-            synchronized (mTarget.getLock()) {
-                mTarget.setViewportMetrics(mBounceEndMetrics.setPageRectFrom(getMetrics()));
-            }
-        }
-    }
-
-    // The callback that performs the fling animation.
-    private class FlingRenderTask extends PanZoomRenderTask {
-
-        public FlingRenderTask() {
-            super();
-        }
-
-        @Override
-        protected void animateFrame() {
-            /*
-             * The pan/zoom controller might have signaled to us that it wants to abort the
-             * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
-             * out.
-             */
-            if (mState != PanZoomState.FLING) {
-                finishAnimation();
-                return;
-            }
-
-            /* Advance flings, if necessary. */
-            boolean flingingX = mX.advanceFling(mLastFrameTimeDelta);
-            boolean flingingY = mY.advanceFling(mLastFrameTimeDelta);
-
-            boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
-
-            /* If we're still flinging in any direction, update the origin. */
-            if (flingingX || flingingY) {
-                updatePosition();
-
-                /*
-                 * Check to see if we're still flinging with an appreciable velocity. The threshold is
-                 * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
-                 * coast smoothly to a stop when not. In other words, require a greater velocity to
-                 * maintain the fling once we enter overscroll.
-                 */
-                float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
-                if (getVelocity() >= threshold) {
-                    // we're still flinging
-                    return;
-                }
-
-                mX.stopFling();
-                mY.stopFling();
-            }
-
-            /* Perform a bounce-back animation if overscrolled. */
-            if (overscrolled) {
-                bounce();
-            } else {
-                finishAnimation();
-                setState(PanZoomState.NOTHING);
-            }
-        }
-    }
-
-    private void finishAnimation() {
-        checkMainThread();
-
-        stopAnimationTask();
-
-        // Force a viewport synchronisation
-        mTarget.forceRedraw(null);
-    }
-
-    /* Returns the nearest viewport metrics with no overscroll visible. */
-    private ImmutableViewportMetrics getValidViewportMetrics() {
-        return getValidViewportMetrics(getMetrics());
-    }
-
-    private ImmutableViewportMetrics getValidViewportMetrics(ImmutableViewportMetrics viewportMetrics) {
-        /* First, we adjust the zoom factor so that we can make no overscrolled area visible. */
-        float zoomFactor = viewportMetrics.zoomFactor;
-        RectF pageRect = viewportMetrics.getPageRect();
-        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 = mTarget.getZoomConstraints();
-
-        if (constraints.getMinZoom() > 0 || !constraints.getAllowZoom()) {
-            minZoomFactor = constraints.getMinZoom();
-        }
-        if (constraints.getMaxZoom() > 0 || !constraints.getAllowZoom()) {
-            maxZoomFactor = constraints.getMaxZoom();
-        }
-
-        // Ensure minZoomFactor keeps the page at least as big as the viewport.
-        if (pageRect.width() > 0) {
-            float pageWidth = pageRect.width();
-            float scaleFactor = viewport.width() / pageWidth;
-            minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor);
-            if (viewport.width() > pageWidth)
-                focusX = 0.0f;
-        }
-        if (pageRect.height() > 0) {
-            float pageHeight = pageRect.height();
-            float scaleFactor = viewport.height() / pageHeight;
-            minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor);
-            if (viewport.height() > pageHeight)
-                focusY = 0.0f;
-        }
-
-        maxZoomFactor = Math.max(maxZoomFactor, minZoomFactor);
-
-        if (zoomFactor < minZoomFactor) {
-            // if one (or both) of the page dimensions is smaller than the viewport,
-            // zoom using the top/left as the focus on that axis. this prevents the
-            // scenario where, if both dimensions are smaller than the viewport, but
-            // by different scale factors, we end up scrolled to the end on one axis
-            // after applying the scale
-            PointF center = new PointF(focusX, focusY);
-            viewportMetrics = viewportMetrics.scaleTo(minZoomFactor, center);
-        } else if (zoomFactor > maxZoomFactor) {
-            PointF center = new PointF(viewport.width() / 2.0f, viewport.height() / 2.0f);
-            viewportMetrics = viewportMetrics.scaleTo(maxZoomFactor, center);
-        }
-
-        /* Now we pan to the right origin. */
-        viewportMetrics = viewportMetrics.clamp();
-
-        return viewportMetrics;
-    }
-
-    private class AxisX extends Axis {
-        AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); }
-        @Override
-        public float getOrigin() { return getMetrics().viewportRectLeft; }
-        @Override
-        protected float getViewportLength() { return getMetrics().getWidth(); }
-        @Override
-        protected float getPageStart() { return getMetrics().pageRectLeft; }
-        @Override
-        protected float getPageLength() { return getMetrics().getPageWidth(); }
-        @Override
-        protected float getVisibleEndOfLayerView() {
-            return mTarget.getVisibleEndOfLayerView().x;
-        }
-        @Override
-        protected void overscrollFling(final float velocity) {
-            if (mOverscroll != null) {
-                mOverscroll.setVelocity(velocity, Overscroll.Axis.X);
-            }
-        }
-        @Override
-        protected void overscrollPan(final float distance) {
-            if (mOverscroll != null) {
-                mOverscroll.setDistance(distance, Overscroll.Axis.X);
-            }
-        }
-    }
-
-    private class AxisY extends Axis {
-        AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); }
-        @Override
-        public float getOrigin() { return getMetrics().viewportRectTop; }
-        @Override
-        protected float getViewportLength() { return getMetrics().getHeight(); }
-        @Override
-        protected float getPageStart() { return getMetrics().pageRectTop; }
-        @Override
-        protected float getPageLength() { return getMetrics().getPageHeight(); }
-        @Override
-        protected float getVisibleEndOfLayerView() {
-            return mTarget.getVisibleEndOfLayerView().y;
-        }
-        @Override
-        protected void overscrollFling(final float velocity) {
-            if (mOverscroll != null) {
-                mOverscroll.setVelocity(velocity, Overscroll.Axis.Y);
-            }
-        }
-        @Override
-        protected void overscrollPan(final float distance) {
-            if (mOverscroll != null) {
-                mOverscroll.setDistance(distance, Overscroll.Axis.Y);
-            }
-        }
-    }
-
-    /*
-     * Zooming
-     */
-    @Override
-    public boolean onScaleBegin(SimpleScaleGestureDetector detector) {
-        if (mState == PanZoomState.ANIMATED_ZOOM)
-            return false;
-
-        if (!mTarget.getZoomConstraints().getAllowZoom())
-            return false;
-
-        setState(PanZoomState.PINCHING);
-        mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
-        cancelTouch();
-
-        final GeckoEvent event = GeckoEvent.createNativeGestureEvent(
-                GeckoEvent.ACTION_MAGNIFY_START, mLastZoomFocus, getMetrics().zoomFactor);
-        if (event != null) {
-            GeckoAppShell.sendEventToGecko(event);
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean onScale(SimpleScaleGestureDetector detector) {
-        if (mTarget.getFullScreenState() != FullScreenState.NONE)
-            return false;
-
-        if (mState != PanZoomState.PINCHING)
-            return false;
-
-        float prevSpan = detector.getPreviousSpan();
-        if (FloatUtils.fuzzyEquals(prevSpan, 0.0f)) {
-            // let's eat this one to avoid setting the new zoom to infinity (bug 711453)
-            return true;
-        }
-
-        synchronized (mTarget.getLock()) {
-            float zoomFactor = getAdjustedZoomFactor(detector.getCurrentSpan() / prevSpan);
-            scrollBy(mLastZoomFocus.x - detector.getFocusX(),
-                     mLastZoomFocus.y - detector.getFocusY());
-            mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
-            ImmutableViewportMetrics target = getMetrics().scaleTo(zoomFactor, mLastZoomFocus);
-
-            // If overscroll is disabled, prevent zooming outside the normal document pans.
-            if (mX.getOverScrollMode() == View.OVER_SCROLL_NEVER || mY.getOverScrollMode() == View.OVER_SCROLL_NEVER) {
-                target = getValidViewportMetrics(target);
-            }
-            mTarget.setViewportMetrics(target);
-        }
-
-        final GeckoEvent event = GeckoEvent.createNativeGestureEvent(
-                GeckoEvent.ACTION_MAGNIFY, mLastZoomFocus, getMetrics().zoomFactor);
-        if (event != null) {
-            GeckoAppShell.sendEventToGecko(event);
-        }
-
-        return true;
-    }
-
-    private ImmutableViewportMetrics applyZoomDelta(ImmutableViewportMetrics metrics, float zoomDelta) {
-        float oldZoom = metrics.zoomFactor;
-        float newZoom = oldZoom + zoomDelta;
-        float adjustedZoom = getAdjustedZoomFactor(newZoom / oldZoom);
-        // since we don't have a particular focus to zoom to, just use the center
-        PointF center = new PointF(metrics.getWidth() / 2.0f, metrics.getHeight() / 2.0f);
-        metrics = metrics.scaleTo(adjustedZoom, center);
-        return metrics;
-    }
-
-    private boolean animatedScale(float zoomDelta) {
-        if (mState != PanZoomState.NOTHING && mState != PanZoomState.BOUNCE) {
-            return false;
-        }
-        synchronized (mTarget.getLock()) {
-            ImmutableViewportMetrics metrics = applyZoomDelta(getMetrics(), zoomDelta);
-            bounce(getValidViewportMetrics(metrics), PanZoomState.BOUNCE);
-        }
-        return true;
-    }
-
-    private float getAdjustedZoomFactor(float zoomRatio) {
-        /*
-         * Apply edge resistance if we're zoomed out smaller than the page size by scaling the zoom
-         * factor toward 1.0.
-         */
-        float resistance = Math.min(mX.getEdgeResistance(true), mY.getEdgeResistance(true));
-        if (zoomRatio > 1.0f)
-            zoomRatio = 1.0f + (zoomRatio - 1.0f) * resistance;
-        else
-            zoomRatio = 1.0f - (1.0f - zoomRatio) * resistance;
-
-        float newZoomFactor = getMetrics().zoomFactor * zoomRatio;
-        float minZoomFactor = 0.0f;
-        float maxZoomFactor = MAX_ZOOM;
-
-        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,
-            // such that it asymptotically reaches minZoomFactor / 2.0
-            // but never exceeds that
-            final float rate = 0.5f; // controls how quickly we approach the limit
-            float excessZoom = minZoomFactor - newZoomFactor;
-            excessZoom = 1.0f - (float)Math.exp(-excessZoom * rate);
-            newZoomFactor = minZoomFactor * (1.0f - excessZoom / 2.0f);
-        }
-
-        if (newZoomFactor > maxZoomFactor) {
-            // 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;
-        }
-
-        return newZoomFactor;
-    }
-
-    @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
-        mTarget.forceRedraw(null);
-
-        PointF point = new PointF(detector.getFocusX(), detector.getFocusY());
-        GeckoEvent event = GeckoEvent.createNativeGestureEvent(GeckoEvent.ACTION_MAGNIFY_END, point, getMetrics().zoomFactor);
-
-        if (event == null) {
-            return;
-        }
-
-        GeckoAppShell.sendEventToGecko(event);
-    }
-
-    @Override
-    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
-                // of BOUNCE) be changing rapidly and gecko will have to redraw the entire
-                // display port area. we trigger a force-redraw upon exiting these states.
-                return false;
-            default:
-                // allow redrawing in other states
-                return true;
-        }
-    }
-
-    private void sendPointToGecko(String event, MotionEvent motionEvent) {
-        String json;
-        try {
-            PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
-            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;
-        }
-
-        GeckoAppShell.notifyObservers(event, json);
-    }
-
-    @Override
-    public boolean onDown(MotionEvent motionEvent) {
-        mWaitForDoubleTap = mTarget.getZoomConstraints().getAllowDoubleTapZoom();
-        return false;
-    }
-
-    @Override
-    public void onShowPress(MotionEvent motionEvent) {
-        // If we get this, it will be followed either by a call to
-        // onSingleTapUp (if the user lifts their finger before the
-        // long-press timeout) or a call to onLongPress (if the user
-        // does not). In the former case, we want to make sure it is
-        // treated as a click. (Note that if this is called, we will
-        // not get a call to onDoubleTap).
-        mWaitForDoubleTap = false;
-    }
-
-    /**
-     * MotionEventHelper dragAsync() robocop tests can have us suppress
-     * longpress events that are spuriously created on slower test devices.
-     */
-    @Override
-    public void setIsLongpressEnabled(boolean isLongpressEnabled) {
-        this.isLongpressEnabled = isLongpressEnabled;
-    }
-
-    @Override
-    public void onLongPress(MotionEvent motionEvent) {
-        if (!isLongpressEnabled || mIgnoreLongPress) {
-            return;
-        }
-
-        GeckoEvent e = GeckoEvent.createLongPressEvent(motionEvent);
-        GeckoAppShell.sendEventToGecko(e);
-    }
-
-    @Override
-    public boolean onSingleTapUp(MotionEvent motionEvent) {
-        // When double-tapping is allowed, we have to wait to see if this is
-        // going to be a double-tap.
-        if (!mWaitForDoubleTap) {
-            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) {
-        // In cases where we don't wait for double-tap, we handle this in onSingleTapUp.
-        if (mWaitForDoubleTap) {
-            sendPointToGecko("Gesture:SingleTap", motionEvent);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onDoubleTap(MotionEvent motionEvent) {
-        sendPointToGecko("Gesture:DoubleTap", motionEvent);
-        return true;
-    }
-
-    private void cancelTouch() {
-        GeckoAppShell.notifyObservers("Gesture:CancelTouch", "");
-    }
-
-    /**
-     * Zoom to a specified rect IN CSS PIXELS.
-     *
-     * While we usually use device pixels, @zoomToRect must be specified in CSS
-     * pixels.
-     */
-    private ImmutableViewportMetrics getMetricsToZoomTo(RectF zoomToRect) {
-        final float startZoom = getMetrics().zoomFactor;
-
-        RectF viewport = getMetrics().getViewport();
-        // 1. adjust the aspect ratio of zoomToRect to match that of the current viewport,
-        // enlarging as necessary (if it gets too big, it will get shrunk in the next step).
-        // while enlarging make sure we enlarge equally on both sides to keep the target rect
-        // centered.
-        float targetRatio = viewport.width() / viewport.height();
-        float rectRatio = zoomToRect.width() / zoomToRect.height();
-        if (FloatUtils.fuzzyEquals(targetRatio, rectRatio)) {
-            // all good, do nothing
-        } else if (targetRatio < rectRatio) {
-            // need to increase zoomToRect height
-            float newHeight = zoomToRect.width() / targetRatio;
-            zoomToRect.top -= (newHeight - zoomToRect.height()) / 2;
-            zoomToRect.bottom = zoomToRect.top + newHeight;
-        } else { // targetRatio > rectRatio) {
-            // need to increase zoomToRect width
-            float newWidth = targetRatio * zoomToRect.height();
-            zoomToRect.left -= (newWidth - zoomToRect.width()) / 2;
-            zoomToRect.right = zoomToRect.left + newWidth;
-        }
-
-        float finalZoom = viewport.width() / zoomToRect.width();
-
-        ImmutableViewportMetrics finalMetrics = getMetrics();
-        finalMetrics = finalMetrics.setViewportOrigin(
-            zoomToRect.left * finalMetrics.zoomFactor,
-            zoomToRect.top * finalMetrics.zoomFactor);
-        finalMetrics = finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f));
-
-        // 2. now run getValidViewportMetrics on it, so that the target viewport is
-        // clamped down to prevent overscroll, over-zoom, and other bad conditions.
-        finalMetrics = getValidViewportMetrics(finalMetrics);
-        return finalMetrics;
-    }
-
-    private boolean animatedZoomTo(RectF zoomToRect) {
-        bounce(getMetricsToZoomTo(zoomToRect), PanZoomState.ANIMATED_ZOOM);
-        return true;
-    }
-
-    /** This function must be called from the UI thread. */
-    @Override
-    public void abortPanning() {
-        checkMainThread();
-        bounce();
-    }
-
-    @Override
-    public void setOverScrollMode(int overscrollMode) {
-        mX.setOverScrollMode(overscrollMode);
-        mY.setOverScrollMode(overscrollMode);
-    }
-
-    @Override
-    public int getOverScrollMode() {
-        return mX.getOverScrollMode();
-    }
-
-    @Override
-    public void setOverscrollHandler(final Overscroll handler) {
-        mOverscroll = handler;
-    }
-
-    @Override
-    public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift) {
-        return aMetrics.offsetViewportByAndClamp(aShift.x, aShift.y);
-    }
-}
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/LayerRenderer.java
@@ -1,33 +1,24 @@
 /* -*- 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 org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.gfx.Layer.RenderContext;
 import org.mozilla.gecko.mozglue.DirectBufferAllocator;
 
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.PointF;
-import android.graphics.Rect;
 import android.graphics.RectF;
 import android.opengl.GLES20;
-import android.os.SystemClock;
 import android.util.Log;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.FloatBuffer;
@@ -40,49 +31,31 @@ import javax.microedition.khronos.egl.EG
 
 /**
  * The layer renderer implements the rendering logic for a layer view.
  */
 public class LayerRenderer implements Tabs.OnTabsChangedListener {
     private static final String LOGTAG = "GeckoLayerRenderer";
     private static final String PROFTAG = "GeckoLayerRendererProf";
 
-    /*
-     * The amount of time a frame is allowed to take to render before we declare it a dropped
-     * frame.
-     */
-    private static final int MAX_FRAME_TIME = 16;   /* 1000 ms / 60 FPS */
-
-    private static final int FRAME_RATE_METER_WIDTH = 128;
-    private static final int FRAME_RATE_METER_HEIGHT = 32;
-
-    private static final long NANOS_PER_MS = 1000000;
     private static final int NANOS_PER_SECOND = 1000000000;
 
     private static final int MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER = 5;
 
     private final LayerView mView;
-    private final ScrollbarLayer mHorizScrollLayer;
-    private final ScrollbarLayer mVertScrollLayer;
-    private final FadeRunnable mFadeRunnable;
     private ByteBuffer mCoordByteBuffer;
     private FloatBuffer mCoordBuffer;
-    private RenderContext mLastPageContext;
     private int mMaxTextureSize;
     private int mBackgroundColor;
 
     private long mLastFrameTime;
     private final CopyOnWriteArrayList<RenderTask> mTasks;
 
     private final CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
 
-    // Dropped frames display
-    private final int[] mFrameTimings;
-    private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
-
     // Render profiling output
     private int mFramesRendered;
     private float mCompleteFramesRendered;
     private boolean mProfileRender;
     private long mProfileOutputTime;
 
     private IntBuffer mPixelBuffer;
 
@@ -138,72 +111,29 @@ public class LayerRenderer implements Ta
         "uniform sampler2D sTexture;\n" +
         "void main() {\n" +
         "    gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
         "}\n";
 
     public LayerRenderer(LayerView view) {
         mView = view;
 
-        final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
-        bitmapOptions.inScaled = false;
-        Bitmap scrollbarImage =
-                BitmapUtils.decodeResource(view.getContext(), R.drawable.scrollbar, bitmapOptions);
-        IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
-        scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size);
-
         mTasks = new CopyOnWriteArrayList<RenderTask>();
         mLastFrameTime = System.nanoTime();
 
-        if (!AppConstants.MOZ_ANDROID_APZ) {
-            mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true);
-            mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false);
-            mFadeRunnable = new FadeRunnable();
-        } else {
-            // final variables need to be initialized in the constructor
-            mVertScrollLayer = null;
-            mHorizScrollLayer = null;
-            mFadeRunnable = null;
-        }
-
-        mFrameTimings = new int[60];
-        mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
-
         Tabs.registerOnTabsChangedListener(this);
         mZoomedViewListeners = new ArrayList<LayerView.ZoomedViewListener>();
     }
 
-    private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) {
-        IntSize potSize = size.nextPowerOfTwo();
-        if (size.equals(potSize)) {
-            return image;
-        }
-        // make the bitmap size a power-of-two in both dimensions if it's not already.
-        Bitmap potImage = Bitmap.createBitmap(potSize.width, potSize.height, image.getConfig());
-        new Canvas(potImage).drawBitmap(image, new Matrix(), null);
-        return potImage;
-    }
-
-    private Bitmap diagonalFlip(Bitmap image) {
-        Matrix rotation = new Matrix();
-        rotation.setValues(new float[] { 0, 1, 0, 1, 0, 0, 0, 0, 1 }); // transform (x,y) into (y,x)
-        Bitmap rotated = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), rotation, true);
-        return rotated;
-    }
-
     public void destroy() {
         if (mCoordByteBuffer != null) {
             DirectBufferAllocator.free(mCoordByteBuffer);
             mCoordByteBuffer = null;
             mCoordBuffer = null;
         }
-        if (!AppConstants.MOZ_ANDROID_APZ) {
-            mHorizScrollLayer.destroy();
-            mVertScrollLayer.destroy();
-        }
         Tabs.unregisterOnTabsChangedListener(this);
         mZoomedViewListeners.clear();
     }
 
     void onSurfaceCreated(EGLConfig config) {
         checkMonitoringEnabled();
         createDefaultProgram();
         activateDefaultProgram();
@@ -354,31 +284,16 @@ public class LayerRenderer implements Ta
             if (mCoordBuffer == null) {
                 throw new IllegalStateException();
             }
         }
         return new RenderContext(viewport, pageRect, zoomFactor,
                                  mPositionHandle, mTextureHandle, mCoordBuffer);
     }
 
-    private void updateDroppedFrames(long frameStartTime) {
-        int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS);
-
-        /* Update the running statistics. */
-        mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
-        mFrameTimingsSum += frameElapsedTime;
-        mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME;
-        mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME;
-
-        mFrameTimings[mCurrentFrame] = frameElapsedTime;
-        mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length;
-
-        int averageTime = mFrameTimingsSum / mFrameTimings.length;
-    }
-
     void checkMonitoringEnabled() {
         mProfileRender = Log.isLoggable(PROFTAG, Log.DEBUG);
     }
 
     /*
      * create a vertex shader type (GLES20.GL_VERTEX_SHADER)
      * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
      */
@@ -388,79 +303,32 @@ public class LayerRenderer implements Ta
         GLES20.glCompileShader(shader);
         return shader;
     }
 
     public Frame createFrame(ImmutableViewportMetrics metrics) {
         return new Frame(metrics);
     }
 
-    class FadeRunnable implements Runnable {
-        private boolean mStarted;
-        long mRunAt; // Would be private but we need both file access and high performance.
-
-        void scheduleStartFade(long delay) {
-            mRunAt = SystemClock.elapsedRealtime() + delay;
-            if (!mStarted) {
-                mView.postDelayed(this, delay);
-                mStarted = true;
-            }
-        }
-
-        void scheduleNextFadeFrame() {
-            if (mStarted) {
-                Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade");
-            }
-            mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps
-        }
-
-        boolean timeToFade() {
-            return !mStarted;
-        }
-
-        @Override
-        public void run() {
-            long timeDelta = mRunAt - SystemClock.elapsedRealtime();
-            if (timeDelta > 0) {
-                // the run-at time was pushed back, so reschedule
-                mView.postDelayed(this, timeDelta);
-            } else {
-                // reached the run-at time, execute
-                mStarted = false;
-                mView.requestRender();
-            }
-        }
-    }
-
     public class Frame {
         // The timestamp recording the start of this frame.
         private long mFrameStartTime;
         // A fixed snapshot of the viewport metrics that this frame is using to render content.
         private final ImmutableViewportMetrics mFrameMetrics;
         // A rendering context for page-positioned layers, and one for screen-positioned layers.
         private final RenderContext mPageContext, mScreenContext;
         // Whether a layer was updated.
         private boolean mUpdated;
-        private final Rect mPageRect;
-        private final Rect mAbsolutePageRect;
 
         public Frame(ImmutableViewportMetrics metrics) {
             mFrameMetrics = metrics;
 
             // Work out the offset due to margins
-            Layer rootLayer = mView.getLayerClient().getRoot();
             mPageContext = createPageContext(metrics);
             mScreenContext = createScreenContext(metrics);
-
-            RectF pageRect = mFrameMetrics.getPageRect();
-            mAbsolutePageRect = RectUtils.round(pageRect);
-
-            PointF origin = mFrameMetrics.getOrigin();
-            pageRect.offset(-origin.x, -origin.y);
-            mPageRect = RectUtils.round(pageRect);
         }
 
         /** This function is invoked via JNI; be careful when modifying signature. */
         @WrapForJNI(allowMultithread = true)
         public void beginDrawing() {
             mFrameStartTime = System.nanoTime();
 
             TextureReaper.get().reap();
@@ -468,38 +336,16 @@ public class LayerRenderer implements Ta
 
             mUpdated = true;
 
             Layer rootLayer = mView.getLayerClient().getRoot();
 
             // Run through pre-render tasks
             runRenderTasks(mTasks, false, mFrameStartTime);
 
-            if (!AppConstants.MOZ_ANDROID_APZ) {
-                boolean hideScrollbars = (mView.getFullScreenState() == FullScreenState.NON_ROOT_ELEMENT);
-                if (!mPageContext.fuzzyEquals(mLastPageContext) && !hideScrollbars) {
-                    // The viewport or page changed, so show the scrollbars again
-                    // as per UX decision. Don't do this if we're disabling scrolling due to
-                    // full-screen mode though.
-                    mVertScrollLayer.unfade();
-                    mHorizScrollLayer.unfade();
-                    mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
-                } else if (mFadeRunnable.timeToFade()) {
-                    final long currentMillis = SystemClock.elapsedRealtime();
-                    final boolean stillFading = mVertScrollLayer.fade(mFadeRunnable.mRunAt, currentMillis) |
-                            mHorizScrollLayer.fade(mFadeRunnable.mRunAt, currentMillis);
-                    if (stillFading) {
-                        mFadeRunnable.scheduleNextFadeFrame();
-                    }
-                }
-                mLastPageContext = mPageContext;
-                mUpdated &= mVertScrollLayer.update(mPageContext);  // called on compositor thread
-                mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread
-            }
-
             /* Update layers. */
             if (rootLayer != null) {
                 // Called on compositor thread.
                 mUpdated &= rootLayer.update(mPageContext);
             }
 
             for (Layer layer : mExtraLayers) {
                 mUpdated &= layer.update(mPageContext); // called on compositor thread
@@ -539,26 +385,16 @@ public class LayerRenderer implements Ta
 
             /* Draw any extra layers that were added (likely plugins) */
             if (mExtraLayers.size() > 0) {
                 for (Layer layer : mExtraLayers) {
                     layer.draw(mPageContext);
                 }
             }
 
-            if (!AppConstants.MOZ_ANDROID_APZ) {
-                /* Draw the vertical scrollbar. */
-                if (mPageRect.height() > mFrameMetrics.getHeight())
-                    mVertScrollLayer.draw(mPageContext);
-
-                /* Draw the horizontal scrollbar. */
-                if (mPageRect.width() > mFrameMetrics.getWidth())
-                    mHorizScrollLayer.draw(mPageContext);
-            }
-
             /* Measure how much of the screen is checkerboarding */
             Layer rootLayer = mView.getLayerClient().getRoot();
             if ((rootLayer != null) &&
                 (mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) {
                 // Calculate the incompletely rendered area of the page
                 float checkerboard =  1.0f - GeckoAppShell.computeRenderIntegrity();
 
                 PanningPerfAPI.recordCheckerboard(checkerboard);
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomController.java
@@ -1,37 +1,36 @@
 /* -*- 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 org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.EventDispatcher;
-
 import android.graphics.PointF;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.GeckoAppShell;
 
 public interface PanZoomController {
     // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
     // between the touch-down and touch-up of a click). In units of density-independent pixels.
     public static final float PAN_THRESHOLD = 1 / 16f * GeckoAppShell.getDpi();
 
     // Threshold for sending touch move events to content
     public static final float CLICK_THRESHOLD = 1 / 50f * GeckoAppShell.getDpi();
 
     static class Factory {
         static PanZoomController create(PanZoomTarget target, View view, EventDispatcher dispatcher) {
             if (org.mozilla.gecko.AppConstants.MOZ_ANDROID_APZ) {
                 return new NativePanZoomController(target, view);
             } else {
-                return new JavaPanZoomController(target, view, dispatcher);
+                throw new IllegalStateException("Must set MOZ_ANDROID_APZ");
             }
         }
     }
 
     public void destroy();
 
     public boolean onTouchEvent(MotionEvent event);
     public boolean onMotionEvent(MotionEvent event);
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/ScrollbarLayer.java
+++ /dev/null
@@ -1,430 +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 org.mozilla.gecko.util.FloatUtils;
-
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.opengl.GLES20;
-import android.util.Log;
-
-import java.nio.FloatBuffer;
-import java.nio.ByteBuffer;
-
-public class ScrollbarLayer extends Layer {
-    private static final String LOGTAG = "GeckoScrollbarLayer";
-
-    public static final long FADE_DELAY = 500; // milliseconds before fade-out starts
-    private static final float FADE_MILLIS = 250; // how long the scrollbar should take to fade
-
-    private final boolean mVertical;
-    private float mOpacity;
-    private final Rect mDirtyRect;
-    private IntSize mSize;
-
-    private int[] mTextureIDs;
-
-    private final BufferedImage mImage;
-
-    // To avoid excessive GC, declare some objects here that would otherwise
-    // be created and destroyed frequently during draw().
-    private final RectF mBarRectF;
-    private final Rect mBarRect;
-    private final float[] mCoords;
-    private final RectF mCapRectF;
-
-    private final LayerRenderer mRenderer;
-    private int mProgram;
-    private int mPositionHandle;
-    private int mTextureHandle;
-    private int mSampleHandle;
-    private int mTMatrixHandle;
-    private int mOpacityHandle;
-
-    // Fragment shader used to draw the scroll-bar with opacity
-    private static final String FRAGMENT_SHADER =
-        "precision mediump float;\n" +
-        "varying vec2 vTexCoord;\n" +
-        "uniform sampler2D sTexture;\n" +
-        "uniform float uOpacity;\n" +
-        "void main() {\n" +
-        "    gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
-        "    gl_FragColor.a *= uOpacity;\n" +
-        "}\n";
-
-    // Dimensions of the texture bitmap (will always be power-of-two)
-    private final int mTexWidth;
-    private final int mTexHeight;
-    // Some useful dimensions of the actual content in the bitmap
-    private final int mBarWidth;
-    private final int mCapLength;
-
-    private final Rect mStartCapTexCoords;  // top/left endcap coordinates
-    private final Rect mBodyTexCoords;      // 1-pixel slice of the texture to be stretched
-    private final Rect mEndCapTexCoords;    // bottom/right endcap coordinates
-
-    ScrollbarLayer(LayerRenderer renderer, Bitmap scrollbarImage, IntSize imageSize, boolean vertical) {
-        super(new IntSize(scrollbarImage.getHeight(), scrollbarImage.getWidth()));
-        mImage = new BufferedImage(scrollbarImage);
-        mRenderer = renderer;
-        mVertical = vertical;
-
-        mBarRectF = new RectF();
-        mBarRect = new Rect();
-        mCoords = new float[20];
-        mCapRectF = new RectF();
-        mDirtyRect = new Rect();
-        mSize = new IntSize(0, 0);
-
-        mTexHeight = scrollbarImage.getHeight();
-        mTexWidth = scrollbarImage.getWidth();
-
-        if (mVertical) {
-            mBarWidth = imageSize.width;
-            mCapLength = imageSize.height / 2;
-            mStartCapTexCoords = new Rect(0, mTexHeight - mCapLength, imageSize.width, mTexHeight);
-            mBodyTexCoords = new Rect(0, mTexHeight - (mCapLength + 1), imageSize.width, mTexHeight - mCapLength);
-            mEndCapTexCoords = new Rect(0, mTexHeight - imageSize.height, imageSize.width, mTexHeight - (mCapLength + 1));
-        } else {
-            mBarWidth = imageSize.height;
-            mCapLength = imageSize.width / 2;
-            mStartCapTexCoords = new Rect(0, mTexHeight - imageSize.height, mCapLength, mTexHeight);
-            mBodyTexCoords = new Rect(mCapLength, mTexHeight - imageSize.height, mCapLength + 1, mTexHeight);
-            mEndCapTexCoords = new Rect(mCapLength + 1, mTexHeight - imageSize.height, imageSize.width, mTexHeight);
-        }
-    }
-
-    private void createProgram() {
-        int vertexShader = LayerRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
-                                                    LayerRenderer.DEFAULT_VERTEX_SHADER);
-        int fragmentShader = LayerRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
-                                                      FRAGMENT_SHADER);
-
-        mProgram = GLES20.glCreateProgram();
-        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
-        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
-        GLES20.glLinkProgram(mProgram);                  // creates OpenGL program executables
-
-        // Get handles to the shaders' vPosition, aTexCoord, sTexture, and uTMatrix members.
-        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
-        mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
-        mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
-        mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix");
-        mOpacityHandle = GLES20.glGetUniformLocation(mProgram, "uOpacity");
-    }
-
-    private void activateProgram() {
-        // Add the program to the OpenGL environment
-        GLES20.glUseProgram(mProgram);
-
-        // Set the transformation matrix
-        GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false,
-                                  LayerRenderer.DEFAULT_TEXTURE_MATRIX, 0);
-
-        // Enable the arrays from which we get the vertex and texture coordinates
-        GLES20.glEnableVertexAttribArray(mPositionHandle);
-        GLES20.glEnableVertexAttribArray(mTextureHandle);
-
-        GLES20.glUniform1i(mSampleHandle, 0);
-        GLES20.glUniform1f(mOpacityHandle, mOpacity);
-    }
-
-    private void deactivateProgram() {
-        GLES20.glDisableVertexAttribArray(mTextureHandle);
-        GLES20.glDisableVertexAttribArray(mPositionHandle);
-        GLES20.glUseProgram(0);
-    }
-
-    /**
-     * Set the opacity of the scrollbar depending on how much time has
-     * passed from the given start time, current time, and the constant duration.
-     * Return true if the opacity was decreased, or false if the scrollbars
-     * are already fully faded out.
-     */
-    public boolean fade(final long startMillis, final long currentMillis) {
-        if (FloatUtils.fuzzyEquals(mOpacity, 0.0f)) {
-            return false;
-        }
-        beginTransaction(); // called on compositor thread
-        mOpacity = Math.max(1 - (currentMillis - startMillis) / FADE_MILLIS, 0.0f);
-        endTransaction();
-        return true;
-    }
-
-    /**
-     * Restore the opacity of the scrollbar to fully opaque.
-     * Return true if the opacity was changed, or false if the scrollbars
-     * are already fully opaque.
-     */
-    public boolean unfade() {
-        if (FloatUtils.fuzzyEquals(mOpacity, 1.0f)) {
-            return false;
-        }
-        beginTransaction(); // called on compositor thread
-        mOpacity = 1.0f;
-        endTransaction();
-        return true;
-    }
-
-    @Override
-    public void draw(RenderContext context) {
-        if (!initialized())
-            return;
-
-        // Create the shader program, if necessary
-        if (mProgram == 0) {
-            createProgram();
-        }
-
-        // Enable the shader program
-        mRenderer.deactivateDefaultProgram();
-        activateProgram();
-
-        GLES20.glEnable(GLES20.GL_BLEND);
-        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
-
-        if (mVertical) {
-            getVerticalRect(context, mBarRectF);
-        } else {
-            getHorizontalRect(context, mBarRectF);
-        }
-        RectUtils.round(mBarRectF, mBarRect);
-
-        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
-
-        float viewWidth = context.viewport.width();
-        float viewHeight = context.viewport.height();
-
-        mBarRectF.set(mBarRect.left, viewHeight - mBarRect.top, mBarRect.right, viewHeight - mBarRect.bottom);
-
-        // We take a 1-pixel slice from the center of the image and scale it to become the bar
-        fillRectCoordBuffer(mCoords, mBarRectF, viewWidth, viewHeight, mBodyTexCoords, mTexWidth, mTexHeight);
-
-        // Get the buffer and handles from the context
-        FloatBuffer coordBuffer = context.coordBuffer;
-        int positionHandle = mPositionHandle;
-        int textureHandle = mTextureHandle;
-
-        // Make sure we are at position zero in the buffer in case other draw methods did not
-        // clean up after themselves
-        coordBuffer.position(0);
-        coordBuffer.put(mCoords);
-
-        // Unbind any the current array buffer so we can use client side buffers
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
-
-        // Vertex coordinates are x,y,z starting at position 0 into the buffer.
-        coordBuffer.position(0);
-        GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
-
-        // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
-        coordBuffer.position(3);
-        GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
-
-        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
-        // Reset the position in the buffer for the next set of vertex and texture coordinates.
-        coordBuffer.position(0);
-        if (mVertical) {
-            // top endcap
-            mCapRectF.set(mBarRectF.left, mBarRectF.top + mCapLength, mBarRectF.right, mBarRectF.top);
-        } else {
-            // left endcap
-            mCapRectF.set(mBarRectF.left - mCapLength, mBarRectF.bottom + mBarWidth, mBarRectF.left, mBarRectF.bottom);
-        }
-
-        fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mStartCapTexCoords, mTexWidth, mTexHeight);
-        coordBuffer.put(mCoords);
-
-        // Vertex coordinates are x,y,z starting at position 0 into the buffer.
-        coordBuffer.position(0);
-        GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
-
-        // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
-        coordBuffer.position(3);
-        GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
-
-        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
-        // Reset the position in the buffer for the next set of vertex and texture coordinates.
-        coordBuffer.position(0);
-        if (mVertical) {
-            // bottom endcap
-            mCapRectF.set(mBarRectF.left, mBarRectF.bottom, mBarRectF.right, mBarRectF.bottom - mCapLength);
-        } else {
-            // right endcap
-            mCapRectF.set(mBarRectF.right, mBarRectF.bottom + mBarWidth, mBarRectF.right + mCapLength, mBarRectF.bottom);
-        }
-        fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mEndCapTexCoords, mTexWidth, mTexHeight);
-        coordBuffer.put(mCoords);
-
-        // Vertex coordinates are x,y,z starting at position 0 into the buffer.
-        coordBuffer.position(0);
-        GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
-
-        // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
-        coordBuffer.position(3);
-        GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
-
-        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
-        // Reset the position in the buffer for the next set of vertex and texture coordinates.
-        coordBuffer.position(0);
-
-        // Enable the default shader program again
-        deactivateProgram();
-        mRenderer.activateDefaultProgram();
-    }
-
-    private void getVerticalRect(RenderContext context, RectF dest) {
-        RectF viewport = context.viewport;
-        RectF pageRect = context.pageRect;
-        float viewportHeight = viewport.height();
-        float barStart = ((viewport.top - pageRect.top) * (viewportHeight / pageRect.height())) + mCapLength;
-        float barEnd = ((viewport.bottom - pageRect.top) * (viewportHeight / pageRect.height())) - mCapLength;
-        if (barStart > barEnd) {
-            float middle = (barStart + barEnd) / 2.0f;
-            barStart = barEnd = middle;
-        }
-        dest.set(viewport.width() - mBarWidth, barStart, viewport.width(), barEnd);
-    }
-
-    private void getHorizontalRect(RenderContext context, RectF dest) {
-        RectF viewport = context.viewport;
-        RectF pageRect = context.pageRect;
-        float viewportWidth = viewport.width();
-        float barStart = ((viewport.left - pageRect.left) * (viewport.width() / pageRect.width())) + mCapLength;
-        float barEnd = ((viewport.right - pageRect.left) * (viewport.width() / pageRect.width())) - mCapLength;
-        if (barStart > barEnd) {
-            float middle = (barStart + barEnd) / 2.0f;
-            barStart = barEnd = middle;
-        }
-        dest.set(barStart, viewport.height() - mBarWidth, barEnd, viewport.height());
-    }
-
-    private void validateTexture() {
-        /* Calculate the ideal texture size. This must be a power of two if
-         * the texture is repeated or OpenGL ES 2.0 isn't supported, as
-         * OpenGL ES 2.0 is required for NPOT texture support (without
-         * extensions), but doesn't support repeating NPOT textures.
-         *
-         * XXX Currently, we don't pick a GLES 2.0 context, so always round.
-         */
-        IntSize textureSize = mImage.getSize().nextPowerOfTwo();
-
-        if (!textureSize.equals(mSize)) {
-            mSize = textureSize;
-
-            // Delete the old texture
-            if (mTextureIDs != null) {
-                TextureReaper.get().add(mTextureIDs);
-                mTextureIDs = null;
-
-                // Free the texture immediately, so we don't incur a
-                // temporarily increased memory usage.
-                TextureReaper.get().reap();
-            }
-        }
-    }
-
-    @Override
-    protected void performUpdates(RenderContext context) {
-        super.performUpdates(context);
-
-        // Reallocate the texture if the size has changed
-        validateTexture();
-
-        // Don't do any work if the image has an invalid size.
-        if (!mImage.getSize().isPositive())
-            return;
-
-        // If we haven't allocated a texture, assume the whole region is dirty
-        if (mTextureIDs == null) {
-            uploadFullTexture();
-        } else {
-            uploadDirtyRect(mDirtyRect);
-        }
-
-        mDirtyRect.setEmpty();
-    }
-
-    private void uploadFullTexture() {
-        IntSize bufferSize = mImage.getSize();
-        uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height));
-    }
-
-    private void uploadDirtyRect(Rect dirtyRect) {
-        // If we have nothing to upload, just return for now
-        if (dirtyRect.isEmpty())
-            return;
-
-        // It's possible that the buffer will be null, check for that and return
-        ByteBuffer imageBuffer = mImage.getBuffer();
-        if (imageBuffer == null)
-            return;
-
-        if (mTextureIDs == null) {
-            mTextureIDs = new int[1];
-            GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
-        }
-
-        int imageFormat = mImage.getFormat();
-        BufferedImageGLInfo glInfo = new BufferedImageGLInfo(imageFormat);
-
-        bindAndSetGLParameters();
-
-        // XXX TexSubImage2D is too broken to rely on on Adreno, and very slow
-        //     on other chipsets, so we always upload the entire buffer.
-        IntSize bufferSize = mImage.getSize();
-        if (mSize.equals(bufferSize)) {
-            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width,
-                                mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
-        } else {
-            // Our texture has been expanded to the next power of two.
-            // XXX We probably never want to take this path, so throw an exception.
-            throw new RuntimeException("Buffer/image size mismatch in ScrollbarLayer!");
-        }
-    }
-
-    private void bindAndSetGLParameters() {
-        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]);
-        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
-                               GLES20.GL_LINEAR);
-        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
-                               GLES20.GL_LINEAR);
-
-        int repeatMode = GLES20.GL_CLAMP_TO_EDGE;
-        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode);
-        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode);
-    }
-
-    public void destroy() {
-        try {
-            if (mImage != null) {
-                mImage.destroy();
-            }
-        } catch (Exception ex) {
-            Log.e(LOGTAG, "error clearing buffers: ", ex);
-        }
-    }
-
-    protected int getTextureID() { return mTextureIDs[0]; }
-    protected boolean initialized() { return mImage != null && mTextureIDs != null; }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mTextureIDs != null)
-                TextureReaper.get().add(mTextureIDs);
-        } finally {
-            super.finalize();
-        }
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java
+++ /dev/null
@@ -1,322 +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 org.json.JSONException;
-
-import android.graphics.PointF;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
-import java.util.Stack;
-
-/**
- * A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector.
- *
- * This gesture detector is more reliable than the built-in ScaleGestureDetector because:
- *
- *   - It doesn't assume that pointer IDs are numbered 0 and 1.
- *
- *   - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some
- *     devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many
- *     pointers are down, with disastrous results (bug 706684).
- *
- *   - Cancelling a zoom into a pan is handled correctly.
- *
- *   - Starting with three or more fingers down, releasing fingers so that only two are down, and
- *     then performing a scale gesture is handled correctly.
- *
- *   - It doesn't take pressure into account, which results in smoother scaling.
- */
-class SimpleScaleGestureDetector {
-    private static final String LOGTAG = "GeckoSimpleScaleGestureDetector";
-
-    private final SimpleScaleGestureListener mListener;
-    private long mLastEventTime;
-    private boolean mScaleResult;
-
-    /* Information about all pointers that are down. */
-    private final LinkedList<PointerInfo> mPointerInfo;
-
-    /** Creates a new gesture detector with the given listener. */
-    SimpleScaleGestureDetector(SimpleScaleGestureListener listener) {
-        mListener = listener;
-        mPointerInfo = new LinkedList<PointerInfo>();
-    }
-
-    /** Forward touch events to this function. */
-    public void onTouchEvent(MotionEvent event) {
-        switch (event.getAction() & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN:
-            // If we get ACTION_DOWN while still tracking any pointers,
-            // something is wrong.  Cancel the current gesture and start over.
-            if (getPointersDown() > 0)
-                onTouchEnd(event);
-            onTouchStart(event);
-            break;
-        case MotionEvent.ACTION_POINTER_DOWN:
-            onTouchStart(event);
-            break;
-        case MotionEvent.ACTION_MOVE:
-            onTouchMove(event);
-            break;
-        case MotionEvent.ACTION_POINTER_UP:
-        case MotionEvent.ACTION_UP:
-        case MotionEvent.ACTION_CANCEL:
-            onTouchEnd(event);
-            break;
-        }
-    }
-
-    private int getPointersDown() {
-        return mPointerInfo.size();
-    }
-
-    private int getActionIndex(MotionEvent event) {
-        return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-            >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-    }
-
-    private void onTouchStart(MotionEvent event) {
-        mLastEventTime = event.getEventTime();
-        mPointerInfo.addFirst(PointerInfo.create(event, getActionIndex(event)));
-        if (getPointersDown() == 2) {
-            sendScaleGesture(EventType.BEGIN);
-        }
-    }
-
-    private void onTouchMove(MotionEvent event) {
-        mLastEventTime = event.getEventTime();
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            PointerInfo pointerInfo = pointerInfoForEventIndex(event, i);
-            if (pointerInfo != null) {
-                pointerInfo.populate(event, i);
-            }
-        }
-
-        if (getPointersDown() == 2) {
-            sendScaleGesture(EventType.CONTINUE);
-        }
-    }
-
-    private void onTouchEnd(MotionEvent event) {
-        mLastEventTime = event.getEventTime();
-
-        int action = event.getAction() & MotionEvent.ACTION_MASK;
-        boolean isCancel = (action == MotionEvent.ACTION_CANCEL ||
-                            action == MotionEvent.ACTION_DOWN);
-
-        int id = event.getPointerId(getActionIndex(event));
-        ListIterator<PointerInfo> iterator = mPointerInfo.listIterator();
-        while (iterator.hasNext()) {
-            PointerInfo pointerInfo = iterator.next();
-            if (!(isCancel || pointerInfo.getId() == id)) {
-                continue;
-            }
-
-            // One of the pointers we were tracking was lifted. Remove its info object from the
-            // list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this
-            // ended the gesture.
-            iterator.remove();
-            pointerInfo.recycle();
-            if (getPointersDown() == 1) {
-                sendScaleGesture(EventType.END);
-            }
-        }
-    }
-
-    /**
-     * Returns the X coordinate of the focus location (the midpoint of the two fingers). If only
-     * one finger is down, returns the location of that finger.
-     */
-    public float getFocusX() {
-        switch (getPointersDown()) {
-        case 1:
-            return mPointerInfo.getFirst().getCurrent().x;
-        case 2:
-            PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
-            return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f;
-        }
-
-        Log.e(LOGTAG, "No gesture taking place in getFocusX()!");
-        return 0.0f;
-    }
-
-    /**
-     * Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only
-     * one finger is down, returns the location of that finger.
-     */
-    public float getFocusY() {
-        switch (getPointersDown()) {
-        case 1:
-            return mPointerInfo.getFirst().getCurrent().y;
-        case 2:
-            PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
-            return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f;
-        }
-
-        Log.e(LOGTAG, "No gesture taking place in getFocusY()!");
-        return 0.0f;
-    }
-
-    /** Returns the most recent distance between the two pointers. */
-    public float getCurrentSpan() {
-        if (getPointersDown() != 2) {
-            Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!");
-            return 0.0f;
-        }
-
-        PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
-        return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent());
-    }
-
-    /** Returns the second most recent distance between the two pointers. */
-    public float getPreviousSpan() {
-        if (getPointersDown() != 2) {
-            Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!");
-            return 0.0f;
-        }
-
-        PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
-        PointF a = pointerA.getPrevious(), b = pointerB.getPrevious();
-        if (a == null || b == null) {
-            a = pointerA.getCurrent();
-            b = pointerB.getCurrent();
-        }
-
-        return PointUtils.distance(a, b);
-    }
-
-    /** Returns the time of the last event related to the gesture. */
-    public long getEventTime() {
-        return mLastEventTime;
-    }
-
-    /** Returns true if the scale gesture is in progress and false otherwise. */
-    public boolean isInProgress() {
-        return getPointersDown() == 2;
-    }
-
-    /* Sends the requested scale gesture notification to the listener. */
-    private void sendScaleGesture(EventType eventType) {
-        switch (eventType) {
-        case BEGIN:
-            mScaleResult = mListener.onScaleBegin(this);
-            break;
-        case CONTINUE:
-            if (mScaleResult) {
-                mListener.onScale(this);
-            }
-            break;
-        case END:
-            if (mScaleResult) {
-                mListener.onScaleEnd(this);
-            }
-            break;
-        }
-    }
-
-    /*
-     * Returns the pointer info corresponding to the given pointer index, or null if the pointer
-     * isn't one that's being tracked.
-     */
-    private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) {
-        int id = event.getPointerId(index);
-        for (PointerInfo pointerInfo : mPointerInfo) {
-            if (pointerInfo.getId() == id) {
-                return pointerInfo;
-            }
-        }
-        return null;
-    }
-
-    private enum EventType {
-        BEGIN,
-        CONTINUE,
-        END,
-    }
-
-    /* Encapsulates information about one of the two fingers involved in the gesture. */
-    private static class PointerInfo {
-        /* A free list that recycles pointer info objects, to reduce GC pauses. */
-        private static Stack<PointerInfo> sPointerInfoFreeList;
-
-        private int mId;
-        private PointF mCurrent, mPrevious;
-
-        private PointerInfo() {
-            // External users should use create() instead.
-        }
-
-        /* Creates or recycles a new PointerInfo instance from an event and a pointer index. */
-        public static PointerInfo create(MotionEvent event, int index) {
-            if (sPointerInfoFreeList == null) {
-                sPointerInfoFreeList = new Stack<PointerInfo>();
-            }
-
-            PointerInfo pointerInfo;
-            if (sPointerInfoFreeList.empty()) {
-                pointerInfo = new PointerInfo();
-            } else {
-                pointerInfo = sPointerInfoFreeList.pop();
-            }
-
-            pointerInfo.populate(event, index);
-            return pointerInfo;
-        }
-
-        /*
-         * Fills in the fields of this instance from the given motion event and pointer index
-         * within that event.
-         */
-        public void populate(MotionEvent event, int index) {
-            mId = event.getPointerId(index);
-            mPrevious = mCurrent;
-            mCurrent = new PointF(event.getX(index), event.getY(index));
-        }
-
-        public void recycle() {
-            mId = -1;
-            mPrevious = mCurrent = null;
-            sPointerInfoFreeList.push(this);
-        }
-
-        public int getId() { return mId; }
-        public PointF getCurrent() { return mCurrent; }
-        public PointF getPrevious() { return mPrevious; }
-
-        @Override
-        public String toString() {
-            if (mId == -1) {
-                return "(up)";
-            }
-
-            try {
-                String prevString;
-                if (mPrevious == null) {
-                    prevString = "n/a";
-                } else {
-                    prevString = PointUtils.toJSON(mPrevious).toString();
-                }
-
-                // The current position should always be non-null.
-                String currentString = PointUtils.toJSON(mCurrent).toString();
-                return "id=" + mId + " cur=" + currentString + " prev=" + prevString;
-            } catch (JSONException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    public static interface SimpleScaleGestureListener {
-        public boolean onScale(SimpleScaleGestureDetector detector);
-        public boolean onScaleBegin(SimpleScaleGestureDetector detector);
-        public void onScaleEnd(SimpleScaleGestureDetector detector);
-    }
-}
-
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/SubdocumentScrollHelper.java
+++ /dev/null
@@ -1,142 +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 org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoEvent;
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.util.GeckoEventListener;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.graphics.PointF;
-import android.os.Handler;
-import android.util.Log;
-
-class SubdocumentScrollHelper implements GeckoEventListener {
-    private static final String LOGTAG = "GeckoSubdocScroll";
-
-    private static final String MESSAGE_PANNING_OVERRIDE = "Panning:Override";
-    private static final String MESSAGE_CANCEL_OVERRIDE = "Panning:CancelOverride";
-    private static final String MESSAGE_SCROLL = "Gesture:Scroll";
-    private static final String MESSAGE_SCROLL_ACK = "Gesture:ScrollAck";
-
-    private final Handler mUiHandler;
-    private final EventDispatcher mEventDispatcher;
-
-    /* This is the amount of displacement we have accepted but not yet sent to JS; this is
-     * only valid when mOverrideScrollPending is true. */
-    private final PointF mPendingDisplacement;
-
-    /* When this is true, we're sending scroll events to JS to scroll the active subdocument. */
-    private boolean mOverridePanning;
-
-    /* When this is true, we have received an ack for the last scroll event we sent to JS, and
-     * are ready to send the next scroll event. Note we only ever have one scroll event inflight
-     * at a time. */
-    private boolean mOverrideScrollAck;
-
-    /* When this is true, we have a pending scroll that we need to send to JS; we were unable
-     * to send it when it was initially requested because mOverrideScrollAck was not true. */
-    private boolean mOverrideScrollPending;
-
-    /* When this is true, the last scroll event we sent actually did some amount of scrolling on
-     * the subdocument; we use this to decide when we have reached the end of the subdocument. */
-    private boolean mScrollSucceeded;
-
-    SubdocumentScrollHelper(EventDispatcher eventDispatcher) {
-        // mUiHandler will be bound to the UI thread since that's where this constructor runs
-        mUiHandler = new Handler();
-        mPendingDisplacement = new PointF();
-
-        mEventDispatcher = eventDispatcher;
-        mEventDispatcher.registerGeckoThreadListener(this,
-            MESSAGE_PANNING_OVERRIDE,
-            MESSAGE_CANCEL_OVERRIDE,
-            MESSAGE_SCROLL_ACK);
-    }
-
-    void destroy() {
-        mEventDispatcher.unregisterGeckoThreadListener(this,
-            MESSAGE_PANNING_OVERRIDE,
-            MESSAGE_CANCEL_OVERRIDE,
-            MESSAGE_SCROLL_ACK);
-    }
-
-    boolean scrollBy(PointF displacement) {
-        if (! mOverridePanning) {
-            return false;
-        }
-
-        if (! mOverrideScrollAck) {
-            mOverrideScrollPending = true;
-            mPendingDisplacement.x += displacement.x;
-            mPendingDisplacement.y += displacement.y;
-            return true;
-        }
-
-        JSONObject json = new JSONObject();
-        try {
-            json.put("x", displacement.x);
-            json.put("y", displacement.y);
-        } catch (JSONException e) {
-            Log.e(LOGTAG, "Error forming subwindow scroll message: ", e);
-        }
-        GeckoAppShell.notifyObservers(MESSAGE_SCROLL, json.toString());
-
-        mOverrideScrollAck = false;
-        mOverrideScrollPending = false;
-        // clear the |mPendingDisplacement| after serializing |displacement| to
-        // JSON because they might be the same object
-        mPendingDisplacement.x = 0;
-        mPendingDisplacement.y = 0;
-
-        return true;
-    }
-
-    void cancel() {
-        mOverridePanning = false;
-    }
-
-    boolean scrolling() {
-        return mOverridePanning;
-    }
-
-    boolean lastScrollSucceeded() {
-        return mScrollSucceeded;
-    }
-
-    // GeckoEventListener implementation
-
-    @Override
-    public void handleMessage(final String event, final JSONObject message) {
-        // This comes in on the Gecko thread; hand off the handling to the UI thread.
-        mUiHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    if (MESSAGE_PANNING_OVERRIDE.equals(event)) {
-                        mOverridePanning = true;
-                        mOverrideScrollAck = true;
-                        mOverrideScrollPending = false;
-                        mScrollSucceeded = true;
-                    } else if (MESSAGE_CANCEL_OVERRIDE.equals(event)) {
-                        mOverridePanning = false;
-                    } else if (MESSAGE_SCROLL_ACK.equals(event)) {
-                        mOverrideScrollAck = true;
-                        mScrollSucceeded = message.getBoolean("scrolled");
-                        if (mOverridePanning && mOverrideScrollPending) {
-                            scrollBy(mPendingDisplacement);
-                        }
-                    }
-                } catch (Exception e) {
-                    Log.e(LOGTAG, "Exception handling message", e);
-                }
-            }
-        });
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/TouchEventHandler.java
+++ /dev/null
@@ -1,307 +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 org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * This class handles incoming touch events from the user and sends them to
- * listeners in Gecko and/or performs the "default action" (asynchronous pan/zoom
- * behaviour. EVERYTHING IN THIS CLASS MUST RUN ON THE UI THREAD.
- *
- * In the following code/comments, a "block" of events refers to a contiguous
- * sequence of events that starts with a DOWN or POINTER_DOWN and goes up to
- * but not including the next DOWN or POINTER_DOWN event.
- *
- * "Dispatching" an event refers to performing the default actions for the event,
- * which at our level of abstraction just means sending it off to the gesture
- * detectors and the pan/zoom controller.
- *
- * If an event is "default-prevented" that means one or more listeners in Gecko
- * has called preventDefault() on the event, which means that the default action
- * for that event should not occur. Usually we care about a "block" of events being
- * default-prevented, which means that the DOWN/POINTER_DOWN event that started
- * the block, or the first MOVE event following that, were prevent-defaulted.
- *
- * A "default-prevented notification" is when we here in Java-land receive a notification
- * from gecko as to whether or not a block of events was default-prevented. This happens
- * at some point after the first or second event in the block is processed in Gecko.
- * This code assumes we get EXACTLY ONE default-prevented notification for each block
- * of events.
- *
- * Note that even if all events are default-prevented, we still send specific types
- * of notifications to the pan/zoom controller. The notifications are needed
- * to respond to user actions a timely manner regardless of default-prevention,
- * and fix issues like bug 749384.
- */
-final class TouchEventHandler implements Tabs.OnTabsChangedListener {
-    private static final String LOGTAG = "GeckoTouchEventHandler";
-
-    // The time limit for listeners to respond with preventDefault on touchevents
-    // before we begin panning the page
-    private final int EVENT_LISTENER_TIMEOUT = 200;
-
-    private final View mView;
-    private final GestureDetector mGestureDetector;
-    private final SimpleScaleGestureDetector mScaleGestureDetector;
-    private final JavaPanZoomController mPanZoomController;
-
-    // the queue of events that we are holding on to while waiting for a preventDefault
-    // notification
-    private final Queue<MotionEvent> mEventQueue;
-    private final ListenerTimeoutProcessor mListenerTimeoutProcessor;
-
-    // whether or not we should wait for touch listeners to respond (this state is
-    // per-tab and is updated when we switch tabs).
-    private boolean mWaitForTouchListeners;
-
-    // true if we should hold incoming events in our queue. this is re-set for every
-    // block of events, this is cleared once we find out if the block has been
-    // default-prevented or not (or we time out waiting for that).
-    private boolean mHoldInQueue;
-
-    // false if the current event block has been default-prevented. In this case,
-    // we still pass the event to both Gecko and the pan/zoom controller, but the
-    // latter will not use it to scroll content. It may still use the events for
-    // other things, such as making the dynamic toolbar visible.
-    private boolean mAllowDefaultAction;
-
-    // this next variable requires some explanation. strap yourself in.
-    //
-    // for each block of events, we do two things: (1) send the events to gecko and expect
-    // exactly one default-prevented notification in return, and (2) kick off a delayed
-    // ListenerTimeoutProcessor that triggers in case we don't hear from the listener in
-    // a timely fashion.
-    // since events are constantly coming in, we need to be able to handle more than one
-    // block of events in the queue.
-    //
-    // this means that there are ordering restrictions on these that we can take advantage of,
-    // and need to abide by. blocks of events in the queue will always be in the order that
-    // the user generated them. default-prevented notifications we get from gecko will be in
-    // the same order as the blocks of events in the queue. the ListenerTimeoutProcessors that
-    // have been posted will also fire in the same order as the blocks of events in the queue.
-    // HOWEVER, we may get multiple default-prevented notifications interleaved with multiple
-    // ListenerTimeoutProcessor firings, and that interleaving is not predictable.
-    //
-    // therefore, we need to make sure that for each block of events, we process the queued
-    // events exactly once, either when we get the default-prevented notification, or when the
-    // timeout expires (whichever happens first). there is no way to associate the
-    // default-prevented notification with a particular block of events other than via ordering,
-    //
-    // so what we do to accomplish this is to track a "processing balance", which is the number
-    // of default-prevented notifications that we have received, minus the number of ListenerTimeoutProcessors
-    // that have fired. (think "balance" as in teeter-totter balance). this value is:
-    // - zero when we are in a state where the next default-prevented notification we expect
-    //   to receive and the next ListenerTimeoutProcessor we expect to fire both correspond to
-    //   the next block of events in the queue.
-    // - positive when we are in a state where we have received more default-prevented notifications
-    //   than ListenerTimeoutProcessors. This means that the next default-prevented notification
-    //   does correspond to the block at the head of the queue, but the next n ListenerTimeoutProcessors
-    //   need to be ignored as they are for blocks we have already processed. (n is the absolute value
-    //   of the balance.)
-    // - negative when we are in a state where we have received more ListenerTimeoutProcessors than
-    //   default-prevented notifications. This means that the next ListenerTimeoutProcessor that
-    //   we receive does correspond to the block at the head of the queue, but the next n
-    //   default-prevented notifications need to be ignored as they are for blocks we have already
-    //   processed. (n is the absolute value of the balance.)
-    private int mProcessingBalance;
-
-    TouchEventHandler(Context context, View view, JavaPanZoomController panZoomController) {
-        mView = view;
-
-        mEventQueue = new LinkedList<MotionEvent>();
-        mPanZoomController = panZoomController;
-        mGestureDetector = new GestureDetector(context, mPanZoomController);
-        mScaleGestureDetector = new SimpleScaleGestureDetector(mPanZoomController);
-        mListenerTimeoutProcessor = new ListenerTimeoutProcessor();
-        mAllowDefaultAction = true;
-
-        mGestureDetector.setOnDoubleTapListener(mPanZoomController);
-
-        Tabs.registerOnTabsChangedListener(this);
-    }
-
-    public void destroy() {
-        Tabs.unregisterOnTabsChangedListener(this);
-    }
-
-    /* This function MUST be called on the UI thread */
-    public boolean handleEvent(MotionEvent event) {
-        if (isDownEvent(event)) {
-            // this is the start of a new block of events! whee!
-            mHoldInQueue = mWaitForTouchListeners;
-
-            // Set mAllowDefaultAction to true so that in the event we dispatch events, the
-            // PanZoomController doesn't treat them as if they've been prevent-defaulted
-            // when they haven't.
-            mAllowDefaultAction = true;
-            if (mHoldInQueue) {
-                // if the new block we are starting is the current block (i.e. there are no
-                // other blocks waiting in the queue, then we should let the pan/zoom controller
-                // know we are waiting for the touch listeners to run
-                if (mEventQueue.isEmpty()) {
-                    mPanZoomController.startingNewEventBlock(event, true);
-                }
-            } else {
-                // we're not going to be holding this block of events in the queue, but we need
-                // a marker of some sort so that the processEventBlock loop deals with the blocks
-                // in the right order as notifications come in. we use a single null event in
-                // the queue as a placeholder for a block of events that has already been dispatched.
-                mEventQueue.add(null);
-                mPanZoomController.startingNewEventBlock(event, false);
-            }
-
-            // set the timeout so that we dispatch these events and update mProcessingBalance
-            // if we don't get a default-prevented notification
-            mView.postDelayed(mListenerTimeoutProcessor, EVENT_LISTENER_TIMEOUT);
-        }
-
-        // if we need to hold the events, add it to the queue, otherwise dispatch
-        // it directly.
-        if (mHoldInQueue) {
-            mEventQueue.add(MotionEvent.obtain(event));
-        } else {
-            dispatchEvent(event, mAllowDefaultAction);
-        }
-
-        return false;
-    }
-
-    /**
-     * This function is how gecko sends us a default-prevented notification. It is called
-     * once gecko knows definitively whether the block of events has had preventDefault
-     * called on it (either on the initial down event that starts the block, or on
-     * the first event following that down event).
-     *
-     * This function MUST be called on the UI thread.
-     */
-    public void handleEventListenerAction(boolean allowDefaultAction) {
-        if (mProcessingBalance > 0) {
-            // this event listener that triggered this took too long, and the corresponding
-            // ListenerTimeoutProcessor runnable already ran for the event in question. the
-            // block of events this is for has already been processed, so we don't need to
-            // do anything here.
-        } else {
-            processEventBlock(allowDefaultAction);
-        }
-        mProcessingBalance--;
-    }
-
-    /* This function MUST be called on the UI thread. */
-    public void setWaitForTouchListeners(boolean aValue) {
-        mWaitForTouchListeners = aValue;
-    }
-
-    private boolean isDownEvent(MotionEvent event) {
-        int action = (event.getAction() & MotionEvent.ACTION_MASK);
-        return (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN);
-    }
-
-    private boolean touchFinished(MotionEvent event) {
-        int action = (event.getAction() & MotionEvent.ACTION_MASK);
-        return (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL);
-    }
-
-    /**
-     * Dispatch the event to the gesture detectors and the pan/zoom controller.
-     */
-    private void dispatchEvent(MotionEvent event, boolean allowDefaultAction) {
-        if (allowDefaultAction) {
-            if (mGestureDetector.onTouchEvent(event)) {
-                return;
-            }
-            mScaleGestureDetector.onTouchEvent(event);
-            if (mScaleGestureDetector.isInProgress()) {
-                return;
-            }
-        }
-        mPanZoomController.handleEvent(event, !allowDefaultAction);
-    }
-
-    /**
-     * Process the block of events at the head of the queue now that we know
-     * whether it has been default-prevented or not.
-     */
-    private void processEventBlock(boolean allowDefaultAction) {
-        if (mEventQueue.isEmpty()) {
-            Log.e(LOGTAG, "Unexpected empty event queue in processEventBlock!", new Exception());
-            return;
-        }
-
-        // the odd loop condition is because the first event in the queue will
-        // always be a DOWN or POINTER_DOWN event, and we want to process all
-        // the events in the queue starting at that one, up to but not including
-        // the next DOWN or POINTER_DOWN event.
-
-        MotionEvent event = mEventQueue.poll();
-        while (true) {
-            // event being null here is valid and represents a block of events
-            // that has already been dispatched.
-
-            if (event != null) {
-                dispatchEvent(event, allowDefaultAction);
-                event.recycle();
-                event = null;
-            }
-            if (mEventQueue.isEmpty()) {
-                // we have processed the backlog of events, and are all caught up.
-                // now we can set clear the hold flag and set the dispatch flag so
-                // that the handleEvent() function can do the right thing for all
-                // remaining events in this block (which is still ongoing) without
-                // having to put them in the queue.
-                mHoldInQueue = false;
-                mAllowDefaultAction = allowDefaultAction;
-                break;
-            }
-            event = mEventQueue.peek();
-            if (event == null || isDownEvent(event)) {
-                // we have finished processing the block we were interested in.
-                // now we wait for the next call to processEventBlock
-                if (event != null) {
-                    mPanZoomController.startingNewEventBlock(event, true);
-                }
-                break;
-            }
-            // pop the event we peeked above, as it is still part of the block and
-            // we want to keep processing
-            mEventQueue.remove();
-        }
-    }
-
-    private class ListenerTimeoutProcessor implements Runnable {
-        /* This MUST be run on the UI thread */
-        @Override
-        public void run() {
-            if (mProcessingBalance < 0) {
-                // gecko already responded with default-prevented notification, and so
-                // the block of events this ListenerTimeoutProcessor corresponds to have
-                // already been removed from the queue.
-            } else {
-                processEventBlock(true);
-            }
-            mProcessingBalance++;
-        }
-    }
-
-    // Tabs.OnTabsChangedListener implementation
-
-    @Override
-    public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
-        if ((Tabs.getInstance().isSelectedTab(tab) && msg == Tabs.TabEvents.STOP) || msg == Tabs.TabEvents.SELECTED) {
-            mWaitForTouchListeners = tab.getHasTouchListeners();
-        }
-    }
-}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -349,51 +349,43 @@ gbjar.sources += ['java/org/mozilla/geck
     'GeckoService.java',
     'GeckoSharedPrefs.java',
     'GeckoSmsManager.java',
     'GeckoThread.java',
     'GeckoUpdateReceiver.java',
     'GeckoView.java',
     'GeckoViewChrome.java',
     'GeckoViewContent.java',
-    'gfx/Axis.java',
     'gfx/BitmapUtils.java',
-    'gfx/BufferedImage.java',
-    'gfx/BufferedImageGLInfo.java',
     'gfx/DisplayPortCalculator.java',
     'gfx/DisplayPortMetrics.java',
     'gfx/DrawTimingQueue.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
     'gfx/GeckoLayerClient.java',
     'gfx/GLController.java',
     'gfx/ImmutableViewportMetrics.java',
     'gfx/IntSize.java',
-    'gfx/JavaPanZoomController.java',
     'gfx/Layer.java',
     'gfx/LayerRenderer.java',
     'gfx/LayerView.java',
     'gfx/NativePanZoomController.java',
     'gfx/Overscroll.java',
     'gfx/OverscrollEdgeEffect.java',
     'gfx/PanningPerfAPI.java',
     'gfx/PanZoomController.java',
     'gfx/PanZoomTarget.java',
     'gfx/PluginLayer.java',
     'gfx/PointUtils.java',
     'gfx/ProgressiveUpdateData.java',
     'gfx/RectUtils.java',
     'gfx/RenderTask.java',
-    'gfx/ScrollbarLayer.java',
-    'gfx/SimpleScaleGestureDetector.java',
-    'gfx/SubdocumentScrollHelper.java',
     'gfx/TextureGenerator.java',
     'gfx/TextureReaper.java',
-    'gfx/TouchEventHandler.java',
     'gfx/ViewTransform.java',
     'gfx/VirtualLayer.java',
     'GlobalHistory.java',
     'GuestSession.java',
     'health/HealthRecorder.java',
     'health/SessionInformation.java',
     'health/StubbedHealthRecorder.java',
     'home/BookmarkFolderView.java',
deleted file mode 100644
index 55462df1dc0e7aeecf6cff11acb9ce48364358e8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001