Bug 1291373 - [geckoview] part 1, Remove JavaPanZoomController (JPZ) from mobile/android r=snorp
authorRandall Barker <rbarker@mozilla.com>
Thu, 04 Aug 2016 15:46:12 -0700
changeset 309716 268f3aea336d92ea7e1ca133bcf548f34f81e65c
parent 309715 6b3f96dbbf2888bc673132e3ef2839ce4857974f
child 309717 19222e86fb30c034d5ec45f9c8f1e51fb7efe183
push id20333
push userkwierso@gmail.com
push dateThu, 18 Aug 2016 00:26:40 +0000
treeherderfx-team@11c94ec980d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1291373
milestone51.0a1
Bug 1291373 - [geckoview] part 1, Remove JavaPanZoomController (JPZ) from mobile/android r=snorp
dom/plugins/base/nsPluginInstanceOwner.cpp
mobile/android/base/AppConstants.java.in
mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TextureGenerator.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TouchEventHandler.java
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1540,32 +1540,32 @@ bool nsPluginInstanceOwner::AddPluginVie
     mJavaView = mInstance->GetJavaSurface();
 
     if (!mJavaView)
       return false;
 
     mJavaView = (void*)jni::GetGeckoThreadEnv()->NewGlobalRef((jobject)mJavaView);
   }
 
-  if (AndroidBridge::Bridge())
-    AndroidBridge::Bridge()->AddPluginView((jobject)mJavaView, aRect, mFullScreen);
-
-  if (mFullScreen)
+  if (mFullScreen) {
+    java::GeckoAppShell::AddFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView)));
     sFullScreenInstance = this;
+  }
 
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
   if (!mInstance || !mJavaView)
     return;
 
-  java::GeckoAppShell::RemovePluginView(
-      jni::Object::Ref::From(jobject(mJavaView)), mFullScreen);
+  if (mFullScreen) {
+    java::GeckoAppShell::RemoveFullScreenPluginView(jni::Object::Ref::From(jobject(mJavaView)));
+  }
   jni::GetGeckoThreadEnv()->DeleteGlobalRef((jobject)mJavaView);
   mJavaView = nullptr;
 
   if (mFullScreen)
     sFullScreenInstance = nullptr;
 }
 
 void
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -237,23 +237,16 @@ public class AppConstants {
     // it if this APK doesn't include API14 support.
     public static final boolean MOZ_ANDROID_BEAM =
 //#ifdef MOZ_ANDROID_BEAM
     Versions.feature14Plus;
 //#else
     false;
 //#endif
 
-    public static final boolean MOZ_ANDROID_APZ =
-//#ifdef MOZ_ANDROID_APZ
-    true;
-//#else
-    false;
-//#endif
-
     // See this wiki page for more details about channel specific build defines:
     // https://wiki.mozilla.org/Platform/Channel-specific_build_defines
     public static final boolean RELEASE_BUILD =
 //#ifdef RELEASE_BUILD
     true;
 //#else
     false;
 //#endif
--- a/mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
@@ -166,32 +166,30 @@ class ActionBarTextSelection extends Lay
                         mViewLeft = 0.0f;
                         mViewTop = 0.0f;
                         mViewZoom = 0.0f;
 
                         // Create text selection layer and add draw-listener for positioning on reflows
                         LayerView layerView = GeckoAppShell.getLayerView();
                         if (layerView != null) {
                             layerView.addDrawListener(mDrawListener);
-                            layerView.addLayer(ActionBarTextSelection.this);
                             layerView.getDynamicToolbarAnimator().addTranslationListener(ActionBarTextSelection.this);
                         }
 
                         if (handles.length() > 1)
                             GeckoAppShell.performHapticFeedback(true);
                     } else if (event.equals("TextSelection:Update")) {
                         if (mActionModeTimerTask != null)
                             mActionModeTimerTask.cancel();
                         showActionMode(message.getJSONArray("actions"));
                     } else if (event.equals("TextSelection:HideHandles")) {
                         // Remove draw-listener and text selection layer
                         LayerView layerView = GeckoAppShell.getLayerView();
                         if (layerView != null) {
                             layerView.removeDrawListener(mDrawListener);
-                            layerView.removeLayer(ActionBarTextSelection.this);
                             layerView.getDynamicToolbarAnimator().removeTranslationListener(ActionBarTextSelection.this);
                         }
 
                         mActionModeTimerTask = new ActionModeTimerTask();
                         mActionModeTimer.schedule(mActionModeTimerTask, SHUTDOWN_DELAY_MS);
 
                         anchorHandle.setVisibility(View.GONE);
                         caretHandle.setVisibility(View.GONE);
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -352,17 +352,16 @@ public abstract class GeckoApp
     }
 
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
         // When a tab is closed, it is always unselected first.
         // When a tab is unselected, another tab is always selected first.
         switch (msg) {
             case UNSELECTED:
-                hidePlugins(tab);
                 break;
 
             case LOCATION_CHANGE:
                 // We only care about location change for the selected tab.
                 if (!Tabs.getInstance().isSelectedTab(tab))
                     break;
                 // Fall through...
             case SELECTED:
@@ -912,40 +911,28 @@ public abstract class GeckoApp
 
         FrameLayout decor = (FrameLayout)getWindow().getDecorView();
         decor.addView(mFullScreenPluginContainer, layoutParams);
 
         mFullScreenPluginView = view;
     }
 
     @Override
