Bug 602976 - Implement full screen mode for Android, r=vlad a=blocking-fennec
authorMichael Wu <mwu@mozilla.com>
Mon, 08 Nov 2010 18:11:13 -0800
changeset 57134 cf55a728ea5c66754a4e0a78e41b015ebae96cf2
parent 57133 1ccf7b6e0eb7c0c4cfe25213d0f6408b25830720
child 57135 17bb9e21dfe1791fe2d24dbb77e8a0009701bb75
push idunknown
push userunknown
push dateunknown
reviewersvlad, blocking-fennec
bugs602976
milestone2.0b8pre
Bug 602976 - Implement full screen mode for Android, r=vlad a=blocking-fennec
embedding/android/AndroidManifest.xml.in
embedding/android/GeckoApp.java
embedding/android/GeckoAppShell.java
embedding/android/GeckoSurfaceView.java
widget/src/android/AndroidBridge.cpp
widget/src/android/AndroidBridge.h
widget/src/android/AndroidJavaWrappers.cpp
widget/src/android/AndroidJavaWrappers.h
widget/src/android/nsWindow.cpp
widget/src/android/nsWindow.h
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -14,19 +14,20 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
     <application android:label="@MOZ_APP_DISPLAYNAME@"
 		 android:icon="@drawable/icon"
 		 android:debuggable="true">
         <activity android:name="App"
                   android:label="@MOZ_APP_DISPLAYNAME@"
-                  android:configChanges="keyboard|keyboardHidden|orientation|mcc|mnc"
+                  android:configChanges="keyboard|keyboardHidden|mcc|mnc"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
-                  android:launchMode="singleTask">
+                  android:launchMode="singleTask"
+                  android:theme="@android:style/Theme.NoTitleBar">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
 
             <!-- Default browser intents -->
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -61,16 +61,17 @@ abstract public class GeckoApp
     extends Activity
 {
     public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK";
     public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
 
     public static FrameLayout mainLayout;
     public static GeckoSurfaceView surfaceView;
     public static GeckoApp mAppContext;
+    public static boolean mFullscreen = false;
     ProgressDialog mProgressDialog;
 
     void showErrorDialog(String message)
     {
         new AlertDialog.Builder(this)
             .setMessage(message)
             .setCancelable(false)
             .setPositiveButton("Exit",
@@ -111,39 +112,43 @@ abstract public class GeckoApp
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         Log.i("GeckoApp", "create");
         super.onCreate(savedInstanceState);
 
         mAppContext = this;
 
-        // hide our window's title, we don't want it
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        getWindow().setFlags(mFullscreen ?
+                             WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
+                             WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
-        checkAndLaunchUpdate();
+        if (surfaceView == null)
+            surfaceView = new GeckoSurfaceView(this);
+        else
+            mainLayout.removeView(surfaceView);
 
-        surfaceView = new GeckoSurfaceView(this);
-        
         mainLayout = new FrameLayout(this);
         mainLayout.addView(surfaceView,
                            new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,
                                                         FrameLayout.LayoutParams.FILL_PARENT));
 
         boolean useLaunchButton = false;
 
         String intentAction = getIntent().getAction();
         if (intentAction != null && intentAction.equals("org.mozilla.gecko.DEBUG"))
             useLaunchButton = true;
 
         setContentView(mainLayout,
                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                                                   ViewGroup.LayoutParams.FILL_PARENT));
 
         if (!GeckoAppShell.sGeckoRunning) {
+            checkAndLaunchUpdate();
+
             try {
                 BufferedReader reader =
                     new BufferedReader(new FileReader("/proc/cpuinfo"));
                 String line;
                 while ((line = reader.readLine()) != null) {
                     int index = line.indexOf("Processor");
                     if (index == -1)
                         continue;
@@ -191,18 +196,16 @@ abstract public class GeckoApp
                             launch();
                         }
                     });
                 mainLayout.addView(b, 300, 200);
             } else {
                 launch();
             }
         }
