Bug 1046344 - Unbitrot and rearrange the old APZ glue code in Fennec. r=snorp
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 08 Aug 2014 17:42:20 -0400
changeset 221578 3cb6a9651e0a2e4d37b25f2278392450c5898a1b
parent 221577 9ff86c2ca0642c857ea3a88079ae8582b25508f0
child 221579 fcc3d8857f00a87a706c5084ffe0652e2b9f0266
push id583
push userbhearsum@mozilla.com
push dateMon, 24 Nov 2014 19:04:58 +0000
treeherdermozilla-release@c107e74250f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1046344
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1046344 - Unbitrot and rearrange the old APZ glue code in Fennec. r=snorp
mobile/android/base/GeckoAppShell.java
mobile/android/base/gfx/GeckoLayerClient.java
mobile/android/base/gfx/NativePanZoomController.java
mobile/android/base/gfx/PanZoomTarget.java
mobile/android/chrome/content/browser.js
mozglue/android/jni-stubs.inc
widget/android/APZCCallbackHandler.cpp
widget/android/APZCCallbackHandler.h
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/moz.build
widget/android/nsIAndroidBridge.idl
widget/android/nsWindow.cpp
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -515,16 +515,34 @@ public class GeckoAppShell
     @WrapElementForJNI
     public static void acknowledgeEvent() {
         synchronized (sEventAckLock) {
             sWaitingForEventAck = false;
             sEventAckLock.notifyAll();
         }
     }
 
+    private static Runnable sCallbackRunnable = new Runnable() {
+        @Override
+        public void run() {
+            ThreadUtils.assertOnUiThread();
+            long nextDelay = runUiThreadCallback();
+            if (nextDelay >= 0) {
+                ThreadUtils.getUiHandler().postDelayed(this, nextDelay);
+            }
+        }
+    };
+
+    private static native long runUiThreadCallback();
+
+    @WrapElementForJNI(allowMultithread = true)
+    private static void requestUiThreadCallback(long delay) {
+        ThreadUtils.getUiHandler().postDelayed(sCallbackRunnable, delay);
+    }
+
     private static float getLocationAccuracy(Location location) {
         float radius = location.getAccuracy();
         return (location.hasAccuracy() && radius > 0) ? radius : 1001;
     }
 
     private static Location getLastKnownLocation(LocationManager lm) {
         Location lastKnownLocation = null;
         List<String> providers = lm.getAllProviders();
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -934,23 +934,16 @@ class GeckoLayerClient implements LayerV
     }
 
     /** Implementation of PanZoomTarget */
     @Override
     public void removeRenderTask(RenderTask task) {
         mView.removeRenderTask(task);
     }
 
-
-    /** Implementation of PanZoomTarget */
-    @Override
-    public boolean postDelayed(Runnable action, long delayMillis) {
-        return mView.postDelayed(action, delayMillis);
-    }
-
     /** Implementation of PanZoomTarget */
     @Override
     public Object getLock() {
         return this;
     }
 
     /** Implementation of PanZoomTarget
      * Converts a point from layer view coordinates to layer coordinates. In other words, given a
--- a/mobile/android/base/gfx/NativePanZoomController.java
+++ b/mobile/android/base/gfx/NativePanZoomController.java
@@ -16,22 +16,20 @@ import org.json.JSONObject;
 import android.graphics.PointF;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 
 class NativePanZoomController implements PanZoomController, GeckoEventListener {
     private final PanZoomTarget mTarget;
     private final EventDispatcher mDispatcher;
-    private final CallbackRunnable mCallbackRunnable;
 
     NativePanZoomController(PanZoomTarget target, View view, EventDispatcher dispatcher) {
         mTarget = target;
         mDispatcher = dispatcher;
-        mCallbackRunnable = new CallbackRunnable();
         if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
             init();
         } else {
             mDispatcher.registerGeckoThreadListener(this, "Gecko:Ready");
         }
     }
 
     public void handleMessage(String event, JSONObject message) {
@@ -70,39 +68,23 @@ class NativePanZoomController implements
         // no-op in APZC, I think
     }
 
     public native void abortAnimation();
 
     private native void init();
     private native void handleTouchEvent(GeckoEvent event);
     private native void handleMotionEvent(GeckoEvent event);
-    private native long runDelayedCallback();
 
     public native void destroy();
     public native void notifyDefaultActionPrevented(boolean prevented);
     public native boolean getRedrawHint();
     public native void setOverScrollMode(int overscrollMode);
     public native int getOverScrollMode();
 
     @WrapElementForJNI(allowMultithread = true, stubName = "RequestContentRepaintWrapper")
     private void requestContentRepaint(float x, float y, float width, float height, float resolution) {
         mTarget.forceRedraw(new DisplayPortMetrics(x, y, x + width, y + height, resolution));
     }
 
-    @WrapElementForJNI(allowMultithread = true, stubName = "PostDelayedCallbackWrapper")
-    private void postDelayedCallback(long delay) {
-        mTarget.postDelayed(mCallbackRunnable, delay);
-    }
-
-    class CallbackRunnable implements Runnable {
-        @Override
-        public void run() {
-            long nextDelay = runDelayedCallback();
-            if (nextDelay >= 0) {
-                mTarget.postDelayed(this, nextDelay);
-            }
-        }
-    }
-
     public void setOverscrollHandler(final Overscroll listener) {
     }
 }
--- a/mobile/android/base/gfx/PanZoomTarget.java
+++ b/mobile/android/base/gfx/PanZoomTarget.java
@@ -20,14 +20,13 @@ public interface PanZoomTarget {
     public void setViewportMetrics(ImmutableViewportMetrics viewport);
     public void scrollBy(float dx, float dy);
     public void scrollMarginsBy(float dx, float dy);
     public void panZoomStopped();
     /** This triggers an (asynchronous) viewport update/redraw. */
     public void forceRedraw(DisplayPortMetrics displayPort);
 
     public boolean post(Runnable action);