-    public void addPluginView(final View view, final RectF rect, final boolean isFullScreen) {
-        ThreadUtils.postToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Tabs tabs = Tabs.getInstance();
-                Tab tab = tabs.getSelectedTab();
-
-                if (isFullScreen) {
+    public void addPluginView(final View view) {
+
+        if (ThreadUtils.isOnUiThread()) {
+            addFullScreenPluginView(view);
+        } else {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
                     addFullScreenPluginView(view);
-                    return;
                 }
-
-                PluginLayer layer = (PluginLayer) tab.getPluginLayer(view);
-                if (layer == null) {
-                    layer = new PluginLayer(view, rect, mLayerView.getRenderer().getMaxTextureSize());
-                    tab.addPluginLayer(view, layer);
-                } else {
-                    layer.reset(rect);
-                    layer.setVisible(true);
-                }
-
-                mLayerView.addLayer(layer);
-            }
-        });
+            });
+        }
     }
 
     /* package */ void removeFullScreenPluginView(View view) {
         if (mFullScreenPluginView == null) {
             Log.w(LOGTAG, "Don't have a fullscreen plugin view");
             return;
         }
 
@@ -970,34 +957,27 @@ public abstract class GeckoApp
 
         mFullScreenPluginView = null;
 
         GeckoScreenOrientation.getInstance().unlock();
         setFullScreen(false);
     }
 
     @Override
-    public void removePluginView(final View view, final boolean isFullScreen) {
-        ThreadUtils.postToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Tabs tabs = Tabs.getInstance();
-                Tab tab = tabs.getSelectedTab();
-
-                if (isFullScreen) {
+    public void removePluginView(final View view) {
+        if (ThreadUtils.isOnUiThread()) {
+            removePluginView(view);
+        } else {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
                     removeFullScreenPluginView(view);
-                    return;
                 }
-
-                PluginLayer layer = (PluginLayer) tab.removePluginLayer(view);
-                if (layer != null) {
-                    layer.destroy();
-                }
-            }
-        });
+            });
+        }
     }
 
     // This method starts downloading an image synchronously and displays the Chooser activity to set the image as wallpaper.
     private void setImageAs(final String aSrc) {
         boolean isDataURI = aSrc.startsWith("data:");
         Bitmap image = null;
         InputStream is = null;
         ByteArrayOutputStream os = null;
@@ -1091,63 +1071,20 @@ public abstract class GeckoApp
                 inSampleSize = Math.round((float)height / idealHeight);
             } else {
                 inSampleSize = Math.round((float)width / idealWidth);
             }
         }
         return inSampleSize;
     }
 
-    private void hidePluginLayer(Layer layer) {
-        LayerView layerView = mLayerView;
-        layerView.removeLayer(layer);
-        layerView.requestRender();
-    }
-
-    private void showPluginLayer(Layer layer) {
-        LayerView layerView = mLayerView;
-        layerView.addLayer(layer);
-        layerView.requestRender();
-    }
-
     public void requestRender() {
         mLayerView.requestRender();
     }
 
-    public void hidePlugins(Tab tab) {
-        for (Layer layer : tab.getPluginLayers()) {
-            if (layer instanceof PluginLayer) {
-                ((PluginLayer) layer).setVisible(false);
-            }
-
-            hidePluginLayer(layer);
-        }
-
-        requestRender();
-    }
-
-    public void showPlugins() {
-        Tabs tabs = Tabs.getInstance();
-        Tab tab = tabs.getSelectedTab();
-
-        showPlugins(tab);
-    }
-
-    public void showPlugins(Tab tab) {
-        for (Layer layer : tab.getPluginLayers()) {
-            showPluginLayer(layer);
-
-            if (layer instanceof PluginLayer) {
-                ((PluginLayer) layer).setVisible(true);
-            }
-        }
-
-        requestRender();
-    }
-
     @Override
     public void setFullScreen(final boolean fullscreen) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 ActivityUtils.setFullScreen(GeckoApp.this, fullscreen);
             }
         });
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -235,38 +235,33 @@ geckoview_java_files = [
     'gfx/DisplayPortMetrics.java',
     'gfx/DrawTimingQueue.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
     'gfx/GeckoLayerClient.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/StackScroller.java',
     'gfx/SubdocumentScrollHelper.java',
-    'gfx/TextureGenerator.java',
-    'gfx/TextureReaper.java',
-    'gfx/TouchEventHandler.java',
     'gfx/ViewTransform.java',
     'gfx/VirtualLayer.java',
     'InputConnectionListener.java',
     'InputMethods.java',
     'notifications/AppNotificationClient.java',
     'notifications/NotificationClient.java',
     'notifications/NotificationHandler.java',
     'notifications/NotificationHelper.java',
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
@@ -62,21 +62,21 @@ public class BaseGeckoInterface implemen
             public void run() {
                 ActivityUtils.setFullScreen(getActivity(), fullscreen);
             }
         });
     }
 
     // Bug 908779: Implement this
     @Override