-
-        super.onCreate(savedInstanceState);
     }
 
     @Override
     protected void onNewIntent(Intent intent) {
         final String action = intent.getAction();
         if (Intent.ACTION_VIEW.equals(action)) {
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
@@ -217,17 +220,16 @@ abstract public class GeckoApp
             GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
             Log.i("GeckoApp","Intent : WEBAPP - " + uri);
         }
     }
 
     @Override
     public void onPause()
     {
-
         Log.i("GeckoApp", "pause");
         GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_PAUSING));
         // The user is navigating away from this activity, but nothing
         // has come to the foreground yet; for Gecko, we may want to
         // stop repainting, for example.
 
         // Whatever we do here should be fast, because we're blocking
         // the next activity from showing up until we finish.
@@ -237,18 +239,16 @@ abstract public class GeckoApp
     }
 
     @Override
     public void onResume()
     {
         Log.i("GeckoApp", "resume");
         if (GeckoAppShell.sGeckoRunning)
             GeckoAppShell.onResume();
-        if (surfaceView != null)
-            surfaceView.mSurfaceNeedsRedraw = true;
         // After an onPause, the activity is back in the foreground.
         // Undo whatever we did in onPause.
         super.onResume();
     }
 
     @Override
     public void onStop()
     {
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -44,25 +44,24 @@ import java.nio.*;
 import java.lang.reflect.*;
 
 import android.os.*;
 import android.app.*;
 import android.text.*;
 import android.view.*;
 import android.view.inputmethod.*;
 import android.content.*;
+import android.content.res.*;
+import android.content.pm.*;
 import android.graphics.*;
 import android.widget.*;
 import android.hardware.*;
 import android.location.*;
 
 import android.util.*;
-import android.content.DialogInterface; 
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.net.Uri;
 
 class GeckoAppShell
 {
     static {
         sGeckoRunning = false;
     }
 
@@ -588,17 +587,26 @@ class GeckoAppShell
         mAlertNotifications.remove(notificationID);
 
         NotificationManager notificationManager = (NotificationManager)
            GeckoApp.mAppContext.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.cancel(notificationID);
     }
 
     public static int getDpi() {
-         DisplayMetrics metrics = new DisplayMetrics();
-         GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-         return metrics.densityDpi;
+        DisplayMetrics metrics = new DisplayMetrics();
+        GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+        return metrics.densityDpi;
+    }
+
+    public static void setFullScreen(boolean fullscreen) {
+        GeckoApp.mFullscreen = fullscreen;
+
+        // force a reconfiguration to hide/show the system bar
+        GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
     }
     public static String showFilePicker(String aFilters) {
         return GeckoApp.mAppContext.
             showFilePicker(getMimeTypeFromExtensions(aFilters));
     }
 }
--- a/embedding/android/GeckoSurfaceView.java
+++ b/embedding/android/GeckoSurfaceView.java
@@ -103,48 +103,64 @@ class GeckoSurfaceView
             }
 
             if (width == 0 || height == 0)
                 mSoftwareBuffer = null;
             else if (mSoftwareBuffer == null ||
                      mSoftwareBuffer.capacity() < (width * height * 2) ||
                      mWidth != width || mHeight != height)
                 mSoftwareBuffer = ByteBuffer.allocateDirect(width * height * 2);
+            boolean doSyncDraw = GeckoAppShell.sGeckoRunning && m2DMode &&
+                                 mSoftwareBuffer != null;
+            mSyncDraw = doSyncDraw;
 
             mFormat = format;
             mWidth = width;
             mHeight = height;
             mSurfaceValid = true;
 
             Log.i("GeckoAppJava", "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
 
             GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height, -1, -1);
             GeckoAppShell.sendEventToGecko(e);
 
-            if (mSurfaceNeedsRedraw) {
+            if (mSoftwareBuffer != null)
                 GeckoAppShell.scheduleRedraw();
-                mSurfaceNeedsRedraw = false;
-            }
+            if (!doSyncDraw)
+                return;
         } finally {
             mSurfaceLock.unlock();
         }
