Partial work for android OGLSurfaceView
authorBenoit Girard <b56girard@gmail.com>
Thu, 26 Jan 2012 14:23:13 -0500
changeset 90851 e94e72794e8248ad8ab4f68b3ae94bb38ebc4bcc
parent 90850 2644887b175e940711ba244c28df58f5e11e61f0
child 90852 82ad03b2e727cec42d5e0a1eedc3c5f331057955
push idunknown
push userunknown
push dateunknown
milestone12.0a1
Partial work for android OGLSurfaceView
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoInputConnection.java
mobile/android/base/Makefile.in
mobile/android/base/gfx/Layer.java
mobile/android/base/gfx/LayerClient.java
mobile/android/base/gfx/LayerController.java
mobile/android/base/gfx/LayerRenderer.java
mobile/android/base/gfx/LayerView.java
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
widget/android/AndroidJavaWrappers.cpp
widget/android/nsWindow.cpp
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -1922,25 +1922,32 @@ CreateSurfaceForWindow(nsIWidget *aWidge
     EGLSurface surface;
 
 
 #ifdef DEBUG
     sEGLLibrary.DumpEGLConfig(config);
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
+    printf_stderr("... requesting window surface from bridge\n");
+
     // On Android, we have to ask Java to make the eglCreateWindowSurface
     // call for us.  See GLHelpers.java for a description of why.
     //
     // We also only have one true "window", so we just use it directly and ignore
     // what was passed in.
+    AndroidGeckoSurfaceView& sview = mozilla::AndroidBridge::Bridge()->SurfaceView();
+    if (sview.isNull()) {
+        printf_stderr("got null surface\n");
+        return NULL;
+    }
+
     printf_stderr("... requesting window surface from bridge\n");
     surface = mozilla::AndroidBridge::Bridge()->
-        CallEglCreateWindowSurface(EGL_DISPLAY(), config,
-                                   mozilla::AndroidBridge::Bridge()->SurfaceView());
+        CallEglCreateWindowSurface(EGL_DISPLAY(), config, sview);
     printf_stderr("got surface %p\n", surface);
 #else
     surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, GET_NATIVE_WINDOW(aWidget), 0);
 #endif
 
 #ifdef MOZ_WIDGET_GONK
     gScreenBounds.x = 0;
     gScreenBounds.y = 0;
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -38,16 +38,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "CompositorParent.h"
 #include "ShadowLayersParent.h"
 #include "LayerManagerOGL.h"
 #include "nsIWidget.h"
 
