bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding, only take one screenshot at a time r=kats
authorBrad Lassey <blassey@mozilla.com>
Thu, 17 May 2012 17:49:40 +0100
changeset 95750 6cf625ab6acde3b172a53c3fb7055d003cda18e1
parent 95749 12809c23e838dd1e332ddda38a003a7f2d1f0e2b
child 95751 de42c0dc441350ad992ecc0e338228bbb2244d5e
push idunknown
push userunknown
push dateunknown
reviewerskats
bugs755070
milestone15.0a1
bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding, only take one screenshot at a time r=kats
mobile/android/base/GeckoAppShell.java
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -93,21 +93,16 @@ public class GeckoAppShell
     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
@@ -123,18 +118,16 @@ 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);
@@ -530,52 +523,17 @@ 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) {
-        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);
-                }
-            }
-        });
+        ScreenshotHandler.notifyScreenShot(data, tabId, x, y, width, height, token);
     }
 
     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());
@@ -2131,16 +2089,34 @@ 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 {
+    static Set<PendingScreenshot> sPendingScreenshots = new HashSet<PendingScreenshot>();
+    private static RectF sCheckerboardPageRect;
+    private static float sLastCheckerboardWidthRatio, sLastCheckerboardHeightRatio;
+    private static RepaintRunnable sRepaintRunnable = new RepaintRunnable();
+    static private int sMaxTextureSize = 0;
+    static final String LOGTAG = "GeckoScreenshot";
+    private static boolean sDisableScreenshot = false;
 
     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;
@@ -2164,24 +2140,23 @@ public class GeckoAppShell
 
         void addRectToRepaint(float top, float left, float bottom, float right) {
             synchronized(this) {
                 mDirtyTop = Math.min(top, mDirtyTop);
                 mDirtyLeft = Math.min(left, mDirtyLeft);
                 mDirtyBottom = Math.max(bottom, mDirtyBottom);
                 mDirtyRight = Math.max(right, mDirtyRight);
                 if (!mIsRepaintRunnablePosted) {
-                    getHandler().postDelayed(this, 5000);
+                    GeckoAppShell.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);
     }
 
@@ -2199,16 +2174,17 @@ public class GeckoAppShell
 
         if (sMaxTextureSize == 0) {
             int[] maxTextureSize = new int[1];
             GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
             sMaxTextureSize = maxTextureSize[0];
             if (sMaxTextureSize == 0)
                 return;
         }
+
         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);
@@ -2224,14 +2200,85 @@ public class GeckoAppShell
         int dy = 0;
         int dw = clamp(minTextureSize, idealDstWidth, sMaxTextureSize);
         int dh = maxPixels / dw;
 
         sLastCheckerboardWidthRatio = dw / sw;
         sLastCheckerboardHeightRatio = dh / sh;
         sCheckerboardPageRect = viewport.getCssPageRect();
 
+        synchronized(sPendingScreenshots) {
+            PendingScreenshot ps = new PendingScreenshot(tab.getId(), GeckoAppShell.SCREENSHOT_WHOLE_PAGE);
+            if (sPendingScreenshots.contains(ps))
+                return;
+            sPendingScreenshots.add(ps);
+        }
         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));
+               (int)FloatMath.ceil(sx), (int)FloatMath.ceil(sy),
+               (int)FloatMath.floor(sw), (int)FloatMath.floor(sh),
+               dx, dy, dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
+    }
+    
+    static class PendingScreenshot {
+        PendingScreenshot(int tabId, int type) {
+            mTabId = tabId;
+            mType = type;
+        }
+
+        public boolean equals(Object obj) {
+            if (!(obj instanceof PendingScreenshot))
+                return false;
+            PendingScreenshot ps = (PendingScreenshot) obj;
+            return ps.mTabId == mTabId && ps.mType == mType;
+        }
+
+        public int hashCode() {
+            return mType + (mTabId << 2);
+        }
+
+        private final int mTabId;
+        private final int mType;
+    }
+
+    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) {
+        synchronized(sPendingScreenshots) {
+            sPendingScreenshots.remove(new PendingScreenshot(tabId, token));
+        }
+
+        GeckoAppShell.getHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    final Tab tab = Tabs.getInstance().getTab(tabId);
+                    if (tab == null)
+                        return;
+
+                    if (!Tabs.getInstance().isSelectedTab(tab) && GeckoAppShell.SCREENSHOT_THUMBNAIL != token)
+                        return;
+
+                    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+                    b.copyPixelsFromBuffer(data);
+                    switch (token) {
+                    case GeckoAppShell.SCREENSHOT_WHOLE_PAGE:
+                        GeckoApp.mAppContext.getLayerController()
+                            .getView().getRenderer()
+                            .setCheckerboardBitmap(b, sCheckerboardPageRect);
+                        break;
+                    case GeckoAppShell.SCREENSHOT_UPDATE:
+                        GeckoApp.mAppContext.getLayerController().getView().getRenderer().
+                            updateCheckerboardBitmap(
+                                b, sLastCheckerboardWidthRatio * x,
+                                sLastCheckerboardHeightRatio * y,
+                                sLastCheckerboardWidthRatio * width,
+                                sLastCheckerboardHeightRatio * height,
+                                sCheckerboardPageRect);
+                        break;
+                    case GeckoAppShell.SCREENSHOT_THUMBNAIL:
+                        GeckoApp.mAppContext.processThumbnail(tab, b, null);
+                        break;
+                    }
+                } finally {
+                    GeckoAppShell.freeDirectBuffer(data);
+                }
+            }
+        });
     }
 }