+
+        mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
+        ByteBuffer bb = null;
+        try {
+            bb = mSyncBuf.take();
+        } catch (InterruptedException ie) {
+            Log.e("GeckoAppJava", "Threw exception while getting sync buf: " + ie);
+        }
+        if (bb != null && bb.capacity() == (width * height * 2)) {
+            mSoftwareBitmap.copyPixelsFromBuffer(bb);
+            Canvas c = holder.lockCanvas();
+            c.drawBitmap(mSoftwareBitmap, 0, 0, null);
+            holder.unlockCanvasAndPost(c);
+        }
     }
 
     public void surfaceCreated(SurfaceHolder holder) {
-        if (GeckoAppShell.sGeckoRunning)
-            mSurfaceNeedsRedraw = true;
     }
 
     public void surfaceDestroyed(SurfaceHolder holder) {
         Log.i("GeckoAppJava", "surface destroyed");
         mSurfaceValid = false;
         mSoftwareBuffer = null;
     }
 
     public ByteBuffer getSoftwareDrawBuffer() {
+        m2DMode = true;
         return mSoftwareBuffer;
     }
 
     /*
      * Called on Gecko thread
      */
 
     public static final int DRAW_ERROR = 0;
@@ -195,27 +211,39 @@ class GeckoSurfaceView
 
             if (!mSurfaceLock.isHeldByCurrentThread())
                 Log.e("GeckoAppJava", "endDrawing while mSurfaceLock not held by current thread!");
 
             mSurfaceLock.unlock();
         }
     }
 
-    public void draw2D(ByteBuffer buffer) {
+    public void draw2D(ByteBuffer buffer, int stride) {
         if (GeckoApp.mAppContext.mProgressDialog != null) {
             GeckoApp.mAppContext.mProgressDialog.dismiss();
             GeckoApp.mAppContext.mProgressDialog = null;
         }
+        if (mSyncDraw) {
+            if (stride != (mWidth * 2))
+                return;
+            mSyncDraw = false;
+            try {
+                mSyncBuf.put(buffer);
+            } catch (InterruptedException ie) {
+                Log.e("GeckoAppJava", "Threw exception while getting sync buf: " + ie);
+            }
+            return;
+        }
+
         if (buffer != mSoftwareBuffer)
             return;
         Canvas c = getHolder().lockCanvas();
         if (c == null)
             return;
-        if (buffer != mSoftwareBuffer) {
+        if (buffer != mSoftwareBuffer || stride != (mWidth * 2)) {
             getHolder().unlockCanvasAndPost(c);
             return;
         }
         if (mSoftwareBitmap == null ||
             mSoftwareBitmap.getHeight() != mHeight ||
             mSoftwareBitmap.getWidth() != mWidth) {
             mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
         }
@@ -276,22 +304,25 @@ class GeckoSurfaceView
     public boolean onTouchEvent(MotionEvent event) {
         GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
         return true;
     }
 
     // Is this surface valid for drawing into?
     boolean mSurfaceValid;
 
-    // Do we need to force a redraw on surfaceChanged?
-    boolean mSurfaceNeedsRedraw;
-
     // Are we actively between beginDrawing/endDrawing?
     boolean mInDrawing;
 
+    // Are we waiting for a buffer to draw in surfaceChanged?
+    boolean mSyncDraw;
+
+    // True if gecko requests a buffer
+    boolean m2DMode;
+
     // let's not change stuff around while we're in the middle of
     // starting drawing, ending drawing, or changing surface
     // characteristics
     ReentrantLock mSurfaceLock;
 
     // Surface format, from surfaceChanged.  Largely
     // useless.
     int mFormat;
@@ -312,9 +343,11 @@ class GeckoSurfaceView
 
     GeckoInputConnection inputConnection;
     boolean mIMEFocus;
     int mIMEState;
 
     // Software rendering
     ByteBuffer mSoftwareBuffer;
     Bitmap mSoftwareBitmap;
+
+    final SynchronousQueue<ByteBuffer> mSyncBuf = new SynchronousQueue<ByteBuffer>();
 }
--- a/widget/src/android/AndroidBridge.cpp
+++ b/widget/src/android/AndroidBridge.cpp
@@ -112,16 +112,17 @@ AndroidBridge::Init(JNIEnv *jEnv,
     jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V");
     jGetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getClipboardText", "()Ljava/lang/String;");
     jSetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setClipboardText", "(Ljava/lang/String;)V");
     jShowAlertNotification = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
     jShowFilePicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showFilePicker", "(Ljava/lang/String;)Ljava/lang/String;");
     jAlertsProgressListener_OnProgress = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "alertsProgressListener_OnProgress", "(Ljava/lang/String;JJLjava/lang/String;)V");
     jAlertsProgressListener_OnCancel = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "alertsProgressListener_OnCancel", "(Ljava/lang/String;)V");
     jGetDpi = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getDpi", "()I");
+    jSetFullScreen = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setFullScreen", "(Z)V");
 
 
     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"));
@@ -513,16 +514,22 @@ AndroidBridge::ShowFilePicker(nsAString&
                                             aFilters.Length());
     jstring jstr =  static_cast<jstring>(mJNIEnv->CallStaticObjectMethod(
                                              mGeckoAppShellClass,
                                              jShowFilePicker, jstrFilers));
     aFilePath.Assign(nsJNIString(jstr));
 }
 
 void