-    public boolean postDelayed(Runnable action, long delayMillis);
     public void postRenderTask(RenderTask task);
     public void removeRenderTask(RenderTask task);
     public Object getLock();
     public PointF convertViewPointToLayerPoint(PointF viewPoint);
 }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -851,20 +851,16 @@ var BrowserApp = {
     window.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).isFirstPaint = true;
     Services.androidBridge.contentDocumentChanged();
   },
 
   get tabs() {
     return this._tabs;
   },
 
-  get selectedTab() {
-    return this._selectedTab;
-  },
-
   set selectedTab(aTab) {
     if (this._selectedTab == aTab)
       return;
 
     if (this._selectedTab) {
       this._selectedTab.setActive(false);
     }
 
@@ -1757,16 +1753,21 @@ var BrowserApp = {
 
   get layersTileHeight() {
     delete this.layersTileHeight;
     let height = Services.prefs.getIntPref("layers.tile-height");
     return this.layersTileHeight = height;
   },
 
   // nsIAndroidBrowserApp
+  get selectedTab() {
+    return this._selectedTab;
+  },
+
+  // nsIAndroidBrowserApp
   getBrowserTab: function(tabId) {
     return this.getTabForId(tabId);
   },
 
   getUITelemetryObserver: function() {
     return UITelemetry;
   },
 
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -338,16 +338,35 @@ Java_org_mozilla_gecko_GeckoAppShell_not
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent", &f_Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent);
 #endif
 
 #ifdef JNI_STUBS
 
+typedef jlong (*Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback_t)(JNIEnv *, jclass);
+static Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback_t f_Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback;
+extern "C" NS_EXPORT jlong JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback(JNIEnv * arg0, jclass arg1) {
+    if (!f_Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback) {
+        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
+                       "JNI Function called before it was loaded");
+        return 0;
+    }
+    return f_Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback(arg0, arg1);
+}
+#endif
+
+#ifdef JNI_BINDINGS
+  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback", &f_Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback);
+#endif
+
+#ifdef JNI_STUBS
+
 typedef void (*Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge_t)(JNIEnv *, jclass, jbyteArray);
 static Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge_t f_Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge;
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge(JNIEnv * arg0, jclass arg1, jbyteArray arg2) {
     if (!f_Java_org_mozilla_gecko_GeckoAppShell_cameraCallbackBridge) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return ;
@@ -452,35 +471,16 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_gfx_NativePanZoomController_handleMotionEvent", &f_Java_org_mozilla_gecko_gfx_NativePanZoomController_handleMotionEvent);
 #endif
 
 #ifdef JNI_STUBS
 
-typedef jlong (*Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback_t)(JNIEnv *, jobject);
-static Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback_t f_Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback;
-extern "C" NS_EXPORT jlong JNICALL
-Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback(JNIEnv * arg0, jobject arg1) {
-    if (!f_Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback) {
-        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
-                       "JNI Function called before it was loaded");
-        return 0;
-    }
-    return f_Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback(arg0, arg1);
-}
-#endif
-
-#ifdef JNI_BINDINGS
-  xul_dlsym("Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback", &f_Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback);
-#endif
-
-#ifdef JNI_STUBS
-
 typedef void (*Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy_t)(JNIEnv *, jobject);
 static Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy_t f_Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy;
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy(JNIEnv * arg0, jobject arg1) {
     if (!f_Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return ;
new file mode 100644
--- /dev/null
+++ b/widget/android/APZCCallbackHandler.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: c++; 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/. */
+
+#include "APZCCallbackHandler.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "nsAppShell.h"
+#include "nsLayoutUtils.h"
+#include "nsPrintfCString.h"
+#include "nsThreadUtils.h"
+#include "base/message_loop.h"
+#include "nsWindow.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "AndroidBridge.h"
+
+using mozilla::layers::APZCCallbackHelper;
+using mozilla::layers::FrameMetrics;
+using mozilla::layers::ScrollableLayerGuid;
+
+namespace mozilla {
+namespace widget {
+namespace android {
+
+StaticRefPtr<APZCCallbackHandler> APZCCallbackHandler::sInstance;
+
+NativePanZoomController*
+APZCCallbackHandler::SetNativePanZoomController(jobject obj)
+{
+    NativePanZoomController* old = mNativePanZoomController;
+    mNativePanZoomController = NativePanZoomController::Wrap(obj);
+    return old;
+}
+
+nsIDOMWindowUtils*
+APZCCallbackHandler::GetDOMWindowUtils()
+{
+    nsIAndroidBrowserApp* browserApp = nullptr;
+    if (!nsAppShell::gAppShell) {
+        return nullptr;
+    }
+    nsAppShell::gAppShell->GetBrowserApp(&browserApp);
+    if (!browserApp) {
+        return nullptr;
+    }
+    nsIBrowserTab* tab = nullptr;
+    if (browserApp->GetSelectedTab(&tab) != NS_OK) {
+        return nullptr;
+    }
+    nsIDOMWindow* window = nullptr;
+    if (tab->GetWindow(&window) != NS_OK) {
+        return nullptr;
+    }
+    nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
+    return utils.get();
+}
+
+void
+APZCCallbackHandler::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+    if (!NS_IsMainThread()) {
+        NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<FrameMetrics>(
+            this, &APZCCallbackHandler::RequestContentRepaint, aFrameMetrics));
+        return;
+    }
+
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID);
+
+    if (aFrameMetrics.mIsRoot) {
+        nsIDOMWindowUtils* utils = GetDOMWindowUtils();
+        if (utils && APZCCallbackHelper::HasValidPresShellId(utils, aFrameMetrics)) {
+            FrameMetrics metrics = aFrameMetrics;
+            APZCCallbackHelper::UpdateRootFrame(utils, metrics);
+            APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, metrics);
+        }
+    } else {
+        // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe.
+        // This requires special handling.
+        nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aFrameMetrics.GetScrollId());
+        if (content) {
+            FrameMetrics newSubFrameMetrics(aFrameMetrics);
+            APZCCallbackHelper::UpdateSubFrame(content, newSubFrameMetrics);
+            APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, newSubFrameMetrics);
+        }
+    }
+}
+
+void
+APZCCallbackHandler::AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId,
+                                             const uint32_t& aScrollGeneration)
+{
+    APZCCallbackHelper::AcknowledgeScrollUpdate(aScrollId, aScrollGeneration);
+}
+
+void
+APZCCallbackHandler::HandleDoubleTap(const CSSPoint& aPoint,
+                                     int32_t aModifiers,
+                                     const mozilla::layers::ScrollableLayerGuid& aGuid)
+{
+    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
+    nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
+            NS_LITERAL_CSTRING("Gesture:DoubleTap"), data));
+}
+
+void
+APZCCallbackHandler::HandleSingleTap(const CSSPoint& aPoint,
+                                     int32_t aModifiers,
+                                     const mozilla::layers::ScrollableLayerGuid& aGuid)
+{
+    // FIXME Send the modifier data to Gecko for use in mouse events.
+    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
+    nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
+            NS_LITERAL_CSTRING("Gesture:SingleTap"), data));
+}
+
+void
+APZCCallbackHandler::HandleLongTap(const CSSPoint& aPoint,
+                                   int32_t aModifiers,
+                                   const mozilla::layers::ScrollableLayerGuid& aGuid)
+{
+    // TODO send content response back to APZC
+    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
+    nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
+            NS_LITERAL_CSTRING("Gesture:LongPress"), data));
+}
+
+void
+APZCCallbackHandler::HandleLongTapUp(const CSSPoint& aPoint,
+                                     int32_t aModifiers,
+                                     const mozilla::layers::ScrollableLayerGuid& aGuid)
+{
+    HandleSingleTap(aPoint, aModifiers, aGuid);
+}
+
+void
+APZCCallbackHandler::SendAsyncScrollDOMEvent(bool aIsRoot,
+                                             const CSSRect& aContentRect,
+                                             const CSSSize& aScrollableSize)
+{
+    // no-op, we don't want to support this event on fennec, and we
+    // want to get rid of this entirely. See bug 898075.
+}
+
+void
+APZCCallbackHandler::PostDelayedTask(Task* aTask, int aDelayMs)
+{
+    AndroidBridge::Bridge()->PostTaskToUiThread(aTask, aDelayMs);
+}
+
+} // namespace android
+} // namespace widget
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/android/APZCCallbackHandler.h
@@ -0,0 +1,63 @@
+/* -*- Mode: c++; 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/. */
+
+#ifndef APZCCallbackHandler_h__
+#define APZCCallbackHandler_h__
+
+#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/TimeStamp.h"
+#include "GeneratedJNIWrappers.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace widget {
+namespace android {
+
+class APZCCallbackHandler MOZ_FINAL : public mozilla::layers::GeckoContentController
+{
+private:
+    static StaticRefPtr<APZCCallbackHandler> sInstance;
+    NativePanZoomController* mNativePanZoomController;
+
+private:
+    APZCCallbackHandler()
+      : mNativePanZoomController(nullptr)
+    {}
+
+    nsIDOMWindowUtils* GetDOMWindowUtils();
+
+public:
+    static APZCCallbackHandler* GetInstance() {
+        if (sInstance.get() == nullptr) {
+            sInstance = new APZCCallbackHandler();
+        }
+        return sInstance.get();
+    }
+
+    NativePanZoomController* SetNativePanZoomController(jobject obj);
+
+public: // GeckoContentController methods
+    void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
+    void AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
+                                 const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
+    void HandleDoubleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
+                         const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+    void HandleSingleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
+                         const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+    void HandleLongTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
+                       const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+    void HandleLongTapUp(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
+                         const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
+    void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect& aContentRect,
+                                 const mozilla::CSSSize& aScrollableSize) MOZ_OVERRIDE;
+    void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
+};
+
+} // namespace android
+} // namespace widget
+} // namespace mozilla
+
+#endif
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -41,17 +41,17 @@
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIHttpChannel.h"
 
 using namespace mozilla;
 using namespace mozilla::widget::android;
 using namespace mozilla::gfx;
 