+#if defined(MOZ_WIDGET_ANDROID)
+#include "AndroidBridge.h"
+#endif
+
 namespace mozilla {
 namespace layers {
 
 CompositorParent::CompositorParent(nsIWidget* aWidget)
   : mStopped(false), mWidget(aWidget)
 {
   MOZ_COUNT_CTOR(CompositorParent);
 }
@@ -121,16 +125,22 @@ CompositorParent::ShadowLayersUpdated()
     SetShadowProperties(root);
   }
   ScheduleComposition();
 }
 
 PLayersParent*
 CompositorParent::AllocPLayers(const LayersBackend &backendType)
 {
+#ifdef MOZ_WIDGET_ANDROID
+  // Registering with the compositor will create the surface view that
+  // the layer manager expects to attach to.
+  RegisterCompositorWithJava();
+#endif
+
   if (backendType == LayerManager::LAYERS_OPENGL) {
     nsRefPtr<LayerManagerOGL> layerManager = new LayerManagerOGL(mWidget);
     mWidget = NULL;
     mLayerManager = layerManager;
 
     if (!layerManager->Initialize()) {
       NS_ERROR("Failed to init OGL Layers");
       return NULL;
@@ -149,11 +159,20 @@ CompositorParent::AllocPLayers(const Lay
 
 bool
 CompositorParent::DeallocPLayers(PLayersParent* actor)
 {
   delete actor;
   return true;
 }
 
+#ifdef MOZ_WIDGET_ANDROID
+void
+CompositorParent::RegisterCompositorWithJava()
+{
+  mozilla::AndroidBridge::Bridge()->RegisterCompositor();
+}
+#endif
+
+
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -70,16 +70,26 @@ public:
 protected:
   virtual PLayersParent* AllocPLayers(const LayersBackend &backendType);
   virtual bool DeallocPLayers(PLayersParent* aLayers);
 
 private:
   void ScheduleComposition();
   void Composite();
 
+  // Platform specific functions
+#ifdef MOZ_WIDGET_ANDROID
+  /**
+   * Register the compositor thread with the Java native thread.
+   * This will replace the temporary compositor with the real
+   * Gecko compositor thread.
+   **/
+  void RegisterCompositorWithJava();
+#endif
+
   nsRefPtr<LayerManager> mLayerManager;
   bool mStopped;
   nsIWidget* mWidget;
 
   DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
 };
 
 } // layers
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -39,17 +39,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.FloatSize;
 import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.LayerController;
-import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PlaceholderLayerClient;
 import org.mozilla.gecko.gfx.RectUtils;
 import org.mozilla.gecko.gfx.ViewportMetrics;
 import org.mozilla.gecko.Tab.HistoryEntry;
 
 import java.io.*;
 import java.util.*;
 import java.util.regex.Pattern;
@@ -1527,17 +1526,17 @@ abstract public class GeckoApp
              *
              * TODO: Fall back to a built-in screenshot of the Fennec Start page for a nice first-
              * run experience, perhaps?
              */
             mLayerController = new LayerController(this);
             mPlaceholderLayerClient = PlaceholderLayerClient.createInstance(this);
             mLayerController.setLayerClient(mPlaceholderLayerClient);
 
-            mGeckoLayout.addView(mLayerController.getView(), 0);
+            mGeckoLayout.addView(mLayerController.getView().getAndroidView(), 0);
         }
 
         mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
 
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up");
 
         if (!sTryCatchAttached) {
             sTryCatchAttached = true;
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -36,17 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
 import org.mozilla.gecko.gfx.LayerController;
-import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.gfx.AbstractLayerView;
 
 import java.io.*;
 import java.lang.reflect.*;
 import java.nio.*;
 import java.nio.channels.*;
 import java.text.*;
 import java.util.*;
 import java.util.zip.*;
@@ -468,19 +468,18 @@ public class GeckoAppShell
 
         // and go
         GeckoAppShell.nativeRun(combinedArgs);
     }
 
     // Called on the UI thread after Gecko loads.
     private static void geckoLoaded() {
         final LayerController layerController = GeckoApp.mAppContext.getLayerController();
-        LayerView v = layerController.getView();
-        mInputConnection = GeckoInputConnection.create(v);
-        v.setInputConnectionHandler(mInputConnection);
+        AbstractLayerView v = layerController.getView();
+        mInputConnection = v.setInputConnectionHandler();
 
         layerController.setOnTouchListener(new View.OnTouchListener() {
             public boolean onTouch(View view, MotionEvent event) {
                 return true;
             }
         });
 
         layerController.notifyLayerClientOfGeometryChange();
@@ -551,17 +550,17 @@ public class GeckoAppShell
         if (tmp != null)
             tmp.countDown();
     }
 
     static Sensor gAccelerometerSensor = null;
     static Sensor gOrientationSensor = null;
 
     public static void enableDeviceMotion(boolean enable) {
-        LayerView v = GeckoApp.mAppContext.getLayerController().getView();
+        AbstractLayerView v = GeckoApp.mAppContext.getLayerController().getView();
         SensorManager sm = (SensorManager) v.getContext().getSystemService(Context.SENSOR_SERVICE);
 
         if (gAccelerometerSensor == null || gOrientationSensor == null) {
             gAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
             gOrientationSensor   = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);
         }
 
         if (enable) {
@@ -575,17 +574,17 @@ public class GeckoAppShell
             if (gOrientationSensor != null)
                 sm.unregisterListener(GeckoApp.mAppContext, gOrientationSensor);
         }
     }
 
     public static void enableLocation(final boolean enable) {
         getMainHandler().post(new Runnable() { 
                 public void run() {
-                    LayerView v = GeckoApp.mAppContext.getLayerController().getView();
+                    //AbstractLayerView v = GeckoApp.mAppContext.getLayerController().getView();
 
                     LocationManager lm = (LocationManager)
                         GeckoApp.mAppContext.getSystemService(Context.LOCATION_SERVICE);
 
                     if (enable) {
                         Criteria criteria = new Criteria();
                         String provider = lm.getBestProvider(criteria, true);
                         if (provider == null)
@@ -1049,25 +1048,25 @@ public class GeckoAppShell
 
     public static String showFilePicker(String aFilters) {
         return GeckoApp.mAppContext.
             showFilePicker(getMimeTypeFromExtensions(aFilters));
     }
 
     public static void performHapticFeedback(boolean aIsLongPress) {
         LayerController layerController = GeckoApp.mAppContext.getLayerController();
-        LayerView layerView = layerController.getView();
-        layerView.performHapticFeedback(aIsLongPress ?
+        AbstractLayerView layerView = layerController.getView();
+        layerView.getAndroidView().performHapticFeedback(aIsLongPress ?
                                         HapticFeedbackConstants.LONG_PRESS :
                                         HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
     private static Vibrator vibrator() {
         LayerController layerController = GeckoApp.mAppContext.getLayerController();
-        LayerView layerView = layerController.getView();
+        AbstractLayerView layerView = layerController.getView();
 
         return (Vibrator) layerView.getContext().getSystemService(Context.VIBRATOR_SERVICE);
     }
 
     public static void vibrate(long milliseconds) {
         vibrator().vibrate(milliseconds);
     }
 
@@ -1723,17 +1722,17 @@ public class GeckoAppShell
     public static void emitGeckoAccessibilityEvent (int eventType, String role, String text, String description, boolean enabled, boolean checked, boolean password) {
         AccessibilityManager accessibilityManager =
             (AccessibilityManager) GeckoApp.mAppContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
 
         if (!accessibilityManager.isEnabled())
             return;
 
         LayerController layerController = GeckoApp.mAppContext.getLayerController();
-        LayerView layerView = layerController.getView();
+        AbstractLayerView layerView = layerController.getView();
 
         AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         event.setClassName(layerView.getClass().getName() + "$" + role);
         event.setPackageName(GeckoApp.mAppContext.getPackageName());
         event.setEnabled(enabled);
         event.setChecked(checked);
         event.setPassword(password);
         event.setContentDescription(description);
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -354,17 +354,17 @@ public class GeckoInputConnection
                                                        mEditable.toString()));
                 setEditable(text);
             }
         }
 
         if (mUpdateRequest == null)
             return;
 
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
 
         if (imm == null) {
             imm = (InputMethodManager)v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
             if (imm == null)
                 return;
         }
 
         mUpdateExtract.flags = 0;
@@ -392,17 +392,17 @@ public class GeckoInputConnection
             int b = Selection.getSelectionEnd(content);
             if (start != a || end != b) {
                 if (DEBUG) Log.d(LOGTAG, String.format(". . . notifySelectionChange: current editable selection: [%d, %d]", a, b));
                 super.setSelection(start, end);
             }
         }
 
         if (imm != null && imm.isFullscreenMode()) {
-            View v = GeckoApp.mAppContext.getLayerController().getView();
+            View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
             imm.updateSelection(v, start, end, -1, -1);
         }
     }
 
     public void reset() {
         mComposing = false;
         mBatchMode = false;
         mUpdateRequest = null;
@@ -537,17 +537,17 @@ public class GeckoInputConnection
                 break;
         }
 
         if (isPreIme && mIMEState != IME_STATE_DISABLED &&
             (event.getMetaState() & KeyEvent.META_ALT_ON) != 0)
             // Let active IME process pre-IME key events
             return false;
 
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
 
         // KeyListener returns true if it handled the event for us.
         if (mIMEState == IME_STATE_DISABLED ||
                 keyCode == KeyEvent.KEYCODE_ENTER ||
                 keyCode == KeyEvent.KEYCODE_DEL ||
                 keyCode == KeyEvent.KEYCODE_TAB ||
                 (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
                 !mKeyListener.onKeyDown(v, mEditable, keyCode, event)) {
@@ -579,34 +579,34 @@ public class GeckoInputConnection
                 break;
         }
 
         if (isPreIme && mIMEState != IME_STATE_DISABLED &&
             (event.getMetaState() & KeyEvent.META_ALT_ON) != 0)
             // Let active IME process pre-IME key events
             return false;
 
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
 
         if (mIMEState == IME_STATE_DISABLED ||
             keyCode == KeyEvent.KEYCODE_ENTER ||
             keyCode == KeyEvent.KEYCODE_DEL ||
             (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
             !mKeyListener.onKeyUp(v, mEditable, keyCode, event))
             GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
         return true;
     }
 
     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
         GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
         return true;
     }
 
     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
         switch (keyCode) {
             case KeyEvent.KEYCODE_MENU:
                 InputMethodManager imm = (InputMethodManager)
                     v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                 imm.toggleSoftInputFromWindow(v.getWindowToken(),
                                               imm.SHOW_FORCED, 0);
                 return true;
             default:
@@ -616,17 +616,17 @@ public class GeckoInputConnection
     }
 
     public boolean isIMEEnabled() {
         // make sure this picks up PASSWORD and PLUGIN states as well
         return mIMEState != IME_STATE_DISABLED;
     }
 
     public void notifyIME(int type, int state) {
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
 
         if (v == null)
             return;
 
         switch (type) {
         case NOTIFY_IME_RESETINPUTSTATE:
             if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: reset");
 
@@ -660,33 +660,33 @@ public class GeckoInputConnection
             IMEStateUpdater.resetIME();
             break;
         }
     }
 
     public void notifyIMEEnabled(int state, String typeHint,
                                         String actionHint, boolean landscapeFS)
     {
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
 
         if (v == null)
             return;
 
         /* When IME is 'disabled', IME processing is disabled.
            In addition, the IME UI is hidden */
         mIMEState = state;
         mIMETypeHint = typeHint;
         mIMEActionHint = actionHint;
         mIMELandscapeFS = landscapeFS;
         IMEStateUpdater.enableIME();
     }
 
 
     public void notifyIMEChange(String text, int start, int end, int newEnd) {
-        View v = GeckoApp.mAppContext.getLayerController().getView();
+        View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
 
         if (v == null)
             return;
 
         InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
         if (imm == null)
             return;
 
@@ -735,17 +735,17 @@ public class GeckoInputConnection
         }
 
         public void run() {
             if (DEBUG) Log.d(LOGTAG, "IME: run()");
             synchronized(IMEStateUpdater.class) {
                 instance = null;
             }
 
-            View v = GeckoApp.mAppContext.getLayerController().getView();
+            View v = GeckoApp.mAppContext.getLayerController().getView().getAndroidView();
             if (DEBUG) Log.d(LOGTAG, "IME: v="+v);
 
             InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
             if (imm == null)
                 return;
 
             if (mReset)
                 imm.restartInput(v);
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -88,16 +88,17 @@ FENNEC_JAVA_FILES = \
   GlobalHistory.java \
   LinkPreference.java \
   ProfileMigrator.java \
   PromptService.java \
   SurfaceLockInfo.java \
   Tab.java \
   Tabs.java \
   TabsTray.java \
+  gfx/AbstractLayerView.java \
   gfx/BitmapUtils.java \
   gfx/BufferedCairoImage.java \
   gfx/CairoGLInfo.java \
   gfx/CairoImage.java \
   gfx/CairoUtils.java \
   gfx/FloatSize.java \
   gfx/GeckoSoftwareLayerClient.java \
   gfx/InputConnectionHandler.java \
@@ -115,16 +116,17 @@ FENNEC_JAVA_FILES = \
   gfx/RectUtils.java \
   gfx/ScrollbarLayer.java \
   gfx/SingleTileLayer.java \
   gfx/TextLayer.java \
   gfx/TextureReaper.java \
   gfx/TileLayer.java \
   gfx/ViewportMetrics.java \
   gfx/WidgetTileLayer.java \
+  gfx/layers/OGLSurfaceView.java \
   ui/Axis.java \
   ui/PanZoomController.java \
   ui/SubdocumentScrollHelper.java \
   GeckoNetworkManager.java \
   $(NULL)
 
 ifdef MOZ_WEBSMS_BACKEND
 FENNEC_JAVA_FILES += GeckoSmsManager.java
--- a/mobile/android/base/gfx/Layer.java
+++ b/mobile/android/base/gfx/Layer.java
@@ -48,17 +48,17 @@ import org.mozilla.gecko.FloatUtils;
 
 public abstract class Layer {
     private final ReentrantLock mTransactionLock;
     private boolean mInTransaction;
     private Point mOrigin;
     private Point mNewOrigin;
     private float mResolution;
     private float mNewResolution;
-    private LayerView mView;
+    private AbstractLayerView mView;
 
     public Layer() {
         mTransactionLock = new ReentrantLock();
         mOrigin = new Point(0, 0);
         mResolution = 1.0f;
     }
 
     /**
@@ -98,17 +98,17 @@ public abstract class Layer {
 
     /**
      * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer"
      * includes altering the underlying CairoImage in any way. Thus you must call this function
      * before modifying the byte buffer associated with this layer.
      *
      * This function may block, so you should never call this on the main UI thread.
      */
-    public void beginTransaction(LayerView aView) {
+    public void beginTransaction(AbstractLayerView aView) {
         if (mTransactionLock.isHeldByCurrentThread())
             throw new RuntimeException("Nested transactions are not supported");
         mTransactionLock.lock();
         mView = aView;
         mInTransaction = true;
         mNewResolution = mResolution;
     }
 
--- a/mobile/android/base/gfx/LayerClient.java
+++ b/mobile/android/base/gfx/LayerClient.java
@@ -49,21 +49,21 @@ public abstract class LayerClient {
 
     public LayerController getLayerController() { return mLayerController; }
     public void setLayerController(LayerController layerController) {
         mLayerController = layerController;
     }
 
     /**
      * A utility function for calling Layer.beginTransaction with the
-     * appropriate LayerView.
+     * appropriate AbstractLayerView.
      */
     public void beginTransaction(Layer aLayer) {
         if (mLayerController != null) {
-            LayerView view = mLayerController.getView();
+            AbstractLayerView view = mLayerController.getView();
             if (view != null) {
                 aLayer.beginTransaction(view);
                 return;
             }
         }
 
         aLayer.beginTransaction();
     }
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -36,17 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.LayerClient;
-import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.gfx.AbstractLayerView;
 import org.mozilla.gecko.ui.PanZoomController;
 import org.mozilla.gecko.GeckoApp;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -65,17 +65,17 @@ import java.lang.Math;
  * to a higher-level view.
  *
  * Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
  */
 public class LayerController {
     private static final String LOGTAG = "GeckoLayerController";
 
     private Layer mRootLayer;                   /* The root layer. */
-    private LayerView mView;                    /* The main rendering view. */
+    private AbstractLayerView mView;            /* The main rendering view. */
     private Context mContext;                   /* The current context. */
     private ViewportMetrics mViewportMetrics;   /* The current viewport metrics. */
 
     private PanZoomController mPanZoomController;
     /*
      * The panning and zooming controller, which interprets pan and zoom gestures for us and
      * updates our visible rect appropriately.
      */
@@ -113,17 +113,17 @@ public class LayerController {
     }
 
     public void setForceRedraw() {
         mForceRedraw = true;
     }
 
     public LayerClient getLayerClient()           { return mLayerClient; }
     public Layer getRoot()                        { return mRootLayer; }
-    public LayerView getView()                    { return mView; }
+    public AbstractLayerView getView()            { return mView; }
     public Context getContext()                   { return mContext; }
     public ViewportMetrics getViewportMetrics()   { return mViewportMetrics; }
 
     public RectF getViewport() {
         return mViewportMetrics.getViewport();
     }
 
     public FloatSize getViewportSize() {
@@ -330,13 +330,13 @@ public class LayerController {
     /*
      * Gesture detection. This is handled only at a high level in this class; we dispatch to the
      * pan/zoom controller to do the dirty work.
      */
     public boolean onTouchEvent(MotionEvent event) {
         if (mPanZoomController.onTouchEvent(event))
             return true;
         if (mOnTouchListener != null)
-            return mOnTouchListener.onTouch(mView, event);
+            return mOnTouchListener.onTouch(mView.getAndroidView(), event);
         return false;
     }
 }
 
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -37,17 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.gfx.BufferedCairoImage;
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.Layer.RenderContext;
 import org.mozilla.gecko.gfx.LayerController;
-import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.gfx.AbstractLayerView;
 import org.mozilla.gecko.gfx.NinePatchTileLayer;
 import org.mozilla.gecko.gfx.SingleTileLayer;
 import org.mozilla.gecko.gfx.TextureReaper;
 import org.mozilla.gecko.gfx.TextLayer;
 import org.mozilla.gecko.gfx.TileLayer;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.Point;
@@ -77,32 +77,32 @@ public class LayerRenderer implements GL
      * The amount of time a frame is allowed to take to render before we declare it a dropped
      * frame.
      */
     private static final int MAX_FRAME_TIME = 16;   /* 1000 ms / 60 FPS */
 
     private static final int FRAME_RATE_METER_WIDTH = 64;
     private static final int FRAME_RATE_METER_HEIGHT = 32;
 
-    private final LayerView mView;
+    private final AbstractLayerView mView;
     private final SingleTileLayer mCheckerboardLayer;
     private final NinePatchTileLayer mShadowLayer;
     private final TextLayer mFrameRateLayer;
     private final ScrollbarLayer mHorizScrollLayer;
     private final ScrollbarLayer mVertScrollLayer;
     private final FadeRunnable mFadeRunnable;
     private RenderContext mLastPageContext;
     private int mMaxTextureSize;
 
     // Dropped frames display
     private int[] mFrameTimings;
     private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
     private boolean mShowFrameRate;
 
-    public LayerRenderer(LayerView view) {
+    public LayerRenderer(AbstractLayerView view) {
         mView = view;
 
         LayerController controller = view.getController();
 
         CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern());
         mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage);
 
         CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern());
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -35,32 +35,35 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.gfx.FloatSize;
 import org.mozilla.gecko.gfx.InputConnectionHandler;
 import org.mozilla.gecko.gfx.LayerController;
+import org.mozilla.gecko.GeckoInputConnection;
 import android.content.Context;
 import android.opengl.GLSurfaceView;
+import android.view.View;
 import android.view.GestureDetector;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.ScaleGestureDetector;
+import android.widget.RelativeLayout;
 
 /**
  * A view rendered by the layer compositor.
  *
  * This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
  * mediator between the LayerRenderer and the LayerController.
  */
-public class LayerView extends GLSurfaceView {
+public class LayerView extends GLSurfaceView implements AbstractLayerView {
     private Context mContext;
     private LayerController mController;
     private InputConnectionHandler mInputConnectionHandler;
     private LayerRenderer mRenderer;
     private GestureDetector mGestureDetector;
     private ScaleGestureDetector mScaleGestureDetector;
     private long mRenderTime;
     private boolean mRenderTimeReset;
@@ -94,18 +97,20 @@ public class LayerView extends GLSurface
 
     public LayerController getController() { return mController; }
 
     /** The LayerRenderer calls this to indicate that the window has changed size. */
     public void setViewportSize(IntSize size) {
         mController.setViewportSize(new FloatSize(size));
     }
 
-    public void setInputConnectionHandler(InputConnectionHandler handler) {
-        mInputConnectionHandler = handler;
+    public GeckoInputConnection setInputConnectionHandler() {
+        mInputConnectionHandler = GeckoInputConnection.create(this);
+        setInputConnectionHandler(mInputConnectionHandler);
+        return mInputConnectionHandler;
     }
 
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
         if (mInputConnectionHandler != null)
             return mInputConnectionHandler.onCreateInputConnection(outAttrs);
         return null;
     }
@@ -166,10 +171,14 @@ public class LayerView extends GLSurface
             mRenderTimeReset = false;
             return System.nanoTime() - mRenderTime;
         }
     }
 
     public int getMaxTextureSize() {
         return mRenderer.getMaxTextureSize();
     }
+
+    public View getAndroidView() {
+        return this;
+    }
 }
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -182,16 +182,18 @@ AndroidBridge::Init(JNIEnv *jEnv,
 
     jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext"));
     jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10"));
     jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"));
     jEGLContextImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLContextImpl"));
     jEGLConfigImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLConfigImpl"));
     jEGLDisplayImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLDisplayImpl"));
 
+    jOGLSurfaceView = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/gfx/layers/OGLSurfaceView"));
+
     InitAndroidJavaWrappers(jEnv);
 
     // jEnv should NOT be cached here by anything -- the jEnv here
     // is not valid for the real gecko main thread, which is set
     // at SetMainThread time.
 
     return true;
 }
@@ -870,17 +872,17 @@ AndroidBridge::ShowInputMethodPicker()
     ALOG_BRIDGE("AndroidBridge::ShowInputMethodPicker");
     mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jShowInputMethodPicker);
 }
 
 void *
 AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview)
 {
     ALOG_BRIDGE("AndroidBridge::CallEglCreateWindowSurface");
-    AutoLocalJNIFrame jniFrame;
+    AutoLocalJNIFrame jniFrame(GetJNIForThread());
 
     /*
      * This is basically:
      *
      *    s = EGLContext.getEGL().eglCreateWindowSurface(new EGLDisplayImpl(dpy),
      *                                                   new EGLConfigImpl(config),
      *                                                   view.getHolder(), null);
      *    return s.mEGLSurface;
@@ -911,16 +913,27 @@ AndroidBridge::CallEglCreateWindowSurfac
 
     jfieldID sfield = mJNIEnv->GetFieldID(jEGLSurfaceImplClass, "mEGLSurface", "I");
 
     jint realSurface = mJNIEnv->GetIntField(surf, sfield);
 
     return (void*) realSurface;
 }
 
+void
+AndroidBridge::RegisterCompositor()
+{
+    ALOG_BRIDGE("AndroidBridge::RegisterCompositor");
+    AutoLocalJNIFrame jniFrame(GetJNIForThread());
+
+    jmethodID registerCompositor = GetJNIForThread()->GetStaticMethodID(jOGLSurfaceView, "registerCompositor", "()V");
+
+    GetJNIForThread()->CallStaticVoidMethod(jOGLSurfaceView, registerCompositor);
+}
+
 bool
 AndroidBridge::GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt)
 {
     ALOG_BRIDGE("AndroidBridge::GetStaticIntField %s", fieldName);
     AutoLocalJNIFrame jniFrame(3);
     jclass cls = mJNIEnv->FindClass(className);
     if (!cls)
         return false;
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -280,16 +280,19 @@ public:
 
         int mEntries;
         JNIEnv* mJNIEnv;
     };
 
     /* See GLHelpers.java as to why this is needed */
     void *CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView& surfaceView);
 
+    // Switch Java to composite with the Gecko Compositor thread
+    void RegisterCompositor();
+
     bool GetStaticStringField(const char *classID, const char *field, nsAString &result);
 
     bool GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt);
 
     void SetKeepScreenOn(bool on);
 
     void ScanMedia(const nsAString& aFile, const nsACString& aMimeType);
 
@@ -457,16 +460,18 @@ protected:
     // stuff we need for CallEglCreateWindowSurface
     jclass jEGLSurfaceImplClass;
     jclass jEGLContextImplClass;
     jclass jEGLConfigImplClass;
     jclass jEGLDisplayImplClass;
     jclass jEGLContextClass;
     jclass jEGL10Class;
 
+    jclass jOGLSurfaceView;
+
     // calls we've dlopened from libjnigraphics.so
     int (* AndroidBitmap_getInfo)(JNIEnv *env, jobject bitmap, void *info);
     int (* AndroidBitmap_lockPixels)(JNIEnv *env, jobject bitmap, void **buffer);
     int (* AndroidBitmap_unlockPixels)(JNIEnv *env, jobject bitmap);
 
     void* (*ANativeWindow_fromSurface)(JNIEnv *env, jobject surface);
     void (*ANativeWindow_release)(void *window);
     int (*ANativeWindow_setBuffersGeometry)(void *window, int width, int height, int format);
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -103,16 +103,18 @@ extern "C" {
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyListCreated(JNIEnv* jenv, jclass, jint, jint, jstring, jstring, jstring, jlong, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGotNextMessage(JNIEnv* jenv, jclass, jint, jstring, jstring, jstring, jlong, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyReadingMessageListFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
 
 #ifdef MOZ_JAVA_COMPOSITOR
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass);
     NS_EXPORT bool JNICALL Java_org_mozilla_gecko_GeckoAppShell_testDirectTexture(JNIEnv* jenv, jclass);
 #endif
+
+    NS_EXPORT void JNICALL Java_org_mozilla_gfx_layers_OGLSurfaceView_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
 }
 
 
 /*
  * Incoming JNI methods
  */
 
 NS_EXPORT void JNICALL
@@ -139,16 +141,22 @@ Java_org_mozilla_gecko_GeckoAppShell_pro
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject obj)
 {
     AndroidBridge::Bridge()->SetSurfaceView(jenv->NewGlobalRef(obj));
 }
 
 NS_EXPORT void JNICALL
+Java_org_mozilla_gfx_layers_OGLSurfaceView_setSurfaceView(JNIEnv *jenv, jclass, jobject obj)
+{
+    AndroidBridge::Bridge()->SetSurfaceView(jenv->NewGlobalRef(obj));
+}
+
+NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject obj)
 {
     AndroidBridge::Bridge()->SetSoftwareLayerClient(jenv->NewGlobalRef(obj));
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *jenv, jclass jc)
 {
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -194,16 +194,24 @@ AndroidGeckoSurfaceView::InitGeckoSurfac
     jBeginDrawingMethod = getMethod("beginDrawing", "()I");
     jGetSoftwareDrawBitmapMethod = getMethod("getSoftwareDrawBitmap", "()Landroid/graphics/Bitmap;");
     jGetSoftwareDrawBufferMethod = getMethod("getSoftwareDrawBuffer", "()Ljava/nio/ByteBuffer;");
     jEndDrawingMethod = getMethod("endDrawing", "()V");
     jDraw2DBitmapMethod = getMethod("draw2D", "(Landroid/graphics/Bitmap;II)V");
     jDraw2DBufferMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;I)V");
     jGetSurfaceMethod = getMethod("getSurface", "()Landroid/view/Surface;");
     jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
+#else
+    initInit();
+
+    jGeckoSurfaceViewClass = getClassGlobalRef("org/mozilla/gecko/gfx/layers/OGLSurfaceView");
+
+    jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
+
+
 #endif
 }
 
 void
 AndroidLocation::InitLocationClass(JNIEnv *jEnv)
 {
     initInit();
 
@@ -651,17 +659,17 @@ jobject
 AndroidGeckoSurfaceView::GetSurface()
 {
     return JNI()->CallObjectMethod(wrapped_obj, jGetSurfaceMethod);
 }
 
 jobject
 AndroidGeckoSurfaceView::GetSurfaceHolder()
 {
-    return JNI()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
+    return GetJNIForThread()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
 }
 
 void
 AndroidRect::Init(JNIEnv *jenv, jobject jobj)
 {
     NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
 
     wrapped_obj = jobj;
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -776,35 +776,44 @@ nsWindow::GetLayerManager(PLayersChild*,
     if (!mUseAcceleratedRendering ||
         sFailedToCreateGLContext)
     {
         printf_stderr(" -- creating basic, not accelerated\n");
         mLayerManager = CreateBasicLayerManager();
         return mLayerManager;
     }
 
-    if (!sGLContext) {
-        // the window we give doesn't matter here
-        sGLContext = mozilla::gl::GLContextProvider::CreateForWindow(this);
+    bool useCompositor =
+        Preferences::GetBool("layers.offmainthreadcomposition.enabled", false);
+
+    if (useCompositor) {
+        CreateCompositor();
     }
 
-    if (sGLContext) {
-        nsRefPtr<mozilla::layers::LayerManagerOGL> layerManager =
-            new mozilla::layers::LayerManagerOGL(this);
+    if (!mLayerManager) {
+        if (!sGLContext) {
+            // the window we give doesn't matter here
+            sGLContext = mozilla::gl::GLContextProvider::CreateForWindow(this);
+        }
+
+        if (sGLContext) {
+                nsRefPtr<mozilla::layers::LayerManagerOGL> layerManager =
+                        new mozilla::layers::LayerManagerOGL(this);
 
-        if (layerManager && layerManager->Initialize(sGLContext))
-            mLayerManager = layerManager;
-        sValidSurface = true;
-    }
+                if (layerManager && layerManager->Initialize(sGLContext))
+                        mLayerManager = layerManager;
+                sValidSurface = true;
+        }
 
-    if (!sGLContext || !mLayerManager) {
-        sGLContext = nsnull;
-        sFailedToCreateGLContext = true;
+        if (!sGLContext || !mLayerManager) {
+                sGLContext = nsnull;
+                sFailedToCreateGLContext = true;
 
-        mLayerManager = CreateBasicLayerManager();
+                mLayerManager = CreateBasicLayerManager();
+        }
     }
 
     return mLayerManager;
 }
 
 gfxASurface*
 nsWindow::GetThebesSurface()
 {