+AndroidBridge::SetFullScreen(PRBool aFullScreen)
+{
+    mJNIEnv->CallStaticIntMethod(mGeckoAppShellClass, jSetFullScreen, aFullScreen);
+}
+
+void
 AndroidBridge::SetSurfaceView(jobject obj)
 {
     mSurfaceView.Init(obj);
 }
 
 void *
 AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview)
 {
--- a/widget/src/android/AndroidBridge.h
+++ b/widget/src/android/AndroidBridge.h
@@ -165,16 +165,18 @@ public:
                                            const nsAString& aAlertText);
 
     void AlertsProgressListener_OnCancel(const nsAString& aAlertName);
 
     int GetDPI();
 
     void ShowFilePicker(nsAString& aFilePath, nsAString& aFilters);
 
+    void SetFullScreen(PRBool aFullScreen);
+
     struct AutoLocalJNIFrame {
         AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) {
             AndroidBridge::Bridge()->JNI()->PushLocalFrame(mEntries);
         }
         // Note! Calling Purge makes all previous local refs created in
         // the AutoLocalJNIFrame's scope INVALID; be sure that you locked down
         // any local refs that you need to keep around in global refs!
         void Purge() {
@@ -230,16 +232,17 @@ protected:
     jmethodID jMoveTaskToBack;
     jmethodID jGetClipboardText;
     jmethodID jSetClipboardText;
     jmethodID jShowAlertNotification;
     jmethodID jShowFilePicker;
     jmethodID jAlertsProgressListener_OnProgress;
     jmethodID jAlertsProgressListener_OnCancel;
     jmethodID jGetDpi;
+    jmethodID jSetFullScreen;
 
     // stuff we need for CallEglCreateWindowSurface
     jclass jEGLSurfaceImplClass;
     jclass jEGLContextImplClass;
     jclass jEGLConfigImplClass;
     jclass jEGLDisplayImplClass;
     jclass jEGLContextClass;
     jclass jEGL10Class;
--- a/widget/src/android/AndroidJavaWrappers.cpp
+++ b/widget/src/android/AndroidJavaWrappers.cpp
@@ -151,17 +151,17 @@ AndroidGeckoSurfaceView::InitGeckoSurfac
 {
     initInit();
 
     jGeckoSurfaceViewClass = getClassGlobalRef("org/mozilla/gecko/GeckoSurfaceView");
 
     jBeginDrawingMethod = getMethod("beginDrawing", "()I");
     jGetSoftwareDrawBufferMethod = getMethod("getSoftwareDrawBuffer", "()Ljava/nio/ByteBuffer;");
     jEndDrawingMethod = getMethod("endDrawing", "()V");
-    jDraw2DMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;)V");
+    jDraw2DMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;I)V");
     jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
 }
 
 void
 AndroidLocation::InitLocationClass(JNIEnv *jEnv)
 {
     initInit();
 
@@ -383,19 +383,19 @@ AndroidGeckoSurfaceView::BeginDrawing()
 
 void
 AndroidGeckoSurfaceView::EndDrawing()
 {
     JNI()->CallVoidMethod(wrapped_obj, jEndDrawingMethod);
 }
 
 void
-AndroidGeckoSurfaceView::Draw2D(jobject buffer)
+AndroidGeckoSurfaceView::Draw2D(jobject buffer, int stride)
 {
-    JNI()->CallVoidMethod(wrapped_obj, jDraw2DMethod, buffer);
+    JNI()->CallVoidMethod(wrapped_obj, jDraw2DMethod, buffer, stride);
 }
 
 jobject
 AndroidGeckoSurfaceView::GetSoftwareDrawBuffer()
 {
     return JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBufferMethod);
 }
 
