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 id16805
push usermwu@mozilla.com
push dateTue, 09 Nov 2010 02:15:10 +0000
treeherdermozilla-central@17bb9e21dfe1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad, blocking-fennec
bugs602976
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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,