-StaticRefPtr<AndroidBridge> AndroidBridge::sBridge;
+AndroidBridge* AndroidBridge::sBridge;
 static unsigned sJavaEnvThreadIndex = 0;
 static jobject sGlobalContext = nullptr;
 static void JavaThreadDetachFunc(void *arg);
 
 // This is a dummy class that can be used in the template for android::sp
 class AndroidRefable {
     void incStrong(void* thing) { }
     void decStrong(void* thing) { }
@@ -1544,18 +1544,17 @@ void AndroidBridge::SyncFrameMetrics(con
 
     aOffset.x = viewTransform->getoffsetX();
     aOffset.y = viewTransform->getoffsetY();
 
     delete viewTransform;
 }
 
 AndroidBridge::AndroidBridge()
-  : mLayerClient(nullptr),
-    mNativePanZoomController(nullptr)
+  : mLayerClient(nullptr)
 {
 }
 
 AndroidBridge::~AndroidBridge()
 {
 }
 
 /* Implementation file */
@@ -1970,87 +1969,18 @@ AndroidBridge::ProgressiveUpdateCallback
     aZoom.scale = progressiveUpdateData->getscale();
 
     bool ret = progressiveUpdateData->getabort();
     delete progressiveUpdateData;
 
     return ret;
 }
 