-    public void addPluginView(final View view, final RectF rect, final boolean isFullScreen) {}
+    public void addPluginView(final View view) {}
 
     // Bug 908781: Implement this
     @Override
-    public void removePluginView(final View view, final boolean isFullScreen) {}
+    public void removePluginView(final View view) {}
 
     // Bug 908783: Implement this
     @Override
     public void enableCameraView() {}
 
     // Bug 908785: Implement this
     @Override
     public void disableCameraView() {}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -1542,28 +1542,25 @@ public class GeckoAppShell
             return (showPassword > 0);
         }
         catch (Exception e) {
             return true;
         }
     }
 
     @WrapForJNI(calledFrom = "gecko")
-    public static void addPluginView(View view,
-                                     float x, float y,
-                                     float w, float h,
-                                     boolean isFullScreen) {
+    public static void addFullScreenPluginView(View view) {
         if (getGeckoInterface() != null)
-             getGeckoInterface().addPluginView(view, new RectF(x, y, x + w, y + h), isFullScreen);
+             getGeckoInterface().addPluginView(view);
     }
 
     @WrapForJNI(calledFrom = "gecko")
-    public static void removePluginView(View view, boolean isFullScreen) {
+    public static void removeFullScreenPluginView(View view) {
         if (getGeckoInterface() != null)
-            getGeckoInterface().removePluginView(view, isFullScreen);
+            getGeckoInterface().removePluginView(view);
     }
 
     /**
      * A plugin that wish to be loaded in the WebView must provide this permission
      * in their AndroidManifest.xml.
      */
     public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
     public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
@@ -1822,18 +1819,18 @@ public class GeckoAppShell
     }
 
     public interface GeckoInterface {
         public GeckoProfile getProfile();
         public Activity getActivity();
         public String getDefaultUAString();
         public void doRestart();
         public void setFullScreen(boolean fullscreen);
-        public void addPluginView(View view, final RectF rect, final boolean isFullScreen);
-        public void removePluginView(final View view, final boolean isFullScreen);
+        public void addPluginView(View view);
+        public void removePluginView(final View view);
         public void enableCameraView();
         public void disableCameraView();
         public void addAppStateListener(AppStateListener listener);
         public void removeAppStateListener(AppStateListener listener);
         public View getCameraView();
         public void notifyWakeLockChanged(String topic, String state);
         public FormAssistPopup getFormAssistPopup();
         public boolean areTabsShown();
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -107,21 +107,16 @@ public class DynamicToolbarAnimator {
         // Listen to the dynamic toolbar pref
         mPrefObserver = new PrefsHelper.PrefHandlerBase() {
             @Override
             public void prefValue(String pref, int value) {
                 SCROLL_TOOLBAR_THRESHOLD = value / 100.0f;
             }
         };
         PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver);
-
-        // JPZ doesn't notify when scrolling root content. This maintains existing behaviour.
-        if (!AppConstants.MOZ_ANDROID_APZ) {
-            mScrollingRootContent = true;
-        }
     }
 
     public void destroy() {
         PrefsHelper.removeObserver(mPrefObserver);
     }
 
     public void addTranslationListener(LayerView.DynamicToolbarListener aListener) {
         mListeners.add(aListener);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -326,39 +326,16 @@ class GeckoLayerClient implements LayerV
             @Override
             public void run() {
                 mPanZoomController.pageRectUpdated();
                 mView.requestRender();
             }
         });
     }
 
