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 92378 ba16d096e3d87b774c0834914c003eaa4cbfd261
parent 92377 5e47633414952424bf3fe0eeac621bea5ff587ce
child 92379 d47c32d6795d85dcfa04bd74eea8b4017f94bb22
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.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
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;