Eliminate coupling between the software layer client and the rest of Fennec
authorPatrick Walton <pwalton@mozilla.com>
Thu, 02 Feb 2012 23:30:41 -0800
changeset 90877 ba16d096e3d87b774c0834914c003eaa4cbfd261
parent 90876 5e47633414952424bf3fe0eeac621bea5ff587ce
child 90878 d47c32d6795d85dcfa04bd74eea8b4017f94bb22
push idunknown
push userunknown
push dateunknown
milestone12.0a1
Eliminate coupling between the software layer client and the rest of Fennec
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/gfx/GeckoLayerClient.java
mobile/android/base/gfx/GeckoSoftwareLayerClient.java
mozglue/android/APKOpen.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsWindow.cpp
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -36,16 +36,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.FloatSize;
+import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.PlaceholderLayerClient;
 import org.mozilla.gecko.gfx.RectUtils;
 import org.mozilla.gecko.gfx.ViewportMetrics;
 import org.mozilla.gecko.Tab.HistoryEntry;
 
@@ -134,17 +135,17 @@ abstract public class GeckoApp
     public static DoorHangerPopup mDoorHangerPopup;
     public static AutoCompletePopup mAutoCompletePopup;
     public Favicons mFavicons;
 
     private Geocoder mGeocoder;
     private Address  mLastGeoAddress;
     private static LayerController mLayerController;
     private static PlaceholderLayerClient mPlaceholderLayerClient;
-    private static GeckoSoftwareLayerClient mSoftwareLayerClient;
+    private static GeckoLayerClient mLayerClient;
     private AboutHomeContent mAboutHomeContent;
     private static AbsoluteLayout mPluginContainer;
 
     public String mLastTitle;
     public String mLastViewport;
     public byte[] mLastScreen;
     public int mOwnActivityDepth = 0;
     private boolean mRestoreSession = false;