-    private void adjustViewport(DisplayPortMetrics displayPort) {
-        // TODO: APZ For fennec might need margins information to deal with
-        // the dynamic toolbar.
-        if (AppConstants.MOZ_ANDROID_APZ)
-            return;
-
-        ImmutableViewportMetrics metrics = getViewportMetrics();
-        ImmutableViewportMetrics clampedMetrics = metrics.clamp();
-
-        if (displayPort == null) {
-            displayPort = DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector());
-        }
-
-        mDisplayPort = displayPort;
-        mGeckoViewport = clampedMetrics;
-
-        if (mRecordDrawTimes) {
-            mDrawTimingQueue.add(displayPort);
-        }
-
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(clampedMetrics, displayPort));
-    }
-
     /** Aborts any pan/zoom animation that is currently in progress. */
     private void abortPanZoomAnimation() {
         if (mPanZoomController != null) {
             post(new Runnable() {
                 @Override
                 public void run() {
                     mPanZoomController.abortAnimation();
                 }
@@ -635,19 +612,17 @@ class GeckoLayerClient implements LayerV
       */
     @WrapForJNI
     public void setPageRect(float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
         synchronized (getLock()) {
             RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
             float ourZoom = getViewportMetrics().zoomFactor;
             setPageRect(RectUtils.scale(cssPageRect, ourZoom), cssPageRect);
             // Here the page size of the document has changed, but the document being displayed
-            // is still the same. Therefore, we don't need to send anything to browser.js; any
-            // changes we need to make to the display port will get sent the next time we call
-            // adjustViewport().
+            // is still the same. Therefore, we don't need to send anything to browser.js;
         }
     }
 
     /** The compositor invokes this function on every frame to figure out what part of the
       * page to display, and to inform Java of the current display port. Since it is called
       * on every frame, it needs to be ultra-fast.
       * It avoids taking any locks or allocating any objects. We keep around a
       * mCurrentViewTransform so we don't need to allocate a new ViewTransform
@@ -960,19 +935,16 @@ class GeckoLayerClient implements LayerV
     {
         mLayerRenderer.deactivateDefaultProgram();
         mLayerRenderer.restoreState(enableScissor, scissorX, scissorY, scissorW, scissorH);
     }
 
     private void geometryChanged(DisplayPortMetrics displayPort) {
         /* Let Gecko know if the screensize has changed */
         sendResizeEventIfNecessary(false, null);
-        if (getRedrawHint()) {
-            adjustViewport(displayPort);
-        }
     }
 
     /** Implementation of LayerView.Listener */
     @Override
     public void renderRequested() {
         if (mView != null) {
             mView.invalidateAndScheduleComposite();
         }
@@ -1022,17 +994,16 @@ class GeckoLayerClient implements LayerV
     @Override
     public void setAnimationTarget(ImmutableViewportMetrics metrics) {
         if (mGeckoIsReady) {
             // We know what the final viewport of the animation is going to be, so
             // immediately request a draw of that area by setting the display port
             // accordingly. This way we should have the content pre-rendered by the
             // time the animation is done.
             DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null);
-            adjustViewport(displayPort);
         }
     }
 
     /** Implementation of PanZoomTarget
      * You must hold the monitor while calling this.
      */
     @Override
     public void setViewportMetrics(ImmutableViewportMetrics metrics) {
@@ -1145,17 +1116,17 @@ class GeckoLayerClient implements LayerV
     public PointF convertViewPointToLayerPoint(PointF viewPoint) {
         if (!mGeckoIsReady) {
             return null;
         }
 
         ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
         PointF origin = viewportMetrics.getOrigin();
         float zoom = viewportMetrics.zoomFactor;
-        ImmutableViewportMetrics geckoViewport = (AppConstants.MOZ_ANDROID_APZ ? mViewportMetrics : mGeckoViewport);
+        ImmutableViewportMetrics geckoViewport = mViewportMetrics;
         PointF geckoOrigin = geckoViewport.getOrigin();
         float geckoZoom = geckoViewport.zoomFactor;
 
         // viewPoint + origin - offset gives the coordinate in device pixels from the top-left corner of the page.
         // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page.
         // geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from
         // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from
         // the current Gecko coordinate in CSS pixels.
@@ -1170,17 +1141,17 @@ class GeckoLayerClient implements LayerV
     public Matrix getMatrixForLayerRectToViewRect() {
         if (!mGeckoIsReady) {
             return null;
         }
 
         ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
         PointF origin = viewportMetrics.getOrigin();
         float zoom = viewportMetrics.zoomFactor;
-        ImmutableViewportMetrics geckoViewport = (AppConstants.MOZ_ANDROID_APZ ? mViewportMetrics : mGeckoViewport);
+        ImmutableViewportMetrics geckoViewport = mViewportMetrics;
         PointF geckoOrigin = geckoViewport.getOrigin();
         float geckoZoom = geckoViewport.zoomFactor;
 
         Matrix matrix = new Matrix();
         matrix.postTranslate(geckoOrigin.x / geckoZoom, geckoOrigin.y / geckoZoom);
         matrix.postScale(zoom, zoom);
         matrix.postTranslate(-origin.x, -origin.y);
         return matrix;
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/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/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java
@@ -3,18 +3,16 @@
  * 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;
@@ -36,17 +34,17 @@ import java.util.concurrent.CopyOnWriteA
 import java.util.ArrayList;
 import java.util.List;
 
 import javax.microedition.khronos.egl.EGLConfig;
 
 /**
  * The layer renderer implements the rendering logic for a layer view.
  */
-public class LayerRenderer implements Tabs.OnTabsChangedListener {
+public class LayerRenderer {
     private static final String LOGTAG = "GeckoLayerRenderer";
 
     /*
      * 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 */
 
@@ -54,30 +52,25 @@ public class LayerRenderer implements Ta
     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;
 
     private IntBuffer mPixelBuffer;
 
     // Used by GLES 2.0
     private int mProgram;
@@ -141,31 +134,19 @@ public class LayerRenderer implements Ta
         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;
         }
@@ -183,21 +164,16 @@ public class LayerRenderer implements Ta
     }
 
     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) {
         createDefaultProgram();
         activateDefaultProgram();
     }
 
@@ -279,32 +255,16 @@ public class LayerRenderer implements Ta
 
             // Remove the task from the list if its finished
             if (!stillRunning) {
                 tasks.remove(task);
             }
         }
     }
 
-    public void addLayer(Layer layer) {
-        synchronized (mExtraLayers) {
-            if (mExtraLayers.contains(layer)) {
-                mExtraLayers.remove(layer);
-            }
-
-            mExtraLayers.add(layer);
-        }
-    }
-
-    public void removeLayer(Layer layer) {
-        synchronized (mExtraLayers) {
-            mExtraLayers.remove(layer);
-        }
-    }
-
     /** Used by robocop for testing purposes. Not for production use! */
     IntBuffer getPixels() {
         IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight());
         synchronized (pixelBuffer) {
             mPixelBuffer = pixelBuffer;
             mView.requestRender();
             try {
                 pixelBuffer.wait();
@@ -440,57 +400,28 @@ public class LayerRenderer implements Ta
             mPageRect = RectUtils.round(pageRect);
         }
 
         /** This function is invoked via JNI; be careful when modifying signature. */
         @WrapForJNI
         public void beginDrawing() {
             mFrameStartTime = System.nanoTime();
 
-            TextureReaper.get().reap();
-            TextureGenerator.get().fill();
-
             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
-            }
         }
 
         private void clear(int color) {
             GLES20.glClearColor(((color >> 16) & 0xFF) / 255.0f,
                                 ((color >> 8) & 0xFF) / 255.0f,
                                 (color & 0xFF) / 255.0f,
                                 0.0f);
             // The bits set here need to match up with those used
@@ -514,35 +445,17 @@ public class LayerRenderer implements Ta
             clear(mBackgroundColor);
         }
 
         @WrapForJNI
         public void drawForeground() {
             // Any GL state which is changed here must be restored in
             // restoreState(...)
 
-            /* 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);
-            }
-
             runRenderTasks(mTasks, true, mFrameStartTime);
-
         }
 
         private void maybeRequestZoomedViewRender(RenderContext context) {
             // Concurrently update of mZoomedViewListeners should not be an issue here
             // because the following line is just a short-circuit
             if (mZoomedViewListeners.size() == 0) {
                 return;
             }
@@ -610,30 +523,16 @@ public class LayerRenderer implements Ta
                     }
                 });
                 mView.setPaintState(LayerView.PAINT_AFTER_FIRST);
             }
             mLastFrameTime = mFrameStartTime;
         }
     }
 
-    @Override
-    public void onTabChanged(final Tab tab, Tabs.TabEvents msg, String data) {
-        // Sets the background of the newly selected tab. This background color
-        // gets cleared in endDrawing(). This function runs on the UI thread,
-        // but other code that touches the paint state is run on the compositor
-        // thread, so this may need to be changed if any problems appear.
-        if (msg == Tabs.TabEvents.SELECTED) {
-            if (mView != null) {
-                mView.setSurfaceBackgroundColor(tab.getBackgroundColor());
-                mView.setPaintState(LayerView.PAINT_START);
-            }
-        }
-    }
-
     public void updateZoomedView(final ByteBuffer data) {
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 for (LayerView.ZoomedViewListener listener : mZoomedViewListeners) {
                     data.position(0);
                     listener.updateView(data);
                 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -288,17 +288,17 @@ public class LayerView extends ScrollVie
         event.offsetLocation(0, -mSurfaceTranslation);
 
         if (mToolbarAnimator != null && mToolbarAnimator.onInterceptTouchEvent(event)) {
             if (mPanZoomController != null) {
                 mPanZoomController.onMotionEventVelocity(event.getEventTime(), mToolbarAnimator.getVelocity());
             }
             return true;
         }
-        if (AppConstants.MOZ_ANDROID_APZ && !mLayerClient.isGeckoReady()) {
+        if (!mLayerClient.isGeckoReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         }
         if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) {
             return true;
         }
         return sendEventToGecko(event);
@@ -310,37 +310,35 @@ public class LayerView extends ScrollVie
         // don't send it to gecko.
         if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
             !GeckoAccessibility.isEnabled()) {
             return false;
         }
 
         event.offsetLocation(0, -mSurfaceTranslation);
 
-        if (AppConstants.MOZ_ANDROID_APZ) {
-            if (!mLayerClient.isGeckoReady()) {
-                // If gecko isn't loaded yet, don't try sending events to the
-                // native code because it's just going to crash
-                return true;
-            } else if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
-                return true;
-            }
+        if (!mLayerClient.isGeckoReady()) {
+            // If gecko isn't loaded yet, don't try sending events to the
+            // native code because it's just going to crash
+            return true;
+        } else if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
+            return true;
         }
 
         return sendEventToGecko(event);
     }
 
     @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         event.offsetLocation(0, -mSurfaceTranslation);
 
         if (AndroidGamepadManager.handleMotionEvent(event)) {
             return true;
         }
-        if (AppConstants.MOZ_ANDROID_APZ && !mLayerClient.isGeckoReady()) {
+        if (!mLayerClient.isGeckoReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         }
         if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
             return true;
         }
         return false;
@@ -447,41 +445,33 @@ public class LayerView extends ScrollVie
     }
 
     public void setIsRTL(boolean aIsRTL) {
         mLayerClient.setIsRTL(aIsRTL);
     }
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (AppConstants.MOZ_ANDROID_APZ && !mLayerClient.isGeckoReady()) {
+        if (!mLayerClient.isGeckoReady()) {
             // If gecko isn't loaded yet, don't try sending events to the
             // native code because it's just going to crash
             return true;
         }
         if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) {
             return true;
         }
         return false;
     }
 
     public void requestRender() {
         if (mListener != null) {
             mListener.renderRequested();
         }
     }
 
-    public void addLayer(Layer layer) {
-        mRenderer.addLayer(layer);
-    }
-
-    public void removeLayer(Layer layer) {
-        mRenderer.removeLayer(layer);
-    }
-
     public void postRenderTask(RenderTask task) {
         mRenderer.postRenderTask(task);
     }
 
     public void removeRenderTask(RenderTask task) {
         mRenderer.removeRenderTask(task);
     }
 
@@ -512,18 +502,17 @@ public class LayerView extends ScrollVie
         mListener = listener;
     }
 
     Listener getListener() {
         return mListener;
     }
 
     private void attachCompositor() {
-        final NativePanZoomController npzc = AppConstants.MOZ_ANDROID_APZ ?
-                (NativePanZoomController) mPanZoomController : null;
+        final NativePanZoomController npzc = (NativePanZoomController) mPanZoomController;
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
             mCompositor.attachToJava(mLayerClient, npzc);
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mCompositor, "attachToJava",
                     GeckoLayerClient.class, mLayerClient,
                     NativePanZoomController.class, npzc);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
@@ -18,21 +18,17 @@ public interface PanZoomController {
     // 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);
-            }
+            return new NativePanZoomController(target, view);
         }
     }
 
     public void destroy();
 
     public boolean onTouchEvent(MotionEvent event);
     public boolean onMotionEvent(MotionEvent event);
     public boolean onKeyEvent(KeyEvent event);
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/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/geckoview/src/main/java/org/mozilla/gecko/gfx/TextureGenerator.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.gfx;
-
-import android.opengl.GLES20;
-import android.util.Log;
-
-import java.util.concurrent.ArrayBlockingQueue;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLContext;
-
-public class TextureGenerator {
-    private static final String LOGTAG = "TextureGenerator";
-    private static final int POOL_SIZE = 5;
-
-    private static TextureGenerator sSharedInstance;
-
-    private final ArrayBlockingQueue<Integer> mTextureIds;
-    private EGLContext mContext;
-
-    private TextureGenerator() { mTextureIds = new ArrayBlockingQueue<Integer>(POOL_SIZE); }
-
-    public static TextureGenerator get() {
-        if (sSharedInstance == null)
-            sSharedInstance = new TextureGenerator();
-        return sSharedInstance;
-    }
-
-    public synchronized int take() {
-        try {
-            // Will block until one becomes available
-            return (int)mTextureIds.take();
-        } catch (InterruptedException e) {
-            return 0;
-        }
-    }
-
-    public synchronized void fill() {
-        EGL10 egl = (EGL10)EGLContext.getEGL();
-        EGLContext context = egl.eglGetCurrentContext();
-
-        if (mContext != null && mContext != context) {
-            mTextureIds.clear();
-        }
-
-        mContext = context;
-
-        int numNeeded = mTextureIds.remainingCapacity();
-        if (numNeeded == 0)
-            return;
-
-        // Clear existing GL errors
-        int error;
-        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
-            Log.w(LOGTAG, String.format("Clearing GL error: %#x", error));
-        }
-
-        int[] textures = new int[numNeeded];
-        GLES20.glGenTextures(numNeeded, textures, 0);
-
-        error = GLES20.glGetError();
-        if (error != GLES20.GL_NO_ERROR) {
-            Log.e(LOGTAG, String.format("Failed to generate textures: %#x", error), new Exception());
-            return;
-        }
-
-        for (int i = 0; i < numNeeded; i++) {
-            mTextureIds.offer(textures[i]);
-        }
-    }
-}
-
-
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.gfx;
-
-import android.opengl.GLES20;
-
-import java.util.ArrayList;
-
-/** Manages a list of dead tiles, so we don't leak resources. */
-public class TextureReaper {
-    private static TextureReaper sSharedInstance;
-    private final ArrayList<Integer> mDeadTextureIDs;
-
-    private TextureReaper() { mDeadTextureIDs = new ArrayList<Integer>(); }
-
-    public static TextureReaper get() {
-        if (sSharedInstance == null)
-            sSharedInstance = new TextureReaper();
-        return sSharedInstance;
-    }
-
-    public void add(int[] textureIDs) {
-        for (int textureID : textureIDs)
-            add(textureID);
-    }
-
-    public void add(int textureID) {
-        mDeadTextureIDs.add(textureID);
-    }
-
-    public void reap() {
-        int numTextures = mDeadTextureIDs.size();
-        // Adreno 200 will generate INVALID_VALUE if len == 0 is passed to glDeleteTextures,
-        // even though it's not supposed to.
-        if (numTextures == 0)
-            return;
-
-        int[] deadTextureIDs = new int[numTextures];
-        for (int i = 0; i < numTextures; i++) {
-            deadTextureIDs[i] = mDeadTextureIDs.get(i);
-        }
-        mDeadTextureIDs.clear();
-
-        GLES20.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0);
-    }
-}
-
-
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/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/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1401,27 +1401,16 @@ NS_IMETHODIMP nsAndroidBridge::GetBrowse
 NS_IMETHODIMP nsAndroidBridge::SetBrowserApp(nsIAndroidBrowserApp *aBrowserApp)
 {
     nsAppShell* const appShell = nsAppShell::Get();
     if (appShell)
         appShell->SetBrowserApp(aBrowserApp);
     return NS_OK;
 }
 
-void
-AndroidBridge::AddPluginView(jobject view, const LayoutDeviceRect& rect, bool isFullScreen) {
-    nsWindow* win = nsWindow::TopWindow();
-    if (!win)
-        return;
-
-    CSSRect cssRect = rect / win->GetDefaultScale();
-    GeckoAppShell::AddPluginView(Object::Ref::From(view), cssRect.x, cssRect.y,
-                                 cssRect.width, cssRect.height, isFullScreen);
-}
-
 extern "C"
 __attribute__ ((visibility("default")))
 jobject JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size);
 
 bool
 AndroidBridge::GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult)
 {
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -241,18 +241,16 @@ public:
     void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
                           const CSSToParentLayerScale& aZoom,
                           const CSSRect& aCssPageRect,
                           const CSSRect& aDisplayPort,
                           const CSSToLayerScale& aPaintedResolution,
                           bool aLayersUpdated, int32_t aPaintSyncId,
                           ScreenMargin& aFixedLayerMargins);
 
-    void AddPluginView(jobject view, const LayoutDeviceRect& rect, bool isFullScreen);
-
     // These methods don't use a ScreenOrientation because it's an
     // enum and that would require including the header which requires
     // include IPC headers which requires including basictypes.h which
     // requires a lot of changes...
     uint32_t GetScreenOrientation();
     uint16_t GetScreenAngle();
 
     int GetAPIVersion() { return mAPIVersion; }
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -81,22 +81,22 @@ constexpr char DownloadsIntegration::Sca
 auto DownloadsIntegration::ScanMedia(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1) -> void
 {
     return mozilla::jni::Method<ScanMedia_t>::Call(DownloadsIntegration::Context(), nullptr, a0, a1);
 }
 
 const char GeckoAppShell::name[] =
         "org/mozilla/gecko/GeckoAppShell";
 
-constexpr char GeckoAppShell::AddPluginView_t::name[];
-constexpr char GeckoAppShell::AddPluginView_t::signature[];
-
-auto GeckoAppShell::AddPluginView(mozilla::jni::Object::Param a0, float a1, float a2, float a3, float a4, bool a5) -> void
+constexpr char GeckoAppShell::AddFullScreenPluginView_t::name[];
+constexpr char GeckoAppShell::AddFullScreenPluginView_t::signature[];
+
+auto GeckoAppShell::AddFullScreenPluginView(mozilla::jni::Object::Param a0) -> void
 {
-    return mozilla::jni::Method<AddPluginView_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1, a2, a3, a4, a5);
+    return mozilla::jni::Method<AddFullScreenPluginView_t>::Call(GeckoAppShell::Context(), nullptr, a0);
 }
 
 constexpr char GeckoAppShell::CancelVibrate_t::name[];
 constexpr char GeckoAppShell::CancelVibrate_t::signature[];
 
 auto GeckoAppShell::CancelVibrate() -> void
 {
     return mozilla::jni::Method<CancelVibrate_t>::Call(GeckoAppShell::Context(), nullptr);
@@ -632,22 +632,22 @@ auto GeckoAppShell::PerformHapticFeedbac
 constexpr char GeckoAppShell::RegisterSurfaceTextureFrameListener_t::name[];
 constexpr char GeckoAppShell::RegisterSurfaceTextureFrameListener_t::signature[];
 
 auto GeckoAppShell::RegisterSurfaceTextureFrameListener(mozilla::jni::Object::Param a0, int32_t a1) -> void
 {
     return mozilla::jni::Method<RegisterSurfaceTextureFrameListener_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1);
 }
 
