Back out bug 760342 for Windows build error, bug 755070 for Android crashes, and bug 760458 because it depends on 755070
authorMatt Brubeck <mbrubeck@mozilla.com>
Fri, 01 Jun 2012 17:45:02 -0700
changeset 95616 d7b8fe3834b9a117dee9e10a7d00e1703d95196d
parent 95615 9a44997a37528492b8e75f7ffc27c87c9fdfe33b
child 95617 fdb686511fc4d925d67ff97953a59e634a76b990
push id825
push userdcamp@campd.org
push dateSat, 02 Jun 2012 23:58:00 +0000
treeherderfx-team@d0ebcaa7efb5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs760342, 755070, 760458
milestone15.0a1
Back out bug 760342 for Windows build error, bug 755070 for Android crashes, and bug 760458 because it depends on 755070
ipc/chromium/src/base/message_loop.cc
ipc/chromium/src/base/message_loop.h
js/public/HashTable.h
js/src/jsweakmap.h
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoEvent.java
mobile/android/base/Tab.java
mobile/android/base/Tabs.java
mobile/android/base/gfx/IntSize.java
mobile/android/base/gfx/LayerRenderer.java
mobile/android/base/gfx/ScreenshotLayer.java
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsAppShell.cpp
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -251,24 +251,16 @@ void MessageLoop::PostNonNestableTask(
   PostTask_Helper(from_here, task, 0, false);
 }
 
 void MessageLoop::PostNonNestableDelayedTask(
     const tracked_objects::Location& from_here, Task* task, int delay_ms) {
   PostTask_Helper(from_here, task, delay_ms, false);
 }
 
-void MessageLoop::PostIdleTask(
-    const tracked_objects::Location& from_here, Task* task) {
-  DCHECK(current() == this);
-  task->SetBirthPlace(from_here);
-  PendingTask pending_task(task, false);
-  deferred_non_nestable_work_queue_.push(pending_task);
-}
-
 // Possibly called on a background thread!
 void MessageLoop::PostTask_Helper(
     const tracked_objects::Location& from_here, Task* task, int delay_ms,
     bool nestable) {
   task->SetBirthPlace(from_here);
 
   PendingTask pending_task(task, nestable);
 
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -116,20 +116,16 @@ public:
       const tracked_objects::Location& from_here, Task* task, int delay_ms);
 
   void PostNonNestableTask(
       const tracked_objects::Location& from_here, Task* task);
 
   void PostNonNestableDelayedTask(
       const tracked_objects::Location& from_here, Task* task, int delay_ms);
 