-mozilla::widget::android::NativePanZoomController*
-AndroidBridge::SetNativePanZoomController(jobject obj)
-{
-    mozilla::widget::android::NativePanZoomController* old = mNativePanZoomController;
-    mNativePanZoomController = mozilla::widget::android::NativePanZoomController::Wrap(obj);
-    return old;
-}
-
 void
-AndroidBridge::RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics)
-{
-    ALOG_BRIDGE("AndroidBridge::RequestContentRepaint");
-
-    // FIXME implement this
-}
-
-void
-AndroidBridge::AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
-                                       const uint32_t& aScrollGeneration)
-{
-    // FIXME implement this
-}
-
-void
-AndroidBridge::HandleDoubleTap(const CSSPoint& aPoint,
-                               int32_t aModifiers,
-                               const mozilla::layers::ScrollableLayerGuid& aGuid)
-{
-    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
-    nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
-            NS_LITERAL_CSTRING("Gesture:DoubleTap"), data));
-}
-
-void
-AndroidBridge::HandleSingleTap(const CSSPoint& aPoint,
-                               int32_t aModifiers,
-                               const mozilla::layers::ScrollableLayerGuid& aGuid)
-{
-    // TODO Send the modifier data to Gecko for use in mouse events.
-    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
-    nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
-            NS_LITERAL_CSTRING("Gesture:SingleTap"), data));
-}
-
-void
-AndroidBridge::HandleLongTap(const CSSPoint& aPoint,
-                             int32_t aModifiers,
-                             const mozilla::layers::ScrollableLayerGuid& aGuid)
-{
-    nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
-    nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
-            NS_LITERAL_CSTRING("Gesture:LongPress"), data));
-}
-
-void
-AndroidBridge::HandleLongTapUp(const CSSPoint& aPoint,
-                               int32_t aModifiers,
-                               const mozilla::layers::ScrollableLayerGuid& aGuid)
-{
-}
-
-void
-AndroidBridge::SendAsyncScrollDOMEvent(bool aIsRoot,
-                                       const CSSRect& aContentRect,
-                                       const CSSSize& aScrollableSize)
-{
-    // FIXME implement this
-}
-
-void
-AndroidBridge::PostDelayedTask(Task* aTask, int aDelayMs)
+AndroidBridge::PostTaskToUiThread(Task* aTask, int aDelayMs)
 {
     // add the new task into the mDelayedTaskQueue, sorted with
     // the earliest task first in the queue
     DelayedTask* newTask = new DelayedTask(aTask, aDelayMs);
     uint32_t i = 0;
     while (i < mDelayedTaskQueue.Length()) {
         if (newTask->IsEarlierThan(mDelayedTaskQueue[i])) {
             mDelayedTaskQueue.InsertElementAt(i, newTask);
@@ -2061,22 +1991,22 @@ AndroidBridge::PostDelayedTask(Task* aTa
     if (i == mDelayedTaskQueue.Length()) {
         // this new task will run after all the existing tasks in the queue
         mDelayedTaskQueue.AppendElement(newTask);
     }
     if (i == 0) {
         // if we're inserting it at the head of the queue, notify Java because
         // we need to get a callback at an earlier time than the last scheduled
         // callback
-        mNativePanZoomController->PostDelayedCallbackWrapper((int64_t)aDelayMs);
+        GeckoAppShell::RequestUiThreadCallback((int64_t)aDelayMs);
     }
 }
 
 int64_t
-AndroidBridge::RunDelayedTasks()
+AndroidBridge::RunDelayedUiThreadTasks()
 {
     while (mDelayedTaskQueue.Length() > 0) {
         DelayedTask* nextTask = mDelayedTaskQueue[0];
         int64_t timeLeft = nextTask->MillisecondsToRunTime();
         if (timeLeft > 0) {
             // this task (and therefore all remaining tasks)
             // have not yet reached their runtime. return the
             // time left until we should be called again
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -21,27 +21,27 @@
 #include "nsColor.h"
 #include "gfxRect.h"
 
 #include "nsIAndroidBridge.h"
 #include "nsIMobileMessageCallback.h"
 
 #include "mozilla/Likely.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Types.h"
 
 // Some debug #defines
 // #define DEBUG_ANDROID_EVENTS
 // #define DEBUG_ANDROID_WIDGET
 
 class nsWindow;
 class nsIDOMMozSmsMessage;
 class nsIObserver;
+class Task;
 
 /* See the comment in AndroidBridge about this function before using it */
 extern "C" MOZ_EXPORT JNIEnv * GetJNIForThread();
 
 extern bool mozilla_AndroidBridge_SetMainThread(pthread_t);
 
 namespace base {
 class Thread;
@@ -90,38 +90,38 @@ public:
 protected:
     virtual ~nsFilePickerCallback() {}
 };
 
 class DelayedTask {
 public:
     DelayedTask(Task* aTask, int aDelayMs) {
         mTask = aTask;
-        mRunTime = TimeStamp::Now() + TimeDuration::FromMilliseconds(aDelayMs);
+        mRunTime = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(aDelayMs);
     }
 
     bool IsEarlierThan(DelayedTask *aOther) {
         return mRunTime < aOther->mRunTime;
     }
 
     int64_t MillisecondsToRunTime() {
-        TimeDuration timeLeft = mRunTime - TimeStamp::Now();
+        mozilla::TimeDuration timeLeft = mRunTime - mozilla::TimeStamp::Now();
         return (int64_t)timeLeft.ToMilliseconds();
     }
 
     Task* GetTask() {
         return mTask;
     }
 
 private:
     Task* mTask;
-    TimeStamp mRunTime;
+    mozilla::TimeStamp mRunTime;
 };
 
-class AndroidBridge MOZ_FINAL : public mozilla::layers::GeckoContentController
+class AndroidBridge MOZ_FINAL
 {
 public:
     enum {
         // Values for NotifyIME, in addition to values from the Gecko
         // IMEMessage enum; use negative values here to prevent conflict
         NOTIFY_IME_OPEN_VKB = -2,
         NOTIFY_IME_REPLY_EVENT = -1,
     };
@@ -129,17 +129,17 @@ public:
     enum {
         LAYER_CLIENT_TYPE_NONE = 0,
         LAYER_CLIENT_TYPE_GL = 2            // AndroidGeckoGLLayerClient
     };
 
     static void ConstructBridge(JNIEnv *jEnv);
 
     static AndroidBridge *Bridge() {
-        return sBridge.get();
+        return sBridge;
     }
 
     static JavaVM *GetVM() {
         MOZ_ASSERT(sBridge);
         return sBridge->mJavaVM;
     }
 
 
@@ -348,17 +348,17 @@ public:
 
     static void InputStreamClose(jobject obj);
     static uint32_t InputStreamAvailable(jobject obj);
     static nsresult InputStreamRead(jobject obj, char *aBuf, uint32_t aCount, uint32_t *aRead);
 
     static nsresult GetExternalPublicDirectory(const nsAString& aType, nsAString& aPath);
 
 protected:
-    static StaticRefPtr<AndroidBridge> sBridge;
+    static AndroidBridge* sBridge;
     nsTArray<nsCOMPtr<nsIMobileMessageCallback> > mSmsRequests;
 
     // the global JavaVM
     JavaVM *mJavaVM;
 
     // the JNIEnv for the main thread
     JNIEnv *mJNIEnv;
     pthread_t mThread;
@@ -433,45 +433,22 @@ protected:
     int (* ANativeWindow_unlockAndPost)(void *window);
 
     int (* Surface_lock)(void* surface, void* surfaceInfo, void* region, bool block);
     int (* Surface_unlockAndPost)(void* surface);
     void (* Region_constructor)(void* region);
     void (* Region_set)(void* region, void* rect);
 
 private:
-    mozilla::widget::android::NativePanZoomController* mNativePanZoomController;
-    // This will always be accessed from one thread (the APZC "controller"
-    // thread, which is the Java UI thread), so we don't need to do locking
-    // to touch it
+    // This will always be accessed from one thread (the Java UI thread),
+    // so we don't need to do locking to touch it.
     nsTArray<DelayedTask*> mDelayedTaskQueue;
-
 public:
-    mozilla::widget::android::NativePanZoomController* SetNativePanZoomController(jobject obj);
-    // GeckoContentController methods
-    void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
-    void AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
-                                 const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
-    void HandleDoubleTap(const CSSPoint& aPoint,
-                         int32_t aModifiers,
-                         const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void HandleSingleTap(const CSSPoint& aPoint,
-                         int32_t aModifiers,
-                         const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void HandleLongTap(const CSSPoint& aPoint,
-                       int32_t aModifiers,
-                       const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void HandleLongTapUp(const CSSPoint& aPoint,
-                         int32_t aModifiers,
-                         const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    void SendAsyncScrollDOMEvent(bool aIsRoot,
-                                 const CSSRect& aContentRect,
-                                 const CSSSize& aScrollableSize) MOZ_OVERRIDE;
-    void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
-    int64_t RunDelayedTasks();
+    void PostTaskToUiThread(Task* aTask, int aDelayMs);
+    int64_t RunDelayedUiThreadTasks();
 };
 
 class AutoJObject {
 public:
     AutoJObject(JNIEnv* aJNIEnv = nullptr) : mObject(nullptr)
     {
         mJNIEnv = aJNIEnv ? aJNIEnv : AndroidBridge::GetJNIEnv();
     }
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Hal.h"
 #include "nsIFile.h"
 #include "nsString.h"
 
 #include "AndroidBridge.h"
 #include "AndroidGraphicBuffer.h"
+#include "APZCCallbackHandler.h"
 
 #include <jni.h>
 #include <pthread.h>
 #include <dlfcn.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <sched.h>
 
@@ -74,16 +75,26 @@ Java_org_mozilla_gecko_GeckoAppShell_not
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *jenv, jclass, jboolean mayWait)
 {
     // poke the appshell
     if (nsAppShell::gAppShell)
         nsAppShell::gAppShell->ProcessNextNativeEvent(mayWait != JNI_FALSE);
 }
 
+NS_EXPORT jlong JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_runUiThreadCallback(JNIEnv* env, jclass)
+{
+    if (!AndroidBridge::Bridge()) {
+        return -1;
+    }
+
+    return AndroidBridge::Bridge()->RunDelayedUiThreadTasks();
+}
+
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject obj)
 {
     AndroidBridge::Bridge()->SetLayerClient(jenv, obj);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *jenv, jclass jc)
@@ -859,17 +870,17 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_init(JNIEnv* env, jobject instance)
 {
     if (!AndroidBridge::Bridge()) {
         return;
     }
 
-    NativePanZoomController* oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(instance);
+    NativePanZoomController* oldRef = APZCCallbackHandler::GetInstance()->SetNativePanZoomController(instance);
     if (oldRef && !oldRef->isNull()) {
         MOZ_ASSERT(false, "Registering a new NPZC when we already have one");
         delete oldRef;
     }
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_handleTouchEvent(JNIEnv* env, jobject instance, jobject event)
@@ -886,34 +897,24 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_handleMotionEvent(JNIEnv* env, jobject instance, jobject event)
 {
     // FIXME implement this
 }
 
-NS_EXPORT jlong JNICALL
-Java_org_mozilla_gecko_gfx_NativePanZoomController_runDelayedCallback(JNIEnv* env, jobject instance)
-{
-    if (!AndroidBridge::Bridge()) {
-        return -1;
-    }
-
-    return AndroidBridge::Bridge()->RunDelayedTasks();
-}
-
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy(JNIEnv* env, jobject instance)
 {
     if (!AndroidBridge::Bridge()) {
         return;
     }
 
-    NativePanZoomController* oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(nullptr);
+    NativePanZoomController* oldRef = APZCCallbackHandler::GetInstance()->SetNativePanZoomController(nullptr);
     if (!oldRef || oldRef->isNull()) {
         MOZ_ASSERT(false, "Clearing a non-existent NPZC");
     } else {
         delete oldRef;
     }
 }
 
 NS_EXPORT void JNICALL
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -74,16 +74,17 @@ jmethodID GeckoAppShell::jNotifyIMEChang
 jmethodID GeckoAppShell::jNotifyIMEContext = 0;
 jmethodID GeckoAppShell::jNotifyWakeLockChanged = 0;
 jmethodID GeckoAppShell::jNotifyXreExit = 0;
 jmethodID GeckoAppShell::jOpenUriExternal = 0;
 jmethodID GeckoAppShell::jPerformHapticFeedback = 0;
 jmethodID GeckoAppShell::jPumpMessageLoop = 0;
 jmethodID GeckoAppShell::jRegisterSurfaceTextureFrameListener = 0;
 jmethodID GeckoAppShell::jRemovePluginView = 0;
+jmethodID GeckoAppShell::jRequestUiThreadCallback = 0;
 jmethodID GeckoAppShell::jScanMedia = 0;
 jmethodID GeckoAppShell::jScheduleRestart = 0;
 jmethodID GeckoAppShell::jSendMessageWrapper = 0;
 jmethodID GeckoAppShell::jSetFullScreen = 0;
 jmethodID GeckoAppShell::jSetKeepScreenOn = 0;
 jmethodID GeckoAppShell::jSetURITitle = 0;
 jmethodID GeckoAppShell::jShowAlertNotificationWrapper = 0;
 jmethodID GeckoAppShell::jShowInputMethodPicker = 0;
@@ -160,16 +161,17 @@ void GeckoAppShell::InitStubs(JNIEnv *jE
     jNotifyIMEContext = getStaticMethod("notifyIMEContext", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     jNotifyWakeLockChanged = getStaticMethod("notifyWakeLockChanged", "(Ljava/lang/String;Ljava/lang/String;)V");
     jNotifyXreExit = getStaticMethod("onXreExit", "()V");
     jOpenUriExternal = getStaticMethod("openUriExternal", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
     jPerformHapticFeedback = getStaticMethod("performHapticFeedback", "(Z)V");
     jPumpMessageLoop = getStaticMethod("pumpMessageLoop", "()Z");
     jRegisterSurfaceTextureFrameListener = getStaticMethod("registerSurfaceTextureFrameListener", "(Ljava/lang/Object;I)V");
     jRemovePluginView = getStaticMethod("removePluginView", "(Landroid/view/View;Z)V");
+    jRequestUiThreadCallback = getStaticMethod("requestUiThreadCallback", "(J)V");
     jScanMedia = getStaticMethod("scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V");
     jScheduleRestart = getStaticMethod("scheduleRestart", "()V");
     jSendMessageWrapper = getStaticMethod("sendMessage", "(Ljava/lang/String;Ljava/lang/String;I)V");
     jSetFullScreen = getStaticMethod("setFullScreen", "(Z)V");
     jSetKeepScreenOn = getStaticMethod("setKeepScreenOn", "(Z)V");
     jSetURITitle = getStaticMethod("setUriTitle", "(Ljava/lang/String;Ljava/lang/String;)V");
     jShowAlertNotificationWrapper = getStaticMethod("showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     jShowInputMethodPicker = getStaticMethod("showInputMethodPicker", "()V");
@@ -1101,16 +1103,28 @@ void GeckoAppShell::RemovePluginView(job
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jRemovePluginView, a0, a1);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
 
+void GeckoAppShell::RequestUiThreadCallback(int64_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallStaticVoidMethod(mGeckoAppShellClass, jRequestUiThreadCallback, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
 void GeckoAppShell::ScanMedia(const nsAString& a0, const nsAString& a1) {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(2) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     jstring j0 = AndroidBridge::NewJavaString(env, a0);
@@ -1991,45 +2005,31 @@ jobject LayerView::RegisterCompositorWra
     }
 
     jobject temp = env->CallStaticObjectMethod(mLayerViewClass, jRegisterCompositorWrapper);
     AndroidBridge::HandleUncaughtException(env);
     jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
     return ret;
 }
 jclass NativePanZoomController::mNativePanZoomControllerClass = 0;
-jmethodID NativePanZoomController::jPostDelayedCallbackWrapper = 0;
 jmethodID NativePanZoomController::jRequestContentRepaintWrapper = 0;
 void NativePanZoomController::InitStubs(JNIEnv *jEnv) {
     initInit();
 
     mNativePanZoomControllerClass = getClassGlobalRef("org/mozilla/gecko/gfx/NativePanZoomController");
-    jPostDelayedCallbackWrapper = getMethod("postDelayedCallback", "(J)V");
     jRequestContentRepaintWrapper = getMethod("requestContentRepaint", "(FFFFF)V");
 }
 
 NativePanZoomController* NativePanZoomController::Wrap(jobject obj) {
     JNIEnv *env = GetJNIForThread();
     NativePanZoomController* ret = new NativePanZoomController(obj, env);
     env->DeleteLocalRef(obj);
     return ret;
 }
 
-void NativePanZoomController::PostDelayedCallbackWrapper(int64_t a0) {
-    JNIEnv *env = GetJNIForThread();
-    if (env->PushLocalFrame(0) != 0) {
-        AndroidBridge::HandleUncaughtException(env);
-        MOZ_CRASH("Exception should have caused crash.");
-    }
-
-    env->CallVoidMethod(wrapped_obj, jPostDelayedCallbackWrapper, a0);
-    AndroidBridge::HandleUncaughtException(env);
-    env->PopLocalFrame(nullptr);
-}
-
 void NativePanZoomController::RequestContentRepaintWrapper(jfloat a0, jfloat a1, jfloat a2, jfloat a3, jfloat a4) {
     JNIEnv *env = GetJNIForThread();
     if (env->PushLocalFrame(0) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     jvalue args[5];
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -81,16 +81,17 @@ public:
     static void NotifyIMEContext(int32_t a0, const nsAString& a1, const nsAString& a2, const nsAString& a3);
     static void NotifyWakeLockChanged(const nsAString& a0, const nsAString& a1);
     static void NotifyXreExit();
     static bool OpenUriExternal(const nsAString& a0, const nsAString& a1, const nsAString& a2 = EmptyString(), const nsAString& a3 = EmptyString(), const nsAString& a4 = EmptyString(), const nsAString& a5 = EmptyString());
     static void PerformHapticFeedback(bool a0);
     static bool PumpMessageLoop();
     static void RegisterSurfaceTextureFrameListener(jobject a0, int32_t a1);
     static void RemovePluginView(jobject a0, bool a1);
+    static void RequestUiThreadCallback(int64_t a0);
     static void ScanMedia(const nsAString& a0, const nsAString& a1);
     static void ScheduleRestart();
     static void SendMessageWrapper(const nsAString& a0, const nsAString& a1, int32_t a2);
     static void SetFullScreen(bool a0);
     static void SetKeepScreenOn(bool a0);
     static void SetURITitle(const nsAString& a0, const nsAString& a1);
     static void ShowAlertNotificationWrapper(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3, const nsAString& a4);
     static void ShowInputMethodPicker();
@@ -166,16 +167,17 @@ protected:
     static jmethodID jNotifyIMEContext;
     static jmethodID jNotifyWakeLockChanged;
     static jmethodID jNotifyXreExit;
     static jmethodID jOpenUriExternal;
     static jmethodID jPerformHapticFeedback;
     static jmethodID jPumpMessageLoop;
     static jmethodID jRegisterSurfaceTextureFrameListener;
     static jmethodID jRemovePluginView;
+    static jmethodID jRequestUiThreadCallback;
     static jmethodID jScanMedia;
     static jmethodID jScheduleRestart;
     static jmethodID jSendMessageWrapper;
     static jmethodID jSetFullScreen;
     static jmethodID jSetKeepScreenOn;
     static jmethodID jSetURITitle;
     static jmethodID jShowAlertNotificationWrapper;
     static jmethodID jShowInputMethodPicker;
@@ -359,22 +361,20 @@ protected:
     static jmethodID jRegisterCompositorWrapper;
 };
 
 class NativePanZoomController : public AutoGlobalWrappedJavaObject {
 public:
     static void InitStubs(JNIEnv *jEnv);
     static NativePanZoomController* Wrap(jobject obj);
     NativePanZoomController(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
-    void PostDelayedCallbackWrapper(int64_t a0);
     void RequestContentRepaintWrapper(jfloat a0, jfloat a1, jfloat a2, jfloat a3, jfloat a4);
     NativePanZoomController() : AutoGlobalWrappedJavaObject() {};
 protected:
     static jclass mNativePanZoomControllerClass;
-    static jmethodID jPostDelayedCallbackWrapper;
     static jmethodID jRequestContentRepaintWrapper;
 };
 
 class ProgressiveUpdateData : public AutoGlobalWrappedJavaObject {
 public:
     static void InitStubs(JNIEnv *jEnv);
     static ProgressiveUpdateData* Wrap(jobject obj);
     ProgressiveUpdateData(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -19,16 +19,17 @@ EXPORTS += [
 
 SOURCES += [
     'AndroidBridge.cpp',
     'AndroidDirectTexture.cpp',
     'AndroidGraphicBuffer.cpp',
     'AndroidJavaWrappers.cpp',
     'AndroidJNI.cpp',
     'AndroidJNIWrapper.cpp',
+    'APZCCallbackHandler.cpp',
     'GeneratedJNIWrappers.cpp',
     'GfxInfo.cpp',
     'NativeJSContainer.cpp',
     'nsAndroidProtocolHandler.cpp',
     'nsAppShell.cpp',
     'nsClipboard.cpp',
     'nsDeviceContextAndroid.cpp',
     'nsIdleServiceAndroid.cpp',
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -19,18 +19,19 @@ interface nsIUITelemetryObserver : nsISu
                    in wstring reason,
                    in unsigned long timestamp);
   void addEvent(in wstring action,
                 in wstring method,
                 in unsigned long timestamp,
                 in wstring extras);
 };
 
-[scriptable, uuid(c31331d2-afad-460f-9c66-728b8c838cec)]
+[scriptable, uuid(78ec5811-78ee-4239-a554-3303f823dbbc)]
 interface nsIAndroidBrowserApp : nsISupports {
+  readonly attribute nsIBrowserTab selectedTab;
   nsIBrowserTab getBrowserTab(in int32_t tabId);
   void getPreferences(in int32_t requestId,
                       [array, size_is(count)] in wstring prefNames,
                       in unsigned long count);
   void observePreferences(in int32_t requestId,
                           [array, size_is(count)] in wstring prefNames,
                           in unsigned long count);
   void removePreferenceObservers(in int32_t requestId);
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -43,16 +43,17 @@ using mozilla::unused;
 #include "Layers.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "ScopedGLHelpers.h"
 #include "mozilla/layers/CompositorOGL.h"
+#include "APZCCallbackHandler.h"
 
 #include "nsTArray.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include "android_npapi.h"
 
 #include "imgIEncoder.h"
@@ -2387,17 +2388,17 @@ mozilla::layers::APZCTreeManager*
 nsWindow::GetAPZCTreeManager()
 {
     if (!sApzcTreeManager) {
         CompositorParent* compositor = sCompositorParent;
         if (!compositor) {
             return nullptr;
         }
         uint64_t rootLayerTreeId = compositor->RootLayerTreeId();
-        CompositorParent::SetControllerForLayerTree(rootLayerTreeId, AndroidBridge::Bridge());
+        CompositorParent::SetControllerForLayerTree(rootLayerTreeId, mozilla::widget::android::APZCCallbackHandler::GetInstance());
         sApzcTreeManager = CompositorParent::GetAPZCTreeManager(rootLayerTreeId);
     }
     return sApzcTreeManager;
 }
 
 uint64_t
 nsWindow::RootLayerTreeId()
 {