--- a/widget/src/android/AndroidJavaWrappers.h
+++ b/widget/src/android/AndroidJavaWrappers.h
@@ -164,17 +164,17 @@ public:
     enum {
         DRAW_ERROR = 0,
         DRAW_GLES_2 = 1
     };
 
     int BeginDrawing();
     jobject GetSoftwareDrawBuffer();
     void EndDrawing();
-    void Draw2D(jobject buffer);
+    void Draw2D(jobject buffer, int stride);
 
     // must have a JNI local frame when calling this,
     // and you'd better know what you're doing
     jobject GetSurfaceHolder();
 
 protected:
     static jclass jGeckoSurfaceViewClass;
     static jmethodID jBeginDrawingMethod;
--- a/widget/src/android/nsWindow.cpp
+++ b/widget/src/android/nsWindow.cpp
@@ -430,16 +430,19 @@ nsWindow::PlaceBehind(nsTopLevelWidgetZP
 
 NS_IMETHODIMP
 nsWindow::SetSizeMode(PRInt32 aMode)
 {
     switch (aMode) {
         case nsSizeMode_Minimized:
             AndroidBridge::Bridge()->MoveTaskToBack();
             break;
+        case nsSizeMode_Fullscreen:
+            MakeFullScreen(PR_TRUE);
+            break;
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::Enable(PRBool aState)
 {
     ALOG("nsWindow[%p]::Enable %d ignored", (void*)this, aState);
@@ -582,16 +585,23 @@ nsWindow::DispatchEvent(nsGUIEvent *aEve
             break;
         }
         return status;
     }
     return nsEventStatus_eIgnore;
 }
 
 NS_IMETHODIMP
+nsWindow::MakeFullScreen(PRBool aFullScreen)
+{
+    AndroidBridge::Bridge()->SetFullScreen(aFullScreen);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsWindow::SetWindowClass(const nsAString& xulWinType)
 {
     return NS_OK;
 }
 
 mozilla::layers::LayerManager*
 nsWindow::GetLayerManager(bool* aAllowRetaining)
 {
@@ -848,58 +858,58 @@ nsWindow::DrawTo(gfxASurface *targetSurf
         targetSurface->SetDeviceOffset(offset);
 
     return PR_TRUE;
 }
 
 void
 nsWindow::OnDraw(AndroidGeckoEvent *ae)
 {
-    AndroidBridge::AutoLocalJNIFrame jniFrame;
-
     ALOG(">> OnDraw");
 
-    AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
-
-    NS_ASSERTION(!sview.isNull(), "SurfaceView is null!");
-
     if (!IsTopLevel()) {
         ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
         DumpWindows();
         return;
     }
 
     if (!mIsVisible) {
         ALOG("##### redraw for window %p, which is not visible -- ignoring!", (void*) this);
         DumpWindows();
         return;
     }
 
+    AndroidBridge::AutoLocalJNIFrame jniFrame;
+
+    AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
+
+    NS_ASSERTION(!sview.isNull(), "SurfaceView is null!");
+
     if (GetLayerManager()->GetBackendType() == LayerManager::LAYERS_BASIC) {
         jobject bytebuf = sview.GetSoftwareDrawBuffer();
         if (!bytebuf) {
             ALOG("no buffer to draw into - skipping draw");
             return;
         }
 
         void *buf = AndroidBridge::JNI()->GetDirectBufferAddress(bytebuf);
         int cap = AndroidBridge::JNI()->GetDirectBufferCapacity(bytebuf);
-        if (!buf || cap < (mBounds.width * mBounds.height * 2)) {
-            ALOG("### Software drawing, but too small a buffer %d expected %d (or no buffer %p)!", cap, mBounds.width * mBounds.height * 2, buf);
+        if (!buf || cap != (mBounds.width * mBounds.height * 2)) {
+            ALOG("### Software drawing, but unexpected buffer size %d expected %d (or no buffer %p)!", cap, mBounds.width * mBounds.height * 2, buf);
             return;
         }
 
         nsRefPtr<gfxImageSurface> targetSurface =
             new gfxImageSurface((unsigned char *)buf,
                                 gfxIntSize(mBounds.width, mBounds.height),
                                 mBounds.width * 2,
                                 gfxASurface::ImageFormatRGB16_565);
 
         DrawTo(targetSurface);
-        sview.Draw2D(bytebuf);
+        sview.Draw2D(bytebuf, mBounds.width * 2);
     } else {
         int drawType = sview.BeginDrawing();
 
         if (drawType == AndroidGeckoSurfaceView::DRAW_ERROR) {
             ALOG("##### BeginDrawing failed!");
             return;
         }
 
--- a/widget/src/android/nsWindow.h
+++ b/widget/src/android/nsWindow.h
@@ -120,30 +120,30 @@ public:
     NS_IMETHOD Invalidate(const nsIntRect &aRect,
                           PRBool aIsSynchronous);
     NS_IMETHOD Update();
     NS_IMETHOD SetFocus(PRBool aRaise = PR_FALSE);
     NS_IMETHOD GetScreenBounds(nsIntRect &aRect);
     virtual nsIntPoint WidgetToScreenOffset();
     NS_IMETHOD DispatchEvent(nsGUIEvent *aEvent, nsEventStatus &aStatus);
     nsEventStatus DispatchEvent(nsGUIEvent *aEvent);
+    NS_IMETHOD MakeFullScreen(PRBool aFullScreen);
     NS_IMETHOD SetWindowClass(const nsAString& xulWinType);
 
 
 
     NS_IMETHOD SetForegroundColor(const nscolor &aColor) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD SetBackgroundColor(const nscolor &aColor) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD SetCursor(nsCursor aCursor) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD SetCursor(imgIContainer* aCursor,
                          PRUint32 aHotspotX,
                          PRUint32 aHotspotY) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD SetHasTransparentBackground(PRBool aTransparent) { return NS_OK; }
     NS_IMETHOD GetHasTransparentBackground(PRBool& aTransparent) { aTransparent = PR_FALSE; return NS_OK; }
     NS_IMETHOD HideWindowChrome(PRBool aShouldHide) { return NS_ERROR_NOT_IMPLEMENTED; }
-    NS_IMETHOD MakeFullScreen(PRBool aFullScreen) { return NS_ERROR_NOT_IMPLEMENTED; }
     virtual void* GetNativeData(PRUint32 aDataType);
     NS_IMETHOD SetTitle(const nsAString& aTitle) { return NS_OK; }
     NS_IMETHOD SetIcon(const nsAString& aIconSpec) { return NS_OK; }
     NS_IMETHOD EnableDragDrop(PRBool aEnable) { return NS_OK; }
     NS_IMETHOD CaptureMouse(PRBool aCapture) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD CaptureRollupEvents(nsIRollupListener *aListener,
                                    nsIMenuRollup *aMenuRollup,
                                    PRBool aDoCapture,