-  // PostIdleTask is not thread safe and should be called on this thread
-  void PostIdleTask(
-      const tracked_objects::Location& from_here, Task* task);
-
   // A variant on PostTask that deletes the given object.  This is useful
   // if the object needs to live until the next run of the MessageLoop (for
   // example, deleting a RenderProcessHost from within an IPC callback is not
   // good).
   //
   // NOTE: This method may be called on any thread.  The object will be deleted
   // on the thread that executes MessageLoop::Run().  If this is not the same
   // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -137,42 +137,39 @@ class HashTable : private AllocPolicy
      * with Ptr/AddPtr, Range objects must not be used after any mutating hash
      * table operation unless the |generation()| is tested.
      */
     class Range
     {
       protected:
         friend class HashTable;
 
-        Range(Entry *c, Entry *e) : cur(c), end(e), validEntry(true) {
+        Range(Entry *c, Entry *e) : cur(c), end(e) {
             while (cur < end && !cur->isLive())
                 ++cur;
         }
 
         Entry *cur, *end;
-        DebugOnly<bool> validEntry;
 
       public:
-        Range() : cur(NULL), end(NULL), validEntry(false) {}
+        Range() : cur(NULL), end(NULL) {}
 
         bool empty() const {
             return cur == end;
         }
 
         T &front() const {
-            JS_ASSERT(validEntry);
             JS_ASSERT(!empty());
             return cur->t;
         }
 
         void popFront() {
             JS_ASSERT(!empty());
             while (++cur < end && !cur->isLive())
                 continue;
-            validEntry = true;
         }
     };
 
     /*
      * A Range whose lifetime delimits a mutating enumeration of a hash table.
      * Since rehashing when elements were removed during enumeration would be
      * bad, it is postponed until |endEnumeration()| is called. If
      * |endEnumeration()| is not called before an Enum's constructor, it will
@@ -203,34 +200,32 @@ class HashTable : private AllocPolicy
          *   HashSet<int> s;
          *   for (HashSet<int>::Enum e(s); !e.empty(); e.popFront())
          *     if (e.front() == 42)
          *       e.removeFront();
          */
         void removeFront() {
             table.remove(*this->cur);
             removed = true;
-            this->validEntry = false;
         }
 
         /*
          * Removes the |front()| element and re-inserts it into the table with
          * a new key at the new Lookup position.  |front()| is invalid after
          * this operation until the next call to |popFront()|.
          */
         void rekeyFront(const Lookup &l, const Key &k) {
             JS_ASSERT(&k != &HashPolicy::getKey(this->cur->t));
             if (match(*this->cur, l))
                 return;
             Entry e = *this->cur;
             HashPolicy::setKey(e.t, const_cast<Key &>(k));
             table.remove(*this->cur);
             table.add(l, e);
             added = true;
-            this->validEntry = false;
         }
 
         void rekeyFront(const Key &k) {
             rekeyFront(k, k);
         }
 
         /* Potentially rehashes the table. */
         ~Enum() {
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -182,16 +182,17 @@ class WeakMap : public HashMap<Key, Valu
             /* If the entry is live, ensure its key and value are marked. */
             Key k(e.front().key);
             bool keyIsMarked = gc::IsMarked(&k);
             if (keyIsMarked) {
                 if (markValue(trc, &e.front().value))
                     markedAny = true;
                 e.rekeyFront(k);
             }
+            JS_ASSERT_IF(keyIsMarked, gc::IsMarked(&e.front().value));
         }
         return markedAny;
     }
 
     void sweep(JSTracer *trc) {
         /* Remove all entries whose keys remain unmarked. */
         for (Enum e(*this); !e.empty(); e.popFront()) {
             Key k(e.front().key);
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -683,17 +683,17 @@ abstract public class GeckoApp
                 byte[] thumbnail = BrowserDB.getThumbnailForUrl(getContentResolver(), tab.getURL());
                 if (thumbnail != null)
                     processThumbnail(tab, null, thumbnail);
                 return;
             }
 
             int dw = tab.getThumbnailWidth();
             int dh = tab.getThumbnailHeight();
-            GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL, tab.getThumbnailBuffer()));
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL));
         }
     }
     
     void processThumbnail(Tab thumbnailTab, Bitmap bitmap, byte[] compressed) {
         if (Tabs.getInstance().isSelectedTab(thumbnailTab)) {
             if (compressed == null) {
                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
                 bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -10,17 +10,16 @@ import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.ScreenshotLayer;
 import org.mozilla.gecko.FloatUtils;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.ViewportMetrics;
-import org.mozilla.gecko.gfx.RectUtils;
 
 import java.io.*;
 import java.lang.reflect.*;
 import java.nio.*;
 import java.text.*;
 import java.util.*;
 import java.util.zip.*;
 import java.util.concurrent.*;
@@ -79,30 +78,36 @@ public class GeckoAppShell
     static public final int WPL_STATE_STOP = 0x00000010;
     static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
     static public final int WPL_STATE_IS_NETWORK = 0x00040000;
 
     public static final String SHORTCUT_TYPE_WEBAPP = "webapp";
     public static final String SHORTCUT_TYPE_BOOKMARK = "bookmark";
 
     static public final int SCREENSHOT_THUMBNAIL = 0;
-    static public final int SCREENSHOT_CHECKERBOARD = 1;
+    static public final int SCREENSHOT_WHOLE_PAGE = 1;
+    static public final int SCREENSHOT_UPDATE = 2;
 
     static public final int RESTORE_NONE = 0;
     static public final int RESTORE_OOM = 1;
     static public final int RESTORE_CRASH = 2;
 
     static private File sCacheFile = null;
     static private int sFreeSpace = -1;
     static File sHomeDir = null;
     static private int sDensityDpi = 0;
     private static Boolean sSQLiteLibsLoaded = false;
     private static Boolean sNSSLibsLoaded = false;
     private static Boolean sLibsSetup = false;
     private static File sGREDir = null;
+    private static RectF sCheckerboardPageRect;
+    private static float sLastCheckerboardWidthRatio, sLastCheckerboardHeightRatio;
+    private static RepaintRunnable sRepaintRunnable = new RepaintRunnable();
+    static private int sMaxTextureSize = 0;
+
     private static Map<String, CopyOnWriteArrayList<GeckoEventListener>> mEventListeners
             = new HashMap<String, CopyOnWriteArrayList<GeckoEventListener>>();
 
     /* Is the value in sVibrationEndTime valid? */
     private static boolean sVibrationMaybePlaying = false;
 
     /* Time (in System.nanoTime() units) when the currently-playing vibration
      * is scheduled to end.  This value is valid only when
@@ -118,16 +123,18 @@ public class GeckoAppShell
     private static Sensor gOrientationSensor = null;
     private static Sensor gProximitySensor = null;
     private static Sensor gLightSensor = null;
 
     private static boolean mLocationHighAccuracy = false;
 
     private static Handler sGeckoHandler;
 
+    private static boolean sDisableScreenshot = false;
+
     /* 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);
@@ -523,17 +530,52 @@ public class GeckoAppShell
     public static void notifyIMEChange(String text, int start, int end, int newEnd) {
         if (mInputConnection != null)
             mInputConnection.notifyIMEChange(text, start, end, newEnd);
     }
 
     // Called by AndroidBridge using JNI
     public static void notifyScreenShot(final ByteBuffer data, final int tabId, final int x, final int y,
                                         final int width, final int height, final int token) {
-        ScreenshotHandler.notifyScreenShot(data, tabId, x, y, width, height, token);
+        getHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    final Tab tab = Tabs.getInstance().getTab(tabId);
+                    if (tab == null)
+                        return;
+
+                    if (!Tabs.getInstance().isSelectedTab(tab) && SCREENSHOT_THUMBNAIL != token)
+                        return;
+
+                    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+                    b.copyPixelsFromBuffer(data);
+                    switch (token) {
+                    case SCREENSHOT_WHOLE_PAGE:
+                        GeckoApp.mAppContext.getLayerController()
+                            .getView().getRenderer()
+                            .setCheckerboardBitmap(b, sCheckerboardPageRect);
+                        break;
+                    case SCREENSHOT_UPDATE:
+                        GeckoApp.mAppContext.getLayerController().getView().getRenderer().
+                            updateCheckerboardBitmap(
+                                b, sLastCheckerboardWidthRatio * x,
+                                sLastCheckerboardHeightRatio * y,
+                                sLastCheckerboardWidthRatio * width,
+                                sLastCheckerboardHeightRatio * height,
+                                sCheckerboardPageRect);
+                        break;
+                    case SCREENSHOT_THUMBNAIL:
+                        GeckoApp.mAppContext.processThumbnail(tab, b, null);
+                        break;
+                    }
+                } finally {
+                    freeDirectBuffer(data);
+                }
+            }
+        });
     }
 
     private static CountDownLatch sGeckoPendingAcks = null;
 
     // Block the current thread until the Gecko event loop is caught up
     synchronized public static void geckoEventSync() {
         sGeckoPendingAcks = new CountDownLatch(1);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createSyncEvent());
@@ -2089,36 +2131,16 @@ public class GeckoAppShell
 
     static native void notifyFilePickerResult(String filePath, long id);
 
     /* Called by JNI from AndroidBridge */
     public static void showFilePickerAsync(String aMimeType, long id) {
         if (!GeckoApp.mAppContext.showFilePicker(aMimeType, new AsyncResultHandler(id)))
             GeckoAppShell.notifyFilePickerResult("", id);
     }
-    public static void screenshotWholePage(Tab tab) {
-        ScreenshotHandler.screenshotWholePage(tab);
-    }
-
-    // Called by AndroidBridge using JNI
-    public static void notifyPaintedRect(float top, float left, float bottom, float right) {
-        ScreenshotHandler.notifyPaintedRect(top, left, bottom, right);
-    }
-}
-
-class ScreenshotHandler {
-    private static Queue<PendingScreenshot> sPendingScreenshots = new ArrayDeque<PendingScreenshot>();
-    private static RectF sCheckerboardPageRect;
-    private static float sLastCheckerboardWidthRatio, sLastCheckerboardHeightRatio;
-    private static RepaintRunnable sRepaintRunnable = new RepaintRunnable();
-    private static int sMaxTextureSize = 0;
-    private static final String LOGTAG = "GeckoScreenshot";
-    private static boolean sDisableScreenshot = false;
-    private static ByteBuffer sWholePageScreenshotBuffer;
-    private static int sCheckerboardBufferWidth, sCheckerboardBufferHeight;
 
     static class RepaintRunnable implements Runnable {
         private boolean mIsRepaintRunnablePosted = false;
         private float mDirtyTop = Float.POSITIVE_INFINITY, mDirtyLeft = Float.POSITIVE_INFINITY;
         private float mDirtyBottom = Float.NEGATIVE_INFINITY, mDirtyRight = Float.NEGATIVE_INFINITY;
 
         public void run() {
             float top, left, bottom, right;
@@ -2131,50 +2153,35 @@ class ScreenshotHandler {
                 // reset these to infinity to start accumulating again
                 mDirtyTop = Float.POSITIVE_INFINITY;
                 mDirtyLeft = Float.POSITIVE_INFINITY;
                 mDirtyBottom = Float.NEGATIVE_INFINITY;
                 mDirtyRight = Float.NEGATIVE_INFINITY;
                 mIsRepaintRunnablePosted = false;
             }
 
-
             Tab tab = Tabs.getInstance().getSelectedTab();
-            ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
-            
-            if (RectUtils.fuzzyEquals(sCheckerboardPageRect, viewport.getCssPageRect())) {
-                float width = right - left;
-                float height = bottom - top;
-                scheduleCheckerboardScreenshotEvent(tab.getId(), 
-                                                    (int)left, (int)top, (int)width, (int)height, 
-                                                    (int)(sLastCheckerboardWidthRatio * left), 
-                                                    (int)(sLastCheckerboardHeightRatio * top),
-                                                    (int)(sLastCheckerboardWidthRatio * width),
-                                                    (int)(sLastCheckerboardHeightRatio * height),
-                                                    sCheckerboardBufferWidth, sCheckerboardBufferHeight);
-            } else {
-                GeckoAppShell.screenshotWholePage(tab);
-            }
+            GeckoAppShell.screenshotWholePage(tab);
         }
 
         void addRectToRepaint(float top, float left, float bottom, float right) {
             synchronized(this) {
-                ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
-                mDirtyTop = Math.max(sCheckerboardPageRect.top, Math.min(top, mDirtyTop));
-                mDirtyLeft = Math.max(sCheckerboardPageRect.left, Math.min(left, mDirtyLeft));
-                mDirtyBottom = Math.min(sCheckerboardPageRect.bottom, Math.max(bottom, mDirtyBottom));
-                mDirtyRight = Math.min(sCheckerboardPageRect.right, Math.max(right, mDirtyRight));
+                mDirtyTop = Math.min(top, mDirtyTop);
+                mDirtyLeft = Math.min(left, mDirtyLeft);
+                mDirtyBottom = Math.max(bottom, mDirtyBottom);
+                mDirtyRight = Math.max(right, mDirtyRight);
                 if (!mIsRepaintRunnablePosted) {
-                    GeckoAppShell.getHandler().postDelayed(this, 5000);
+                    getHandler().postDelayed(this, 5000);
                     mIsRepaintRunnablePosted = true;
                 }
             }
         }
     }
 
+    // Called by AndroidBridge using JNI
     public static void notifyPaintedRect(float top, float left, float bottom, float right) {
         sRepaintRunnable.addRectToRepaint(top, left, bottom, right);
     }
 
     private static int clamp(int min, int val, int max) {
         return Math.max(Math.min(max, val), min);
     }
 
@@ -2191,19 +2198,17 @@ class ScreenshotHandler {
             return;
 
         if (sMaxTextureSize == 0) {
             int[] maxTextureSize = new int[1];
             GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
             sMaxTextureSize = maxTextureSize[0];
             if (sMaxTextureSize == 0)
                 return;
-            sWholePageScreenshotBuffer = GeckoAppShell.allocateDirectBuffer(ScreenshotLayer.getMaxNumPixels() * 2 /* 16 bpp */);
         }
-
         ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
         Log.i(LOGTAG, "Taking whole-screen screenshot, viewport: " + viewport);
         // source width and height to screenshot
         float sx = viewport.cssPageRectLeft;
         float sy = viewport.cssPageRectTop;
         float sw = viewport.cssPageRectRight - viewport.cssPageRectLeft;
         float sh = viewport.cssPageRectBottom - viewport.cssPageRectTop;
         int maxPixels = Math.min(ScreenshotLayer.getMaxNumPixels(), sMaxTextureSize * sMaxTextureSize);
@@ -2214,101 +2219,19 @@ class ScreenshotHandler {
         // calc destination width and hight
         int idealDstWidth = IntSize.nextPowerOfTwo(sw / idealZoomFactor);
         // min texture size such that the other dimention doesn't excede the max
         int minTextureSize = maxPixels / sMaxTextureSize;
         int dx = 0;
         int dy = 0;
         int dw = clamp(minTextureSize, idealDstWidth, sMaxTextureSize);
         int dh = maxPixels / dw;
-        sCheckerboardBufferWidth = dw;
-        sCheckerboardBufferHeight = dh;
 
         sLastCheckerboardWidthRatio = dw / sw;
         sLastCheckerboardHeightRatio = dh / sh;
         sCheckerboardPageRect = viewport.getCssPageRect();
-        scheduleCheckerboardScreenshotEvent(tab.getId(), (int)sx, (int)sy, (int)sw, (int)sh, dx, dy, dw, dh, dw, dh);
-    }
 
-    static void scheduleCheckerboardScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int bw, int bh) {
-        float totalSize = sw * sh;
-        int numSlices = (int) Math.ceil(totalSize / 100000);
-        int srcSliceSize = (int) Math.ceil(sh / numSlices);
-        int dstSliceSize = (int) Math.ceil(dh / numSlices);
-        for (int i = 0; i < numSlices; i++) {
-            GeckoEvent event =
-                GeckoEvent.createScreenshotEvent(tabId, 
-                                                 sx, sy + srcSliceSize * i,  sw, srcSliceSize, 
-                                                 dx, dy + dstSliceSize * i,  dw, dstSliceSize, bw, bh,
-                                                 GeckoAppShell.SCREENSHOT_CHECKERBOARD,
-                                                 sWholePageScreenshotBuffer);
-            synchronized(sPendingScreenshots) {
-                sPendingScreenshots.add(new PendingScreenshot(tabId, event));
-                if (sPendingScreenshots.size() == 1)
-                    sendNextEventToGecko();
-            }
-        }
-    }
-
-    static void sendNextEventToGecko() {
-        synchronized(sPendingScreenshots) {
-            if (sPendingScreenshots.isEmpty())
-                return;
-            GeckoAppShell.sendEventToGecko(sPendingScreenshots.element().getEvent());
-        }
-    }
-    
-    static class PendingScreenshot {
-        private final GeckoEvent mEvent;
-        private final int mTabId;
-
-        PendingScreenshot(int tabId, GeckoEvent event) {
-            mTabId = tabId;
-            mEvent = event;
-        }
-
-        GeckoEvent getEvent() {
-            return mEvent;
-        }
-
-    }
-
-    public static void notifyScreenShot(final ByteBuffer data, final int tabId, final int x, final int y,
-                                        final int width, final int height, final int token) {
-
-        GeckoAppShell.getHandler().post(new Runnable() {
-            public void run() {
-                final Tab tab = Tabs.getInstance().getTab(tabId);
-                if (tab == null) {
-                    if (token == GeckoAppShell.SCREENSHOT_CHECKERBOARD) {
-                        synchronized(sPendingScreenshots) {
-                            sPendingScreenshots.remove();
-                            sendNextEventToGecko();
-                        }
-                    }
-                    return;
-                }
-                switch (token) {
-                    case GeckoAppShell.SCREENSHOT_CHECKERBOARD:
-                    {
-                        GeckoApp.mAppContext.getLayerController()
-                            .getView().getRenderer()
-                            .setCheckerboardBitmap(data, width, height, sCheckerboardPageRect);
-                        synchronized(sPendingScreenshots) {
-                            sPendingScreenshots.remove();
-                            sendNextEventToGecko();
-                        }
-                        break;
-                    }
-                    case GeckoAppShell.SCREENSHOT_THUMBNAIL:
-                    {
-                        if (Tabs.getInstance().isSelectedTab(tab)) {
-                            Bitmap b = tab.getThumbnailBitmap();
-                            b.copyPixelsFromBuffer(data);
-                            GeckoApp.mAppContext.processThumbnail(tab, b, null);
-                        }
-                        break;
-                    }
-                }
-            }
-        });
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(),
+                (int)FloatMath.ceil(sx), (int)FloatMath.ceil(sy),
+                (int)FloatMath.floor(sw), (int)FloatMath.floor(sh),
+                dx, dy, dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
     }
 }
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -18,17 +18,16 @@ import android.hardware.*;
 import android.location.*;
 import android.util.FloatMath;
 import android.util.DisplayMetrics;
 import android.graphics.PointF;
 import android.text.format.Time;
 import android.os.SystemClock;
 import java.lang.Math;
 import java.lang.System;
-import java.nio.ByteBuffer;
 
 import android.util.Log;
 
 /* We're not allowed to hold on to most events given to us
  * so we save the parts of the events we want to use in GeckoEvent.
  * Fields have different meanings depending on the event type.
  */
 
@@ -112,18 +111,16 @@ public class GeckoEvent {
 
     public double mBandwidth;
     public boolean mCanBeMetered;
 
     public int mNativeWindow;
 
     public short mScreenOrientation;
 
-    public ByteBuffer mBuffer;
-
     private GeckoEvent(int evType) {
         mType = evType;
     }
 
     public static GeckoEvent createPauseEvent(boolean isApplicationInBackground) {
         GeckoEvent event = new GeckoEvent(ACTIVITY_PAUSING);
         event.mFlags = isApplicationInBackground ? 0 : 1;
         return event;
@@ -454,27 +451,25 @@ public class GeckoEvent {
 
     public static GeckoEvent createNetworkEvent(double bandwidth, boolean canBeMetered) {
         GeckoEvent event = new GeckoEvent(NETWORK_CHANGED);
         event.mBandwidth = bandwidth;
         event.mCanBeMetered = canBeMetered;
         return event;
     }
 
-    public static GeckoEvent createScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int bw, int bh, int token, ByteBuffer buffer) {
+    public static GeckoEvent createScreenshotEvent(int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int token) {
         GeckoEvent event = new GeckoEvent(SCREENSHOT);
-        event.mPoints = new Point[5];
+        event.mPoints = new Point[4];
         event.mPoints[0] = new Point(sx, sy);
         event.mPoints[1] = new Point(sw, sh);
         event.mPoints[2] = new Point(dx, dy);
         event.mPoints[3] = new Point(dw, dh);
-        event.mPoints[4] = new Point(bw, bh);
         event.mMetaState = tabId;
         event.mFlags = token;
-        event.mBuffer = buffer;
         return event;
     }
 
     public static GeckoEvent createScreenOrientationEvent(short aScreenOrientation) {
         GeckoEvent event = new GeckoEvent(SCREENORIENTATION_CHANGED);
         event.mScreenOrientation = aScreenOrientation;
         return event;
     }
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -16,17 +16,16 @@ import android.util.Log;
 import android.view.Surface;
 import android.view.View;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.gfx.Layer;
 
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
@@ -60,18 +59,16 @@ public final class Tab {
     private float mMinZoom;
     private float mMaxZoom;
     private ArrayList<View> mPluginViews;
     private HashMap<Object, Layer> mPluginLayers;
     private ContentResolver mContentResolver;
     private ContentObserver mContentObserver;
     private int mCheckerboardColor = Color.WHITE;
     private int mState;
-    private ByteBuffer mThumbnailBuffer;
-    private Bitmap mThumbnailBitmap;
 
     public static final int STATE_DELAYED = 0;
     public static final int STATE_LOADING = 1;
     public static final int STATE_SUCCESS = 2;
     public static final int STATE_ERROR = 3;
 
     public Tab(int id, String url, boolean external, int parentId, String title) {
         mId = id;
@@ -136,41 +133,16 @@ public final class Tab {
     public Drawable getFavicon() {
         return mFavicon;
     }
 
     public Drawable getThumbnail() {
         return mThumbnail;
     }
 
-    synchronized public ByteBuffer getThumbnailBuffer() {
-        int capacity = getThumbnailWidth() * getThumbnailHeight() * 2 /* 16 bpp */;
-        if (mThumbnailBuffer != null && mThumbnailBuffer.capacity() == capacity)
-            return mThumbnailBuffer;
-        if (mThumbnailBuffer != null)
-            GeckoAppShell.freeDirectBuffer(mThumbnailBuffer); // not calling freeBuffer() because it would deadlock
-        return mThumbnailBuffer = GeckoAppShell.allocateDirectBuffer(capacity);
-    }
-
-    public Bitmap getThumbnailBitmap() {
-        if (mThumbnailBitmap != null)
-            return mThumbnailBitmap;
-        return mThumbnailBitmap = Bitmap.createBitmap(getThumbnailWidth(), getThumbnailHeight(), Bitmap.Config.RGB_565);
-    }
-
-    public void finalize() {
-        freeBuffer();
-    }
-
-    synchronized void freeBuffer() {
-        if (mThumbnailBuffer != null)
-            GeckoAppShell.freeDirectBuffer(mThumbnailBuffer);
-        mThumbnailBuffer = null;
-    }
-
     float getDensity() {
         if (sDensity == 0.0f) {
             sDensity = GeckoApp.mAppContext.getDisplayMetrics().density;
         }
         return sDensity;
     }
 
     int getThumbnailWidth() {
@@ -182,20 +154,23 @@ public final class Tab {
     }
 
     public void updateThumbnail(final Bitmap b) {
         final Tab tab = this;
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
                 if (b != null) {
                     try {
+                        Bitmap bitmap = Bitmap.createScaledBitmap(b, getThumbnailWidth(), getThumbnailHeight(), false);
+
                         if (mState == Tab.STATE_SUCCESS)
-                            saveThumbnailToDB(new BitmapDrawable(b));
+                            saveThumbnailToDB(new BitmapDrawable(bitmap));
 
-                        mThumbnail = new BitmapDrawable(b);
+                        mThumbnail = new BitmapDrawable(bitmap);
+                        b.recycle();
                     } catch (OutOfMemoryError oom) {
                         Log.e(LOGTAG, "Unable to create/scale bitmap", oom);
                         mThumbnail = null;
                     }
                 } else {
                     mThumbnail = null;
                 }
                 GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -71,20 +71,18 @@ public class Tabs implements GeckoEventL
         }
 
         Log.i(LOGTAG, "Added a tab with id: " + id);
         return tab;
     }
 
     public void removeTab(int id) {
         if (tabs.containsKey(id)) {
-            Tab tab = getTab(id);
-            order.remove(tab);
+            order.remove(getTab(id));
             tabs.remove(id);
-            tab.freeBuffer();
             Log.i(LOGTAG, "Removed a tab with id: " + id);
         }
     }
 
     public Tab selectTab(int id) {
         if (!tabs.containsKey(id))
             return null;
 
--- a/mobile/android/base/gfx/IntSize.java
+++ b/mobile/android/base/gfx/IntSize.java
@@ -66,16 +66,10 @@ public class IntSize {
 
     public static int nextPowerOfTwo(float value) {
         return nextPowerOfTwo((int) value);
     }
 
     public IntSize nextPowerOfTwo() {
         return new IntSize(nextPowerOfTwo(width), nextPowerOfTwo(height));
     }
-
-    public static boolean isPowerOfTwo(int value) {
-        if (value == 0)
-            return false;
-        return (value & (value - 1)) == 0;
-    }
 }
 
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -129,18 +129,18 @@ public class LayerRenderer implements GL
     public static final String DEFAULT_FRAGMENT_SHADER =
         "precision highp float;\n" +
         "varying vec2 vTexCoord;\n" +
         "uniform sampler2D sTexture;\n" +
         "void main() {\n" +
         "    gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
         "}\n";
 
-    public void setCheckerboardBitmap(ByteBuffer data, int width, int height, RectF pageRect) {
-        mCheckerboardLayer.setBitmap(data, width, height);
+    public void setCheckerboardBitmap(Bitmap bitmap, RectF pageRect) {
+        mCheckerboardLayer.setBitmap(bitmap);
         mCheckerboardLayer.beginTransaction();
         try {
             mCheckerboardLayer.setPosition(RectUtils.round(pageRect));
             mCheckerboardLayer.invalidate();
         } finally {
             mCheckerboardLayer.endTransaction();
         }
     }
--- a/mobile/android/base/gfx/ScreenshotLayer.java
+++ b/mobile/android/base/gfx/ScreenshotLayer.java
@@ -30,39 +30,25 @@ public class ScreenshotLayer extends Sin
     private ScreenshotImage mImage;
     // Size of the image buffer
     private IntSize mBufferSize;
     // The size of the bitmap painted in the buffer
     // (may be smaller than mBufferSize due to power of 2 padding)
     private IntSize mImageSize;
     // Whether we have an up-to-date image to draw
     private boolean mHasImage;
-    private static String LOGTAG = "GeckoScreenshot";
 
     public static int getMaxNumPixels() {
         return SCREENSHOT_SIZE_LIMIT;
     }
 
     public void reset() {
         mHasImage = false;
     }
 
-    void setBitmap(ByteBuffer data, int width, int height) {
-        mImageSize = new IntSize(width, height);
-        if (IntSize.isPowerOfTwo(width) && IntSize.isPowerOfTwo(height)) {
-            mBufferSize = mImageSize;
-            mHasImage = true;
-            mImage.setBitmap(data, width, height, CairoImage.FORMAT_RGB16_565);
-        } else {
-            Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
-            b.copyPixelsFromBuffer(data);
-            setBitmap(b);
-        }
-    }
-    
     void setBitmap(Bitmap bitmap) {
         mImageSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
         int width = IntSize.nextPowerOfTwo(bitmap.getWidth());
         int height = IntSize.nextPowerOfTwo(bitmap.getHeight());
         mBufferSize = new IntSize(width, height);
         mImage.setBitmap(bitmap, width, height, CairoImage.FORMAT_RGB16_565);
         mHasImage = true;
     }
@@ -125,27 +111,17 @@ public class ScreenshotLayer extends Sin
                     GeckoAppShell.freeDirectBuffer(mBuffer);
                     mBuffer = null;
                 }
             } finally {
                 super.finalize();
             }
         }
 
-        void copyBuffer(ByteBuffer src, ByteBuffer dst, int size) {
-            dst.asIntBuffer().put(src.asIntBuffer());
-        }
-
-        synchronized void setBitmap(ByteBuffer data, int width, int height, int format) {
-            mSize = new IntSize(width, height);
-            mFormat = format;
-            copyBuffer(data, mBuffer, width * height * 2);
-        }
-
-        synchronized void setBitmap(Bitmap bitmap, int width, int height, int format) {
+        void setBitmap(Bitmap bitmap, int width, int height, int format) {
             Bitmap tmp;
             mSize = new IntSize(width, height);
             mFormat = format;
             if (width == bitmap.getWidth() && height == bitmap.getHeight()) {
                 tmp = bitmap;
             } else {
                 tmp = Bitmap.createBitmap(width, height, CairoUtils.cairoFormatTobitmapConfig(mFormat));
                 new Canvas(tmp).drawBitmap(bitmap, 0.0f, 0.0f, new Paint());
@@ -157,15 +133,15 @@ public class ScreenshotLayer extends Sin
             Bitmap tmp = Bitmap.createBitmap(mSize.width, mSize.height, CairoUtils.cairoFormatTobitmapConfig(mFormat));
             tmp.copyPixelsFromBuffer(mBuffer.asIntBuffer());
             Canvas c = new Canvas(tmp);
             c.drawBitmap(bitmap, x, y, new Paint());
             tmp.copyPixelsToBuffer(mBuffer.asIntBuffer());
         }
 
         @Override
-        synchronized public ByteBuffer getBuffer() { return mBuffer; }
+        public ByteBuffer getBuffer() { return mBuffer; }
         @Override
-        synchronized public IntSize getSize() { return mSize; }
+        public IntSize getSize() { return mSize; }
         @Override
-        synchronized public int getFormat() { return mFormat; }
+        public int getFormat() { return mFormat; }
     }
 }
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -24,18 +24,16 @@
 #include "nsIThreadManager.h"
 #include "mozilla/dom/sms/PSms.h"
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #include "nsPresContext.h"
 #include "nsIDocShell.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/dom/ScreenOrientation.h"
-#include "nsIDOMWindowUtils.h"
-#include "nsIDOMClientRect.h"
 
 #ifdef DEBUG
 #define ALOG_BRIDGE(args...) ALOG(args)
 #else
 #define ALOG_BRIDGE(args...)
 #endif
 
 #define IME_FULLSCREEN_PREF "widget.ime.android.landscape_fullscreen"
@@ -2345,56 +2343,56 @@ AndroidBridge::RemovePluginView(jobject 
 }
 
 extern "C"
 __attribute__ ((visibility("default")))
 jobject JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size);
 
 
-nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstX, PRInt32 dstY, PRInt32 dstW, PRInt32 dstH, PRInt32 bufW, PRInt32 bufH, PRInt32 tabId, PRInt32 token, jobject buffer)
+nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token)
 {
     nsresult rv;
-    float scale = 1.0;
- 
-    if (!buffer)
-        return NS_OK;
 
     // take a screenshot, as wide as possible, proportional to the destination size
     if (!srcW && !srcH) {
-        nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
-        if (!utils)
+        nsCOMPtr<nsIDOMDocument> doc;
+        rv = window->GetDocument(getter_AddRefs(doc));
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (!doc)
             return NS_ERROR_FAILURE;
 
-        nsCOMPtr<nsIDOMClientRect> rect;
-        rv = utils->GetRootBounds(getter_AddRefs(rect));
+        nsCOMPtr<nsIDOMElement> docElement;
+        rv = doc->GetDocumentElement(getter_AddRefs(docElement));
         NS_ENSURE_SUCCESS(rv, rv);
-        if (!rect)
+        if (!docElement)
             return NS_ERROR_FAILURE;
 
-        float left, top, width, height;
-        rect->GetLeft(&left);
-        rect->GetTop(&top);
-        rect->GetWidth(&width);
-        rect->GetHeight(&height);
-
-        if (width == 0 || height == 0)
+        PRInt32 viewportHeight;
+        PRInt32 pageWidth;
+        PRInt32 pageHeight;
+        window->GetInnerHeight(&viewportHeight);
+        docElement->GetScrollWidth(&pageWidth);
+        docElement->GetScrollHeight(&pageHeight);
+
+        // use the page or viewport dimensions, whichever is larger
+        PRInt32 width = pageWidth;
+        PRInt32 height = viewportHeight > pageHeight ? viewportHeight : pageHeight;
+
+        if (!width || !height)
             return NS_ERROR_FAILURE;
 
         float aspectRatio = ((float) dstW) / dstH;
         if (width / aspectRatio < height) {
             srcW = width;
             srcH = width / aspectRatio;
         } else {
             srcW = height * aspectRatio;
             srcH = height;
         }
-
-        srcX = left;
-        srcY = top;
     }
 
     JNIEnv* env = GetJNIEnv();
     if (!env)
         return NS_OK;
 
     AutoLocalJNIFrame jniFrame(env);
 
@@ -2412,27 +2410,31 @@ nsresult AndroidBridge::TakeScreenshot(n
     nsIPresShell* presShell = presContext->PresShell();
     PRUint32 renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
                                nsIPresShell::RENDER_DOCUMENT_RELATIVE);
     nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale),
              nsPresContext::CSSPixelsToAppUnits(srcY / scale),
              nsPresContext::CSSPixelsToAppUnits(srcW / scale),
              nsPresContext::CSSPixelsToAppUnits(srcH / scale));
 
-    PRUint32 stride = bufW * 2 /* 16 bpp */;
+    PRUint32 stride = dstW * 2;
+    PRUint32 bufferSize = dstH * stride;
+
+    jobject buffer = Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(env, NULL, bufferSize);
+    if (!buffer)
+        return NS_OK;
 
     void* data = env->GetDirectBufferAddress(buffer);
-    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(bufW, bufH), stride, gfxASurface::ImageFormatRGB16_565);
+    memset(data, 0, bufferSize);
+    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(dstW, dstH), stride, gfxASurface::ImageFormatRGB16_565);
     nsRefPtr<gfxContext> context = new gfxContext(surf);
-    gfxPoint pt(dstX, dstY);
-    context->Translate(pt);
     context->Scale(scale * dstW / srcW, scale * dstH / srcH);
     rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
     NS_ENSURE_SUCCESS(rv, rv);
-    env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, srcX * dstW / srcW , srcY * dstH / srcH, bufW, bufH, token);
+    env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot, buffer, tabId, srcX * dstW / srcW , srcY * dstH / srcH, dstW, dstH, token);
     return NS_OK;
 }
 
 void
 AndroidBridge::NotifyPaintedRect(float top, float left, float bottom, float right)
 {
     JNIEnv* env = GetJNIEnv();
     if (!env)
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -150,17 +150,17 @@ public:
 
     /* These are defined in mobile/android/base/GeckoAppShell.java */
     enum {
         SCREENSHOT_THUMBNAIL = 0,
         SCREENSHOT_WHOLE_PAGE = 1,
         SCREENSHOT_UPDATE = 2
     };
 
-    nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstY, PRInt32 dstX, PRInt32 dstW, PRInt32 dstH, PRInt32 bufW, PRInt32 bufH, PRInt32 tabId, PRInt32 token, jobject buffer);
+    nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token);
 
     static void NotifyPaintedRect(float top, float left, float bottom, float right);
 
     void AcknowledgeEventSync();
 
     void EnableLocation(bool aEnable);
     void EnableLocationHighAccuracy(bool aEnable);
 
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -37,17 +37,16 @@ jfieldID AndroidGeckoEvent::jPointerInde
 jfieldID AndroidGeckoEvent::jRangeTypeField = 0;
 jfieldID AndroidGeckoEvent::jRangeStylesField = 0;
 jfieldID AndroidGeckoEvent::jRangeForeColorField = 0;
 jfieldID AndroidGeckoEvent::jRangeBackColorField = 0;
 jfieldID AndroidGeckoEvent::jLocationField = 0;
 jfieldID AndroidGeckoEvent::jBandwidthField = 0;
 jfieldID AndroidGeckoEvent::jCanBeMeteredField = 0;
 jfieldID AndroidGeckoEvent::jScreenOrientationField = 0;