-constexpr char GeckoAppShell::RemovePluginView_t::name[];
-constexpr char GeckoAppShell::RemovePluginView_t::signature[];
-
-auto GeckoAppShell::RemovePluginView(mozilla::jni::Object::Param a0, bool a1) -> void
+constexpr char GeckoAppShell::RemoveFullScreenPluginView_t::name[];
+constexpr char GeckoAppShell::RemoveFullScreenPluginView_t::signature[];
+
+auto GeckoAppShell::RemoveFullScreenPluginView(mozilla::jni::Object::Param a0) -> void
 {
-    return mozilla::jni::Method<RemovePluginView_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1);
+    return mozilla::jni::Method<RemoveFullScreenPluginView_t>::Call(GeckoAppShell::Context(), nullptr, a0);
 }
 
 constexpr char GeckoAppShell::RequestUiThreadCallback_t::name[];
 constexpr char GeckoAppShell::RequestUiThreadCallback_t::signature[];
 
 auto GeckoAppShell::RequestUiThreadCallback(int64_t a0) -> void
 {
     return mozilla::jni::Method<RequestUiThreadCallback_t>::Call(GeckoAppShell::Context(), nullptr, a0);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -293,40 +293,35 @@ public:
 
 class GeckoAppShell : public mozilla::jni::ObjectBase<GeckoAppShell>
 {
 public:
     static const char name[];
 
     explicit GeckoAppShell(const Context& ctx) : ObjectBase<GeckoAppShell>(ctx) {}
 
-    struct AddPluginView_t {
+    struct AddFullScreenPluginView_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param,
-                float,
-                float,
-                float,
-                float,
-                bool> Args;
-        static constexpr char name[] = "addPluginView";
-        static constexpr char signature[] =
-                "(Landroid/view/View;FFFFZ)V";
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "addFullScreenPluginView";
+        static constexpr char signature[] =
+                "(Landroid/view/View;)V";
         static const bool isStatic = true;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
-    static auto AddPluginView(mozilla::jni::Object::Param, float, float, float, float, bool) -> void;
+    static auto AddFullScreenPluginView(mozilla::jni::Object::Param) -> void;
 
     struct CancelVibrate_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "cancelVibrate";
         static constexpr char signature[] =
@@ -1744,36 +1739,35 @@ public:
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
     static auto RegisterSurfaceTextureFrameListener(mozilla::jni::Object::Param, int32_t) -> void;
 
-    struct RemovePluginView_t {
+    struct RemoveFullScreenPluginView_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param,
-                bool> Args;
-        static constexpr char name[] = "removePluginView";
-        static constexpr char signature[] =
-                "(Landroid/view/View;Z)V";
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "removeFullScreenPluginView";
+        static constexpr char signature[] =
+                "(Landroid/view/View;)V";
         static const bool isStatic = true;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::GECKO;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
-    static auto RemovePluginView(mozilla::jni::Object::Param, bool) -> void;
+    static auto RemoveFullScreenPluginView(mozilla::jni::Object::Param) -> void;
 
     struct RequestUiThreadCallback_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int64_t> Args;
         static constexpr char name[] = "requestUiThreadCallback";