diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index 38d6d4a606ea..d718661a5f98 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -11,9 +11,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.ContentResolver; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Configuration; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; @@ -28,31 +26,21 @@ import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; import android.view.LayoutInflater; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; -import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; -import android.widget.ExpandableListView; import android.widget.ImageButton; import android.widget.ListView; import android.widget.TabWidget; import android.widget.Toast; import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Map; -import org.mozilla.gecko.db.BrowserContract.Bookmarks; -import org.mozilla.gecko.db.BrowserContract.Combined; -import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.db.BrowserDB; -import org.json.JSONObject; - public class AwesomeBar extends GeckoActivity { private static final String LOGTAG = "GeckoAwesomeBar"; @@ -61,7 +49,8 @@ public class AwesomeBar extends GeckoActivity { static final String TARGET_KEY = "target"; static final String SEARCH_KEY = "search"; static final String USER_ENTERED_KEY = "user_entered"; - static enum Target { NEW_TAB, CURRENT_TAB }; + + enum Target { NEW_TAB, CURRENT_TAB }; private String mTarget; private AwesomeBarTabs mAwesomeTabs; @@ -590,5 +579,4 @@ public class AwesomeBar extends GeckoActivity { } return false; } - } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 3332710bf79e..0c0523904c18 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -13,16 +13,12 @@ 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.net.URL; -import java.net.MalformedURLException; import java.text.*; import java.util.*; import java.util.zip.*; @@ -54,7 +50,6 @@ import android.net.NetworkInfo; import android.graphics.Bitmap; import android.graphics.drawable.*; -import org.json.JSONArray; import org.json.JSONObject; public class GeckoAppShell @@ -95,7 +90,6 @@ 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; @@ -721,7 +715,7 @@ public class GeckoAppShell // Native Fennec doesn't care because the Java code already knows the selection indexes. } - static void onXreExit() { + public static void onXreExit() { // mLaunchState can only be Launched or GeckoRunning at this point GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoExiting); Log.i(LOGTAG, "XRE exited"); @@ -736,7 +730,7 @@ public class GeckoAppShell System.exit(0); } - static void scheduleRestart() { + public static void scheduleRestart() { Log.i(LOGTAG, "scheduling restart"); gRestartScheduled = true; } @@ -761,7 +755,7 @@ public class GeckoAppShell // "Installs" an application by creating a shortcut // This is the entry point from AndroidBridge.h - static void createShortcut(String aTitle, String aURI, String aIconData, String aType) { + public static void createShortcut(String aTitle, String aURI, String aIconData, String aType) { if ("webapp".equals(aType)) { Log.e(LOGTAG, "createShortcut with no unique URI should not be used for aType = webapp!"); } @@ -777,7 +771,8 @@ public class GeckoAppShell } // internal, for webapps - static void createShortcut(String aTitle, String aURI, String aUniqueURI, String aIconData, String aType) { + private static void createShortcut(String aTitle, String aURI, String aUniqueURI, + String aIconData, String aType) { byte[] raw = Base64.decode(aIconData.substring(22), Base64.DEFAULT); Bitmap bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length); createShortcut(aTitle, aURI, aUniqueURI, bitmap, aType); @@ -989,14 +984,14 @@ public class GeckoAppShell return bitmap; } - static String[] getHandlersForMimeType(String aMimeType, String aAction) { + public static String[] getHandlersForMimeType(String aMimeType, String aAction) { Intent intent = getIntentForActionString(aAction); if (aMimeType != null && aMimeType.length() > 0) intent.setType(aMimeType); return getHandlersForIntent(intent); } - static String[] getHandlersForURL(String aURL, String aAction) { + public static String[] getHandlersForURL(String aURL, String aAction) { // aURL may contain the whole URL or just the protocol Uri uri = aURL.indexOf(':') >= 0 ? Uri.parse(aURL) : new Uri.Builder().scheme(aURL).build(); Intent intent = getIntentForActionString(aAction); @@ -1004,7 +999,7 @@ public class GeckoAppShell return getHandlersForIntent(intent); } - static String[] getHandlersForIntent(Intent intent) { + private static String[] getHandlersForIntent(Intent intent) { PackageManager pm = GeckoApp.mAppContext.getPackageManager(); List list = pm.queryIntentActivities(intent, 0); int numAttr = 4; @@ -1022,7 +1017,7 @@ public class GeckoAppShell return ret; } - static Intent getIntentForActionString(String aAction) { + private static Intent getIntentForActionString(String aAction) { // Default to the view action if no other action as been specified. if (aAction != null && aAction.length() > 0) return new Intent(aAction); @@ -1030,11 +1025,11 @@ public class GeckoAppShell return new Intent(Intent.ACTION_VIEW); } - static String getExtensionFromMimeType(String aMimeType) { + public static String getExtensionFromMimeType(String aMimeType) { return MimeTypeMap.getSingleton().getExtensionFromMimeType(aMimeType); } - static String getMimeTypeFromExtensions(String aFileExt) { + public static String getMimeTypeFromExtensions(String aFileExt) { MimeTypeMap mtm = MimeTypeMap.getSingleton(); StringTokenizer st = new StringTokenizer(aFileExt, "., "); String type = null; @@ -1132,8 +1127,8 @@ public class GeckoAppShell GeckoApp.mAppContext.getResources().getString(R.string.share_title))); } - static boolean openUriExternal(String aUriSpec, String aMimeType, String aPackageName, - String aClassName, String aAction, String aTitle) { + public static boolean openUriExternal(String aUriSpec, String aMimeType, String aPackageName, + String aClassName, String aAction, String aTitle) { Intent intent = getIntentForActionString(aAction); if (aAction.equalsIgnoreCase(Intent.ACTION_SEND)) { Intent shareIntent = getIntentForActionString(aAction); @@ -1186,9 +1181,8 @@ public class GeckoAppShell } } - static SynchronousQueue sClipboardQueue = - new SynchronousQueue(); - private static String EMPTY_STRING = new String(); + private static final SynchronousQueue sClipboardQueue = new SynchronousQueue(); + private static final String EMPTY_STRING = new String(); // On some devices, access to the clipboard service needs to happen // on a thread with a looper, so dispatch this to our looper thread @@ -1196,7 +1190,6 @@ public class GeckoAppShell // gecko thread, which is most likely this thread static String getClipboardText() { getHandler().post(new Runnable() { - @SuppressWarnings("deprecation") public void run() { Context context = GeckoApp.mAppContext; String text = null; @@ -1229,7 +1222,6 @@ public class GeckoAppShell static void setClipboardText(final String text) { getHandler().post(new Runnable() { - @SuppressWarnings("deprecation") public void run() { Context context = GeckoApp.mAppContext; if (android.os.Build.VERSION.SDK_INT >= 11) { @@ -1564,10 +1556,10 @@ public class GeckoAppShell boolean callback(int pid); } - static int sPidColumn = -1; - static int sUserColumn = -1; - private static void EnumerateGeckoProcesses(GeckoProcessesVisitor visiter) { + private static int sPidColumn = -1; + private static int sUserColumn = -1; + private static void EnumerateGeckoProcesses(GeckoProcessesVisitor visiter) { try { // run ps and parse its output @@ -1763,9 +1755,9 @@ public class GeckoAppShell return null; } - static native void executeNextRunnable(); + private static native void executeNextRunnable(); - static class GeckoRunnableCallback implements Runnable { + private static final class GeckoRunnableCallback implements Runnable { public void run() { Log.i(LOGTAG, "run GeckoRunnableCallback"); GeckoAppShell.executeNextRunnable(); @@ -1776,15 +1768,15 @@ public class GeckoAppShell Log.i(LOGTAG, "post to " + (mainThread ? "main " : "") + "java thread"); getMainHandler().post(new GeckoRunnableCallback()); } - - public static android.hardware.Camera sCamera = null; - - static native void cameraCallbackBridge(byte[] data); - static int kPreferedFps = 25; - static byte[] sCameraBuffer = null; + private static android.hardware.Camera sCamera; - static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) { + private static native void cameraCallbackBridge(byte[] data); + + private static final int kPreferedFps = 25; + private static byte[] sCameraBuffer; + + public static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) { Log.i(LOGTAG, "initCamera(" + aContentType + ", " + aWidth + "x" + aHeight + ") on thread " + Thread.currentThread().getId()); getMainHandler().post(new Runnable() { @@ -1880,7 +1872,7 @@ public class GeckoAppShell return result; } - static synchronized void closeCamera() { + public static synchronized void closeCamera() { Log.i(LOGTAG, "closeCamera() on thread " + Thread.currentThread().getId()); getMainHandler().post(new Runnable() { public void run() { @@ -1920,7 +1912,8 @@ public class GeckoAppShell } } - static SynchronousQueue sTracerQueue = new SynchronousQueue(); + private static final SynchronousQueue sTracerQueue = new SynchronousQueue(); + public static void fireAndWaitForTracerEvent() { getMainHandler().post(new Runnable() { public void run() { @@ -2017,11 +2010,11 @@ public class GeckoAppShell return GeckoBatteryManager.getCurrentInformation(); } - static void checkUriVisited(String uri) { // invoked from native JNI code + public static void checkUriVisited(String uri) { // invoked from native JNI code GlobalHistory.getInstance().checkUriVisited(uri); } - static void markUriVisited(final String uri) { // invoked from native JNI code + public static void markUriVisited(final String uri) { // invoked from native JNI code getHandler().post(new Runnable() { public void run() { GlobalHistory.getInstance().add(uri); @@ -2029,7 +2022,7 @@ public class GeckoAppShell }); } - static void hideProgressDialog() { + public static void hideProgressDialog() { // unused stub } @@ -2263,7 +2256,7 @@ public class GeckoAppShell msg.recycle(); } - static class AsyncResultHandler extends FilePickerResultHandler { + private static final class AsyncResultHandler extends FilePickerResultHandler { private long mId; AsyncResultHandler(long id) { super(null); @@ -2273,7 +2266,6 @@ public class GeckoAppShell public void onActivityResult(int resultCode, Intent data) { GeckoAppShell.notifyFilePickerResult(handleActivityResult(resultCode, data), mId); } - } static native void notifyFilePickerResult(String filePath, long id); @@ -2304,251 +2296,253 @@ public class GeckoAppShell return data; } -} + private static final class ScreenshotHandler { + private static Queue sPendingScreenshots = new LinkedList(); + 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; + private static Rect sAcumulatedRect = new Rect(); -class ScreenshotHandler { - private static Queue sPendingScreenshots = new LinkedList(); - 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; - private static Rect sAcumulatedRect = new Rect(); + private static final 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; - 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; - // synchronize so we don't try to accumulate more rects while painting the ones we have - synchronized(this) { - top = mDirtyTop; - left = mDirtyLeft; - right = mDirtyRight; - bottom = mDirtyBottom; - // 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; - } + public void run() { + float top, left, bottom, right; + // synchronize so we don't try to accumulate more rects while painting the ones we have + synchronized(this) { + top = mDirtyTop; + left = mDirtyLeft; + right = mDirtyRight; + bottom = mDirtyBottom; + // 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(); - if (tab == null) - return; - LayerController layerController = GeckoApp.mAppContext.getLayerController(); - if (layerController == null) - return; - ImmutableViewportMetrics viewport = layerController.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 - viewport.cssPageRectLeft)), - (int)(sLastCheckerboardHeightRatio * (top - viewport.cssPageRectTop)), - (int)(sLastCheckerboardWidthRatio * width), - (int)(sLastCheckerboardHeightRatio * height), - sCheckerboardBufferWidth, sCheckerboardBufferHeight); - } else { - GeckoAppShell.screenshotWholePage(tab); - } - } - - void addRectToRepaint(float top, float left, float bottom, float right) { - if (sDisableScreenshot || sCheckerboardPageRect == null) { - // if screenshotting is disabled just ignore the rect to repaint. - // if sCheckerboardPageRect is null, we haven't done a full-page - // screenshot yet (or screenshotWholePage failed for some reason), - // so ignore partial updates. - return; - } - 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)); - if (!mIsRepaintRunnablePosted) { - GeckoAppShell.getHandler().postDelayed(this, 5000); - mIsRepaintRunnablePosted = true; + Tab tab = Tabs.getInstance().getSelectedTab(); + if (tab == null) + return; + LayerController layerController = GeckoApp.mAppContext.getLayerController(); + if (layerController == null) + return; + ImmutableViewportMetrics viewport = layerController.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 - viewport.cssPageRectLeft)), + (int)(sLastCheckerboardHeightRatio * (top - viewport.cssPageRectTop)), + (int)(sLastCheckerboardWidthRatio * width), + (int)(sLastCheckerboardHeightRatio * height), + sCheckerboardBufferWidth, sCheckerboardBufferHeight); + } else { + GeckoAppShell.screenshotWholePage(tab); } } - } - } - 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); - } - - // Invoked via reflection from robocop test - public static void disableScreenshot() { - sDisableScreenshot = true; - } - - public static void screenshotWholePage(Tab tab) { - if (sDisableScreenshot) { - return; - } - if (GeckoApp.mAppContext.isApplicationInBackground()) - 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; - if (sw == 0 || sh == 0) - return; - int maxPixels = Math.min(ScreenshotLayer.getMaxNumPixels(), sMaxTextureSize * sMaxTextureSize); - // 2Mb of 16bit image data - // may be bumped by up to 4x for power of 2 alignment - float idealZoomFactor = (float)Math.sqrt((sw * sh) / (maxPixels / 4)); - - // 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); - if (numSlices == 0 || dw == 0 || dh == 0) - return; - 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, - i < numSlices - 1 ? - GeckoAppShell.SCREENSHOT_CHECKERBOARD - : GeckoAppShell.SCREENSHOT_CHECKERBOARD_AND_UPDATE, - 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 left, final int top, - final int right, final int bottom, - final int bufferWidth, final int bufferHeight, 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 || - token == GeckoAppShell.SCREENSHOT_CHECKERBOARD_AND_UPDATE) { - synchronized(sPendingScreenshots) { - sPendingScreenshots.remove(); - sendNextEventToGecko(); - } - } + void addRectToRepaint(float top, float left, float bottom, float right) { + if (sDisableScreenshot || sCheckerboardPageRect == null) { + // if screenshotting is disabled just ignore the rect to repaint. + // if sCheckerboardPageRect is null, we haven't done a full-page + // screenshot yet (or screenshotWholePage failed for some reason), + // so ignore partial updates. return; } - - switch (token) { - case GeckoAppShell.SCREENSHOT_CHECKERBOARD: - { - // screenshots are always processed sequentially, with the last one - // marked with the SCREENSHOT_CHECKERBOARD_AND_UPDATE token - synchronized (sAcumulatedRect) { - sAcumulatedRect.union(left, top, right, bottom); - } - synchronized(sPendingScreenshots) { - sPendingScreenshots.remove(); - sendNextEventToGecko(); - } - break; - } - case GeckoAppShell.SCREENSHOT_CHECKERBOARD_AND_UPDATE: - { - final Rect rect; - synchronized (sAcumulatedRect) { - sAcumulatedRect.union(left, top, right, bottom); - rect = new Rect(sAcumulatedRect); - sAcumulatedRect.setEmpty(); - } - GeckoApp.mAppContext.getLayerController() - .getView().getRenderer() - .setCheckerboardBitmap(data, bufferWidth, bufferHeight, sCheckerboardPageRect, rect); - synchronized(sPendingScreenshots) { - sPendingScreenshots.remove(); - sendNextEventToGecko(); - } - break; - } - case GeckoAppShell.SCREENSHOT_THUMBNAIL: - { - GeckoApp.mAppContext.handleThumbnailData(tab, data); - break; + 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)); + if (!mIsRepaintRunnablePosted) { + GeckoAppShell.getHandler().postDelayed(this, 5000); + mIsRepaintRunnablePosted = true; } } } - }); + } + + 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); + } + + // Invoked via reflection from robocop test + public static void disableScreenshot() { + sDisableScreenshot = true; + } + + public static void screenshotWholePage(Tab tab) { + if (sDisableScreenshot) { + return; + } + if (GeckoApp.mAppContext.isApplicationInBackground()) + 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; + if (sw == 0 || sh == 0) + return; + int maxPixels = Math.min(ScreenshotLayer.getMaxNumPixels(), sMaxTextureSize * sMaxTextureSize); + // 2Mb of 16bit image data + // may be bumped by up to 4x for power of 2 alignment + float idealZoomFactor = (float)Math.sqrt((sw * sh) / (maxPixels / 4)); + + // 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); + } + + private 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); + if (numSlices == 0 || dw == 0 || dh == 0) + return; + 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, + i < numSlices - 1 ? + GeckoAppShell.SCREENSHOT_CHECKERBOARD + : GeckoAppShell.SCREENSHOT_CHECKERBOARD_AND_UPDATE, + sWholePageScreenshotBuffer); + synchronized(sPendingScreenshots) { + sPendingScreenshots.add(new PendingScreenshot(tabId, event)); + if (sPendingScreenshots.size() == 1) + sendNextEventToGecko(); + } + } + } + + private static void sendNextEventToGecko() { + synchronized(sPendingScreenshots) { + if (sPendingScreenshots.isEmpty()) + return; + GeckoAppShell.sendEventToGecko(sPendingScreenshots.element().getEvent()); + } + } + + private static final 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 left, final int top, + final int right, final int bottom, + final int bufferWidth, final int bufferHeight, 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 || + token == GeckoAppShell.SCREENSHOT_CHECKERBOARD_AND_UPDATE) { + synchronized(sPendingScreenshots) { + sPendingScreenshots.remove(); + sendNextEventToGecko(); + } + } + return; + } + + switch (token) { + case GeckoAppShell.SCREENSHOT_CHECKERBOARD: + { + // screenshots are always processed sequentially, with the last one + // marked with the SCREENSHOT_CHECKERBOARD_AND_UPDATE token + synchronized (sAcumulatedRect) { + sAcumulatedRect.union(left, top, right, bottom); + } + synchronized(sPendingScreenshots) { + sPendingScreenshots.remove(); + sendNextEventToGecko(); + } + break; + } + case GeckoAppShell.SCREENSHOT_CHECKERBOARD_AND_UPDATE: + { + final Rect rect; + synchronized (sAcumulatedRect) { + sAcumulatedRect.union(left, top, right, bottom); + rect = new Rect(sAcumulatedRect); + sAcumulatedRect.setEmpty(); + } + GeckoApp.mAppContext.getLayerController() + .getView().getRenderer() + .setCheckerboardBitmap(data, bufferWidth, bufferHeight, sCheckerboardPageRect, rect); + synchronized(sPendingScreenshots) { + sPendingScreenshots.remove(); + sendNextEventToGecko(); + } + break; + } + case GeckoAppShell.SCREENSHOT_THUMBNAIL: + { + GeckoApp.mAppContext.handleThumbnailData(tab, data); + break; + } + } + } + }); + } } } diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index caf7f64d11da..5d4b781c19f2 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -5,17 +5,14 @@ package org.mozilla.gecko.gfx; -import android.util.Log; import android.view.SurfaceHolder; -import android.view.SurfaceView; + import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; -import javax.microedition.khronos.opengles.GL; -import javax.microedition.khronos.opengles.GL10; public class GLController { private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; @@ -29,7 +26,7 @@ public class GLController { private EGL10 mEGL; private EGLDisplay mEGLDisplay; private EGLConfig mEGLConfig; - private EGLSurface mEGLSurface; + public EGLSurface mEGLSurface; // accessed from JNI private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4; @@ -167,7 +164,7 @@ public class GLController { * This class does not keep a reference to the provided EGL surface; the * caller assumes ownership of the surface once it is returned. */ - private EGLSurface provideEGLSurface() { + public EGLSurface provideEGLSurface() { if (mEGL == null) { initEGL(); } @@ -186,7 +183,7 @@ public class GLController { return "Error " + mEGL.eglGetError(); } - public static class GLControllerException extends RuntimeException { + private static final class GLControllerException extends RuntimeException { public static final long serialVersionUID = 1L; GLControllerException(String e) { diff --git a/mobile/android/base/gfx/SurfaceTextureLayer.java b/mobile/android/base/gfx/SurfaceTextureLayer.java index 90bf2fc60962..f1a836ab9e14 100644 --- a/mobile/android/base/gfx/SurfaceTextureLayer.java +++ b/mobile/android/base/gfx/SurfaceTextureLayer.java @@ -7,16 +7,13 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.GeckoApp; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.opengl.GLES20; import android.util.Log; import android.view.Surface; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; + import java.nio.FloatBuffer; public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener { @@ -24,7 +21,7 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3) private final SurfaceTexture mSurfaceTexture; - private final Surface mSurface; + public final Surface mSurface; // accessed from JNI private int mTextureId; private boolean mHaveFrame; private float[] mTextureTransform = new float[16];