-jfieldID AndroidGeckoEvent::jByteBufferField = 0;
 
 jclass AndroidPoint::jPointClass = 0;
 jfieldID AndroidPoint::jXField = 0;
 jfieldID AndroidPoint::jYField = 0;
 
 jclass AndroidRect::jRectClass = 0;
 jfieldID AndroidRect::jBottomField = 0;
 jfieldID AndroidRect::jLeftField = 0;
@@ -99,22 +98,16 @@ jmethodID AndroidGeckoSurfaceView::jGetH
     (jClass = jclass(jEnv->NewGlobalRef(jEnv->FindClass(cname))))
 
 #define getField(fname, ftype) \
     ((jfieldID) jEnv->GetFieldID(jClass, fname, ftype))
 
 #define getMethod(fname, ftype) \
     ((jmethodID) jEnv->GetMethodID(jClass, fname, ftype))
 
-RefCountedJavaObject::~RefCountedJavaObject() {
-    if (mObject)
-        GetJNIForThread()->DeleteGlobalRef(mObject);
-    mObject = NULL;
-}
-
 void
 mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
 {
     AndroidGeckoEvent::InitGeckoEventClass(jEnv);
     AndroidPoint::InitPointClass(jEnv);
     AndroidLocation::InitLocationClass(jEnv);
     AndroidRect::InitRectClass(jEnv);
     AndroidGeckoLayerClient::InitGeckoLayerClientClass(jEnv);
@@ -156,17 +149,16 @@ AndroidGeckoEvent::InitGeckoEventClass(J
     jRangeTypeField = getField("mRangeType", "I");
     jRangeStylesField = getField("mRangeStyles", "I");
     jRangeForeColorField = getField("mRangeForeColor", "I");
     jRangeBackColorField = getField("mRangeBackColor", "I");
     jLocationField = getField("mLocation", "Landroid/location/Location;");
     jBandwidthField = getField("mBandwidth", "D");
     jCanBeMeteredField = getField("mCanBeMetered", "Z");
     jScreenOrientationField = getField("mScreenOrientation", "S");
-    jByteBufferField = getField("mBuffer", "Ljava/nio/ByteBuffer;");
 }
 
 void
 AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
 {
 #ifndef MOZ_JAVA_COMPOSITOR
     initInit();
 
@@ -501,18 +493,17 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
         case ACTIVITY_RESUMING: {
             mFlags = jenv->GetIntField(jobj, jFlagsField);
             break;
         }
 
         case SCREENSHOT: {
             mMetaState = jenv->GetIntField(jobj, jMetaStateField);
             mFlags = jenv->GetIntField(jobj, jFlagsField);
-            ReadPointArray(mPoints, jenv, jPoints, 5);
-            mByteBuffer = new RefCountedJavaObject(jenv, jenv->GetObjectField(jobj, jByteBufferField));
+            ReadPointArray(mPoints, jenv, jPoints, 4);
             break;
         }
 
         case PAINT_LISTEN_START_EVENT: {
             mMetaState = jenv->GetIntField(jobj, jMetaStateField);
             break;
         }
 
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -36,37 +36,16 @@ void InitAndroidJavaWrappers(JNIEnv *jEn
  * Note: do not store global refs to any WrappedJavaObject;
  * these are live only during a particular JNI method, as
  * NewGlobalRef is -not- called on the jobject.
  *
  * If this is needed, WrappedJavaObject can be extended to
  * handle it.
  */
 
-class RefCountedJavaObject {
-public:
-    RefCountedJavaObject(JNIEnv* env, jobject obj) : mObject(env->NewGlobalRef(obj)) {}
-
-    ~RefCountedJavaObject();
-
-    PRInt32 AddRef() { return ++mRefCnt; }
-
-    PRInt32 Release() {
-        PRInt32 refcnt = --mRefCnt;
-        if (refcnt == 0)
-            delete this;
-        return refcnt;
-    }
-
-    jobject GetObject() { return mObject; }
-private:
-    PRInt32 mRefCnt;
-    jobject mObject;
-};
-
 class WrappedJavaObject {
 public:
     WrappedJavaObject() :
         wrapped_obj(0)
     { }
 
     WrappedJavaObject(jobject jobj) {
         Init(jobj);
@@ -592,17 +571,16 @@ public:
     int RangeType() { return mRangeType; }
     int RangeStyles() { return mRangeStyles; }
     int RangeForeColor() { return mRangeForeColor; }
     int RangeBackColor() { return mRangeBackColor; }
     nsGeoPosition* GeoPosition() { return mGeoPosition; }
     double Bandwidth() { return mBandwidth; }
     bool CanBeMetered() { return mCanBeMetered; }
     short ScreenOrientation() { return mScreenOrientation; }
-    RefCountedJavaObject* ByteBuffer() { return mByteBuffer; }
 
 protected:
     int mAction;
     int mType;
     int64_t mTime;
     nsTArray<nsIntPoint> mPoints;
     nsTArray<nsIntPoint> mPointRadii;
     nsTArray<int> mPointIndicies;
@@ -617,17 +595,16 @@ protected:
     int mRangeForeColor, mRangeBackColor;
     double mX, mY, mZ;
     int mPointerIndex;
     nsString mCharacters, mCharactersExtra;
     nsRefPtr<nsGeoPosition> mGeoPosition;
     double mBandwidth;
     bool mCanBeMetered;
     short mScreenOrientation;
-    nsRefPtr<RefCountedJavaObject> mByteBuffer;
 
     void ReadIntArray(nsTArray<int> &aVals,
                       JNIEnv *jenv,
                       jfieldID field,
                       PRUint32 count);
     void ReadFloatArray(nsTArray<float> &aVals,
                         JNIEnv *jenv,
                         jfieldID field,
@@ -671,17 +648,16 @@ protected:
     static jfieldID jRangeForeColorField;
     static jfieldID jRangeBackColorField;
     static jfieldID jLocationField;
 
     static jfieldID jBandwidthField;
     static jfieldID jCanBeMeteredField;
 
     static jfieldID jScreenOrientationField;
-    static jfieldID jByteBufferField;
 
 public:
     enum {
         NATIVE_POKE = 0,
         KEY_EVENT = 1,
         MOTION_EVENT = 2,
         SENSOR_EVENT = 3,
         UNUSED1_EVENT = 4,
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -1,18 +1,16 @@
 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Make sure the order of included headers
 #include "base/basictypes.h"
 #include "nspr/prtypes.h"
-#include "base/message_loop.h"
-#include "base/task.h"
 
 #include "mozilla/Hal.h"
 #include "nsAppShell.h"
 #include "nsWindow.h"
 #include "nsThreadUtils.h"
 #include "nsICommandLineRunner.h"
 #include "nsIObserverService.h"
 #include "nsIAppStartup.h"
@@ -60,44 +58,16 @@ PRLogModuleInfo *gWidgetLog = nsnull;
 
 nsIGeolocationUpdate *gLocationCallback = nsnull;
 nsAutoPtr<mozilla::AndroidGeckoEvent> gLastSizeChange;
 
 nsAppShell *nsAppShell::gAppShell = nsnull;
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
 
-class ScreenshotRunnable : public nsRunnable {
-public:
-    ScreenshotRunnable(nsIAndroidBrowserApp* aBrowserApp, int aTabId, nsTArray<nsIntPoint>& aPoints, int aToken, RefCountedJavaObject* aBuffer):
-        mBrowserApp(aBrowserApp), mTabId(aTabId), mPoints(aPoints), mToken(aToken), mBuffer(aBuffer) {}
-
-    virtual nsresult Run() {
-        nsCOMPtr<nsIDOMWindow> domWindow;
-        nsCOMPtr<nsIBrowserTab> tab;
-        mBrowserApp->GetBrowserTab(mTabId, getter_AddRefs(tab));
-        if (!tab)
-            return NS_OK;
-
-        tab->GetWindow(getter_AddRefs(domWindow));
-        if (!domWindow)
-            return NS_OK;
-
-        NS_ASSERTION(mPoints.Length() == 5, "Screenshot event does not have enough coordinates");
-
-        AndroidBridge::Bridge()->TakeScreenshot(domWindow, mPoints[0].x, mPoints[0].y, mPoints[1].x, mPoints[1].y, mPoints[2].x, mPoints[2].y, mPoints[3].x, mPoints[3].y, mPoints[4].x, mPoints[4].y, mTabId, mToken, mBuffer->GetObject());
-        return NS_OK;
-    }
-private:
-    nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
-    nsTArray<nsIntPoint> mPoints;
-    int mTabId, mToken;
-    nsRefPtr<RefCountedJavaObject> mBuffer;
-};
-
 class AfterPaintListener : public nsIDOMEventListener {
   public:
     NS_DECL_ISUPPORTS
 
     void Register(nsIDOMWindow* window) {
         if (mEventTarget)
             Unregister();
         nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
@@ -114,25 +84,33 @@ class AfterPaintListener : public nsIDOM
         mEventTarget = nsnull;
     }
 
     virtual nsresult HandleEvent(nsIDOMEvent* aEvent) {
         nsCOMPtr<nsIDOMNotifyPaintEvent> paintEvent = do_QueryInterface(aEvent);
         if (!paintEvent)
             return NS_OK;
 
-        nsCOMPtr<nsIDOMClientRect> rect;
-        paintEvent->GetBoundingClientRect(getter_AddRefs(rect));
-        float top, left, bottom, right;
-        rect->GetTop(&top);
-        rect->GetLeft(&left);
-        rect->GetRight(&right);
-        rect->GetBottom(&bottom);
-        __android_log_print(ANDROID_LOG_INFO, "GeckoScreenshot", "rect: %f, %f, %f, %f", top, left, right, bottom);
-        AndroidBridge::NotifyPaintedRect(top, left, bottom, right);
+        nsCOMPtr<nsIDOMClientRectList> rects;
+        paintEvent->GetClientRects(getter_AddRefs(rects));
+        if (!rects)
+            return NS_OK;
+        PRUint32 length;
+        rects->GetLength(&length);
+        for (PRUint32 i = 0; i < length; ++i) {
+            float top, left, bottom, right;
+            nsCOMPtr<nsIDOMClientRect> rect = rects->GetItemAt(i);
+            if (!rect)
+                continue;
+            rect->GetTop(&top);
+            rect->GetLeft(&left);
+            rect->GetRight(&right);
+            rect->GetBottom(&bottom);
+            AndroidBridge::NotifyPaintedRect(top, left, bottom, right);
+        }
         return NS_OK;
     }
 
     ~AfterPaintListener() {
         if (mEventTarget)
             Unregister();
     }
 
@@ -442,23 +420,31 @@ nsAppShell::ProcessNextNativeEvent(bool 
         if (!mBrowserApp)
             break;
 
         AndroidBridge* bridge = AndroidBridge::Bridge();
         if (!bridge)
             break;
 
         PRInt32 token = curEvent->Flags();
-        PRInt32 tabId = curEvent->MetaState();
+
+        nsCOMPtr<nsIDOMWindow> domWindow;
+        nsCOMPtr<nsIBrowserTab> tab;
+        mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab));
+        if (!tab)
+            break;
+
+        tab->GetWindow(getter_AddRefs(domWindow));
+        if (!domWindow)
+            break;
+
+        float scale = 1.0;
         nsTArray<nsIntPoint> points = curEvent->Points();
-        RefCountedJavaObject* buffer = curEvent->ByteBuffer();
-        nsCOMPtr<ScreenshotRunnable> sr = 
-            new ScreenshotRunnable(mBrowserApp, tabId, points, token, buffer);
-        MessageLoop::current()->PostIdleTask(
-            FROM_HERE, NewRunnableMethod(sr.get(), &ScreenshotRunnable::Run));
+        NS_ASSERTION(points.Length() == 4, "Screenshot event does not have enough coordinates");
+        bridge->TakeScreenshot(domWindow, points[0].x, points[0].y, points[1].x, points[1].y, points[3].x, points[3].y, curEvent->MetaState(), scale, curEvent->Flags());
         break;
     }
 
     case AndroidGeckoEvent::VIEWPORT:
     case AndroidGeckoEvent::BROADCAST: {
 
         if (curEvent->Characters().Length() == 0)
             break;