@@ -560,51 +561,50 @@ abstract public class GeckoApp
 
     public class SessionSnapshotRunnable implements Runnable {
         Tab mThumbnailTab;
         SessionSnapshotRunnable(Tab thumbnailTab) {
             mThumbnailTab = thumbnailTab;
         }
 
         public void run() {
-            synchronized (mSoftwareLayerClient) {
+            synchronized (mLayerClient) {
                 Tab tab = Tabs.getInstance().getSelectedTab();
                 if (tab == null)
                     return;
 
                 HistoryEntry lastHistoryEntry = tab.getLastHistoryEntry();
                 if (lastHistoryEntry == null)
                     return;
 
-                if (getLayerController().getLayerClient() != mSoftwareLayerClient)
+                if (getLayerController().getLayerClient() != mLayerClient)
                     return;
 
-                ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics();
+                ViewportMetrics viewportMetrics = mLayerClient.getGeckoViewportMetrics();
                 if (viewportMetrics != null)
                     mLastViewport = viewportMetrics.toJSON();
 
                 mLastTitle = lastHistoryEntry.mTitle;
                 getAndProcessThumbnailForTab(tab, true);
             }
         }
     }
 
     void getAndProcessThumbnailForTab(final Tab tab, boolean forceBigSceenshot) {
         boolean isSelectedTab = Tabs.getInstance().isSelectedTab(tab);
-        final Bitmap bitmap = isSelectedTab ?
-            mSoftwareLayerClient.getBitmap() : null;
+        final Bitmap bitmap = isSelectedTab ? mLayerClient.getBitmap() : null;
         
         if (bitmap != null) {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
             processThumbnail(tab, bitmap, bos.toByteArray());
         } else {
             mLastScreen = null;
-            int sw = forceBigSceenshot ? mSoftwareLayerClient.getWidth() : tab.getMinScreenshotWidth();
-            int sh = forceBigSceenshot ? mSoftwareLayerClient.getHeight(): tab.getMinScreenshotHeight();
+            int sw = forceBigSceenshot ? mLayerClient.getWidth() : tab.getMinScreenshotWidth();
+            int sh = forceBigSceenshot ? mLayerClient.getHeight(): tab.getMinScreenshotHeight();
             int dw = forceBigSceenshot ? sw : tab.getThumbnailWidth();
             int dh = forceBigSceenshot ? sh : tab.getThumbnailHeight();
             try {
                 JSONObject message = new JSONObject();
                 message.put("tabID", tab.getId());
 
                 JSONObject source = new JSONObject();
                 source.put("width", sw);
@@ -1587,17 +1587,17 @@ abstract public class GeckoApp
             cameraView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
         }
 
         if (mLayerController == null) {
             /*
              * Create a layer client so that Gecko will have a buffer to draw into, but don't hook
              * it up to the layer controller yet.
              */
-            mSoftwareLayerClient = new GeckoSoftwareLayerClient(this);
+            mLayerClient = new GeckoSoftwareLayerClient(this);
 
             /*
              * Hook a placeholder layer client up to the layer controller so that the user can pan
              * and zoom a cached screenshot of the previous page. This call will return null if
              * there is no cached screenshot; in that case, we have no choice but to display a
              * checkerboard.
              *
              * TODO: Fall back to a built-in screenshot of the Fennec Start page for a nice first-
@@ -2442,17 +2442,17 @@ abstract public class GeckoApp
             args.put("parentId", Tabs.getInstance().getSelectedTab().getId());
         } catch (Exception e) {
             Log.e(LOGTAG, "error building JSON arguments");
         }
         Log.i(LOGTAG, "Sending message to Gecko: " + SystemClock.uptimeMillis() + " - Tab:Add");
         GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Add", args.toString()));
     }
 
-    public GeckoSoftwareLayerClient getSoftwareLayerClient() { return mSoftwareLayerClient; }
+    public GeckoLayerClient getLayerClient() { return mLayerClient; }
     public LayerController getLayerController() { return mLayerController; }
 
     // accelerometer
     public void onAccuracyChanged(Sensor sensor, int accuracy)
     {
     }
 
     public void onSensorChanged(SensorEvent event)
@@ -2521,17 +2521,17 @@ abstract public class GeckoApp
     }
 
 
     private void connectGeckoLayerClient() {
         if (mPlaceholderLayerClient != null)
             mPlaceholderLayerClient.destroy();
 
         LayerController layerController = getLayerController();
-        layerController.setLayerClient(mSoftwareLayerClient);
+        layerController.setLayerClient(mLayerClient);
     }
 
 }
 
 class PluginLayoutParams extends AbsoluteLayout.LayoutParams
 {
     private static final int MAX_DIMENSION = 2048;
     private static final String LOGTAG = "GeckoApp.PluginLayoutParams";
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -34,17 +34,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.BitmapUtils;
-import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
+import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.AbstractLayerView;
 
 import java.io.*;
 import java.lang.reflect.*;
 import java.nio.*;
 import java.nio.channels.*;
 import java.text.*;
@@ -119,17 +119,17 @@ public class GeckoAppShell
     /* The Android-side API: API methods that Android calls */
 
     // Initialization methods
     public static native void nativeInit();
     public static native void nativeRun(String args);
 
     // helper methods
     //    public static native void setSurfaceView(GeckoSurfaceView sv);
-    public static native void setSoftwareLayerClient(GeckoSoftwareLayerClient client);
+    public static native void setLayerClient(GeckoLayerClient client, int type);
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadGeckoLibsNative(String apkName);
     public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
@@ -462,19 +462,20 @@ public class GeckoAppShell
 
     public static void runGecko(String apkPath, String args, String url, boolean restoreSession) {
         // run gecko -- it will spawn its own thread
         GeckoAppShell.nativeInit();
 
         Log.i(LOGTAG, "post native init");
 
         // Tell Gecko where the target byte buffer is for rendering
-        GeckoAppShell.setSoftwareLayerClient(GeckoApp.mAppContext.getSoftwareLayerClient());
+        GeckoAppShell.setLayerClient(GeckoApp.mAppContext.getLayerClient(),
+                                     GeckoApp.mAppContext.getLayerClient().getType());
 
-        Log.i(LOGTAG, "setSoftwareLayerClient called");
+        Log.i(LOGTAG, "setLayerClient called");
 
         // First argument is the .apk path
         String combinedArgs = apkPath + " -greomni " + apkPath;
         if (args != null)
             combinedArgs += " " + args;
         if (url != null)
             combinedArgs += " -remote " + url;
         if (restoreSession)
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -41,28 +41,33 @@ package org.mozilla.gecko.gfx;
 import org.mozilla.gecko.FloatUtils;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoEventListener;
 import org.json.JSONException;
 import org.json.JSONObject;
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public abstract class GeckoLayerClient extends LayerClient implements GeckoEventListener {
     private static final String LOGTAG = "GeckoLayerClient";
 
+    public static final int LAYER_CLIENT_TYPE_NONE = 0;
+    public static final int LAYER_CLIENT_TYPE_SOFTWARE = 1;
+    public static final int LAYER_CLIENT_TYPE_GL = 2;
+
     protected IntSize mScreenSize;
     protected IntSize mBufferSize;
 
     protected Layer mTileLayer;
 
     /* The viewport that Gecko is currently displaying. */
     protected ViewportMetrics mGeckoViewport;
 
@@ -86,16 +91,18 @@ public abstract class GeckoLayerClient e
     /* Used by robocop for testing purposes */
     private DrawListener mDrawListener;
 
     protected abstract boolean handleDirectTextureChange(boolean hasDirectTexture);
     protected abstract boolean shouldDrawProceed(int tileWidth, int tileHeight);
     protected abstract void updateLayerAfterDraw(Rect updatedRect);
     protected abstract IntSize getBufferSize();
     protected abstract IntSize getTileSize();
+    public abstract Bitmap getBitmap();
+    public abstract int getType();
 
     public GeckoLayerClient(Context context) {
         mScreenSize = new IntSize(0, 0);
         mBufferSize = new IntSize(0, 0);
     }
 
     /** Attaches the root layer to the layer controller so that Gecko appears. */
     @Override
@@ -302,16 +309,31 @@ public abstract class GeckoLayerClient e
 
     @Override
     public void geometryChanged() {
         /* Let Gecko know if the screensize has changed */
         sendResizeEventIfNecessary();
         render();
     }
 
+    public int getWidth() {
+        return mBufferSize.width;
+    }
+
+    public int getHeight() {
+        return mBufferSize.height;
+    }
+
+    public ViewportMetrics getGeckoViewportMetrics() {
+        // Return a copy, as we modify this inside the Gecko thread
+        if (mGeckoViewport != null)
+            return new ViewportMetrics(mGeckoViewport);
+        return null;
+    }
+
     private void sendResizeEventIfNecessary() {
         sendResizeEventIfNecessary(false);
     }
 
     /** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */
     public void setDrawListener(DrawListener listener) {
         mDrawListener = listener;
     }
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -90,24 +90,16 @@ public class GeckoSoftwareLayerClient ex
             public ByteBuffer getBuffer() { return mBuffer; }
             @Override
             public IntSize getSize() { return mBufferSize; }
             @Override
             public int getFormat() { return mFormat; }
         };
     }
 
-    public int getWidth() {
-        return mBufferSize.width;
-    }
-
-    public int getHeight() {
-        return mBufferSize.height;
-    }
-
     protected void finalize() throws Throwable {
         try {
             if (mBuffer != null)
                 GeckoAppShell.freeDirectBuffer(mBuffer);
             mBuffer = null;
         } finally {
             super.finalize();
         }
@@ -224,24 +216,17 @@ public class GeckoSoftwareLayerClient ex
             return;
         }
 
         updatedRect.offset(mRenderOffset.x, mRenderOffset.y);
         ((MultiTileLayer)mTileLayer).invalidate(updatedRect);
         ((MultiTileLayer)mTileLayer).setRenderOffset(mRenderOffset);
     }
 
-    public ViewportMetrics getGeckoViewportMetrics() {
-        // Return a copy, as we modify this inside the Gecko thread
-        if (mGeckoViewport != null)
-            return new ViewportMetrics(mGeckoViewport);
-        return null;
-    }
-
-    public void copyPixelsFromMultiTileLayer(Bitmap target) {
+    private void copyPixelsFromMultiTileLayer(Bitmap target) {
         Canvas c = new Canvas(target);
         ByteBuffer tileBuffer = mBuffer.slice();
         int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
 
         for (int y = 0; y <= mBufferSize.height; y += TILE_SIZE.height) {
             for (int x = 0; x <= mBufferSize.width; x += TILE_SIZE.width) {
                 // Create a Bitmap from this tile
                 Bitmap tile = Bitmap.createBitmap(TILE_SIZE.width, TILE_SIZE.height,
@@ -254,16 +239,17 @@ public class GeckoSoftwareLayerClient ex
 
                 // Progress the buffer to the next tile
                 tileBuffer.position(TILE_SIZE.getArea() * bpp);
                 tileBuffer = tileBuffer.slice();
             }
         }
     }
 
+    @Override
     public Bitmap getBitmap() {
         if (mTileLayer == null)
             return null;
 
         // Begin a tile transaction, otherwise the buffer can be destroyed while
         // we're reading from it.
         beginTransaction(mTileLayer);
         try {
@@ -303,16 +289,21 @@ public class GeckoSoftwareLayerClient ex
      * Gecko calls this function to signal that it is done with the back buffer. After this call,
      * it is forbidden for Gecko to touch the buffer.
      */
     public void unlockBuffer() {
         /* no-op */
     }
 
     @Override
+    public int getType() {
+        return LAYER_CLIENT_TYPE_SOFTWARE;
+    }
+
+    @Override
     protected IntSize getBufferSize() {
         // Round up depending on layer implementation to remove texture wastage
         if (!mHasDirectTexture) {
             // Round to the next multiple of the tile size
             return new IntSize(((mScreenSize.width + LayerController.MIN_BUFFER.width - 1) /
                                     TILE_SIZE.width + 1) * TILE_SIZE.width,
                                ((mScreenSize.height + LayerController.MIN_BUFFER.height - 1) /
                                     TILE_SIZE.height + 1) * TILE_SIZE.height);
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -279,17 +279,17 @@ Java_org_mozilla_gecko_GeckoAppShell_ ##
   return f_ ## name(jenv, jc, one, two, three, four, five, six, seven, eight); \
 }
 
 SHELL_WRAPPER0(nativeInit)
 SHELL_WRAPPER1(nativeRun, jstring)
 SHELL_WRAPPER1(notifyGeckoOfEvent, jobject)
 SHELL_WRAPPER0(processNextNativeEvent)
 SHELL_WRAPPER1(setSurfaceView, jobject)
-SHELL_WRAPPER1(setSoftwareLayerClient, jobject)
+SHELL_WRAPPER2(setLayerClient, jobject, jint)
 SHELL_WRAPPER0(onResume)
 SHELL_WRAPPER0(onLowMemory)
 SHELL_WRAPPER3(callObserver, jstring, jstring, jstring)
 SHELL_WRAPPER1(removeObserver, jstring)
 SHELL_WRAPPER1(onChangeNetworkLinkStatus, jstring)
 SHELL_WRAPPER1(reportJavaCrash, jstring)
 SHELL_WRAPPER0(executeNextRunnable)
 SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
@@ -693,17 +693,17 @@ loadGeckoLibs(const char *apkName)
     __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!");
 
 #define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(xul_handle, "Java_org_mozilla_gecko_GeckoAppShell_" #name)
   GETFUNC(nativeInit);
   GETFUNC(nativeRun);
   GETFUNC(notifyGeckoOfEvent);
   GETFUNC(processNextNativeEvent);
   GETFUNC(setSurfaceView);
-  GETFUNC(setSoftwareLayerClient);
+  GETFUNC(setLayerClient);
   GETFUNC(onResume);
   GETFUNC(onLowMemory);
   GETFUNC(callObserver);
   GETFUNC(removeObserver);
   GETFUNC(onChangeNetworkLinkStatus);
   GETFUNC(reportJavaCrash);
   GETFUNC(executeNextRunnable);
   GETFUNC(cameraCallbackBridge);
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -951,19 +951,39 @@ AndroidBridge::GetShowPasswordSetting()
 
 void
 AndroidBridge::SetSurfaceView(jobject obj)
 {
     mSurfaceView.Init(obj);
 }
 
 void
-AndroidBridge::SetSoftwareLayerClient(jobject obj)
+AndroidBridge::SetLayerClient(jobject obj, jint type)
 {
-    mSoftwareLayerClient.Init(obj);
+    switch (type) {
+        case LAYER_CLIENT_TYPE_SOFTWARE: {
+            AndroidGeckoSoftwareLayerClient *client = new AndroidGeckoSoftwareLayerClient();
+            client->Init(obj);
+            mLayerClient = client;
+            break;
+        }
+        case LAYER_CLIENT_TYPE_GL: {
+            // TODO: Implement.
+#if 0
+            AndroidGeckoGLLayerClient *client = new AndroidGeckoGLLayerClient();
+            client->Init(obj);
+            mLayerClient = client;
+#endif
+            break;
+        }
+        default:
+            NS_ASSERTION(0, "Unknown layer client type!");
+    }
+
+    mLayerClientType = type;
 }
 
 void
 AndroidBridge::ShowInputMethodPicker()
 {
     ALOG_BRIDGE("AndroidBridge::ShowInputMethodPicker");
 
     JNIEnv *env = GetJNIEnv();
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -105,16 +105,22 @@ class AndroidBridge
 public:
     enum {
         NOTIFY_IME_RESETINPUTSTATE = 0,
         NOTIFY_IME_SETOPENSTATE = 1,
         NOTIFY_IME_CANCELCOMPOSITION = 2,
         NOTIFY_IME_FOCUSCHANGE = 3
     };
 
+    enum {
+        LAYER_CLIENT_TYPE_NONE = 0,
+        LAYER_CLIENT_TYPE_SOFTWARE = 1,     // AndroidGeckoSoftwareLayerClient
+        LAYER_CLIENT_TYPE_GL = 2            // AndroidGeckoGLLayerClient
+    };
+
     static AndroidBridge *ConstructBridge(JNIEnv *jEnv,
                                           jclass jGeckoAppShellClass);
 
     static AndroidBridge *Bridge() {
         return sBridge;
     }
 
     static JavaVM *GetVM() {
@@ -163,18 +169,19 @@ public:
     void EnableLocation(bool aEnable);
 
     void ReturnIMEQueryResult(const PRUnichar *aResult, PRUint32 aLen, int aSelStart, int aSelLen);
 
     void NotifyXreExit();
 
     void ScheduleRestart();
 
-    void SetSoftwareLayerClient(jobject jobj);
-    AndroidGeckoSoftwareLayerClient &GetSoftwareLayerClient() { return mSoftwareLayerClient; }
+    void SetLayerClient(jobject jobj, jint type);
+    int GetLayerClientType() const { return mLayerClientType; }
+    AndroidGeckoLayerClient &GetLayerClient() { return *mLayerClient; }
 
     void SetSurfaceView(jobject jobj);
     AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
 
     bool GetHandlersForURL(const char *aURL, 
                              nsIMutableArray* handlersArray = nsnull,
                              nsIHandlerApp **aDefaultApp = nsnull,
                              const nsAString& aAction = EmptyString());
@@ -391,22 +398,24 @@ protected:
     JavaVM *mJavaVM;
 
     // the JNIEnv for the main thread
     JNIEnv *mJNIEnv;
     void *mThread;
 
     // the GeckoSurfaceView
     AndroidGeckoSurfaceView mSurfaceView;
-    AndroidGeckoSoftwareLayerClient mSoftwareLayerClient;
+
+    AndroidGeckoLayerClient *mLayerClient;
+    int mLayerClientType;
 
     // the GeckoAppShell java class
     jclass mGeckoAppShellClass;
 
-    AndroidBridge() { }
+    AndroidBridge() : mLayerClient(NULL), mLayerClientType(0) { }
     bool Init(JNIEnv *jEnv, jclass jGeckoApp);
 
     bool mOpenedGraphicsLibraries;
     void OpenGraphicsLibraries();
 
     bool mHasNativeBitmapAccess;
     bool mHasNativeWindowAccess;
 
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -74,17 +74,17 @@ using namespace mozilla;
 using namespace mozilla::dom::sms;
 
 /* Forward declare all the JNI methods as extern "C" */
 
 extern "C" {
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *, jclass);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *, jclass, jobject event);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *, jclass);
-    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject sv);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject sv, jint type);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *, jclass);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *, jclass);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_callObserver(JNIEnv *, jclass, jstring observerKey, jstring topic, jstring data);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_removeObserver(JNIEnv *jenv, jclass, jstring jObserverKey);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *, jclass, jstring status);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *, jclass, jstring stack);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_executeNextRunnable(JNIEnv *, jclass);
@@ -139,19 +139,19 @@ 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_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject obj)
+Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject obj, jint type)
 {
-    AndroidBridge::Bridge()->SetSoftwareLayerClient(jenv->NewGlobalRef(obj));
+    AndroidBridge::Bridge()->SetLayerClient(jenv->NewGlobalRef(obj), type);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *jenv, jclass jc)
 {
     if (nsAppShell::gAppShell) {
         nsAppShell::gAppShell->NotifyObservers(nsnull,
                                                "memory-pressure",
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -188,17 +188,16 @@ private:
     static jclass jGeckoSoftwareLayerClientClass;
     static jmethodID jLockBufferMethod;
     static jmethodID jUnlockBufferMethod;
 
 protected:
     static jmethodID jGetRenderOffsetMethod;
 };
 
-
 class AndroidGeckoSurfaceView : public WrappedJavaObject
 {
 public:
     static void InitGeckoSurfaceViewClass(JNIEnv *jEnv);
 
     AndroidGeckoSurfaceView() { }
     AndroidGeckoSurfaceView(jobject jobj) {
         Init(jobj);
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1258,50 +1258,51 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
 
     nsEventStatus status;
     status = DispatchEvent(&event);
 
     return;
     // END HACK: gl layers
 #endif
 
-    AndroidGeckoSoftwareLayerClient &client =
-        AndroidBridge::Bridge()->GetSoftwareLayerClient();
+    AndroidGeckoLayerClient &client = AndroidBridge::Bridge()->GetLayerClient();
     if (!client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height,
                              gAndroidTileSize.width, gAndroidTileSize.height,
                              metadata, HasDirectTexture())) {
         return;
     }
 
     nsIntRect dirtyRect = ae->Rect().Intersect(nsIntRect(0, 0, gAndroidBounds.width, gAndroidBounds.height));
 
+    int layerClientType = AndroidBridge::Bridge()->GetLayerClientType();
+
     unsigned char *bits = NULL;
     if (HasDirectTexture()) {
       if (sDirectTexture->Width() != gAndroidBounds.width ||
           sDirectTexture->Height() != gAndroidBounds.height) {
         sDirectTexture->Reallocate(gAndroidBounds.width, gAndroidBounds.height);
       }
 
       sDirectTexture->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, dirtyRect, &bits);
-    } else {
-      bits = client.LockBufferBits();
+    } else if (layerClientType == AndroidBridge::LAYER_CLIENT_TYPE_SOFTWARE) {
+        bits = ((AndroidGeckoSoftwareLayerClient &)client).LockBufferBits();
     }
 
     if (!bits) {
         ALOG("### Failed to lock buffer");
     } else if (targetSurface->CairoStatus()) {
         ALOG("### Failed to create a valid surface from the bitmap");
     } else {
         DrawTo(targetSurface, dirtyRect);
     }
 
     if (HasDirectTexture()) {
         sDirectTexture->Unlock();
-    } else {
-        client.UnlockBuffer();
+    } else if (layerClientType == AndroidBridge::LAYER_CLIENT_TYPE_SOFTWARE) {
+        ((AndroidGeckoSoftwareLayerClient &)client).UnlockBuffer();
     }
 
     client.EndDrawing(dirtyRect);
     return;
 #endif
 
     if (!sSurfaceExists) {
         return;