bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding r=kats,cjones

This commit is contained in:
Brad Lassey 2012-06-14 09:08:51 -07:00
Родитель 0084eaa3e2
Коммит 4bb4023c83
16 изменённых файлов: 360 добавлений и 122 удалений

Просмотреть файл

@ -577,8 +577,10 @@ public class GeckoAppShell
// these 2 stubs are never called in XUL Fennec, but we need them so that
// the JNI code shared between XUL and Native Fennec doesn't die.
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) {
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) {
}
public static void notifyPaintedRect(float top, float left, float bottom, float right) {

Просмотреть файл

@ -256,6 +256,14 @@ void MessageLoop::PostNonNestableDelayedTask(
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,

Просмотреть файл

@ -121,6 +121,10 @@ public:
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

Просмотреть файл

@ -768,7 +768,7 @@ abstract public class GeckoApp
int dw = tab.getThumbnailWidth();
int dh = tab.getThumbnailHeight();
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL));
GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, 0, 0, 0, 0, dw, dh, dw, dh, GeckoAppShell.SCREENSHOT_THUMBNAIL, tab.getThumbnailBuffer()));
}
}

Просмотреть файл

@ -15,6 +15,7 @@ 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.*;
@ -82,8 +83,8 @@ public class GeckoAppShell
public static final String SHORTCUT_TYPE_BOOKMARK = "bookmark";
static public final int SCREENSHOT_THUMBNAIL = 0;
static public final int SCREENSHOT_WHOLE_PAGE = 1;
static public final int SCREENSHOT_UPDATE = 2;
static public final int SCREENSHOT_CHECKERBOARD = 1;
static public final int SCREENSHOT_CHECKERBOARD_AND_UPDATE = 2;
static public final int RESTORE_NONE = 0;
static public final int RESTORE_OOM = 1;
@ -97,11 +98,6 @@ public class GeckoAppShell
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>>();
@ -127,8 +123,6 @@ public class GeckoAppShell
private static Handler sGeckoHandler;
private static boolean sDisableScreenshot = false;
/* The Android-side API: API methods that Android calls */
// Initialization methods
@ -536,44 +530,11 @@ public class GeckoAppShell
}
// 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);
}
}
});
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) {
ScreenshotHandler.notifyScreenShot(data, tabId, left, top, right, bottom, bufferWidth, bufferHeight, token);
}
private static CountDownLatch sGeckoPendingAcks = null;
@ -2145,6 +2106,31 @@ public class GeckoAppShell
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);
}
public static void notifyWakeLockChanged(String topic, String state) {
GeckoApp.mAppContext.notifyWakeLockChanged(topic, state);
}
}
class ScreenshotHandler {
private static Queue<PendingScreenshot> sPendingScreenshots = new LinkedList<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;
private static Rect sAcumulatedRect = new Rect();
static class RepaintRunnable implements Runnable {
private boolean mIsRepaintRunnablePosted = false;
@ -2167,25 +2153,40 @@ public class GeckoAppShell
mIsRepaintRunnablePosted = false;
}
Tab tab = Tabs.getInstance().getSelectedTab();
GeckoAppShell.screenshotWholePage(tab);
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);
}
}
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);
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) {
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);
}
@ -2212,7 +2213,9 @@ public class GeckoAppShell
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
@ -2220,6 +2223,8 @@ public class GeckoAppShell
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
@ -2233,18 +2238,123 @@ public class GeckoAppShell
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();
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));
scheduleCheckerboardScreenshotEvent(tab.getId(), (int)sx, (int)sy, (int)sw, (int)sh, dx, dy, dw, dh, dw, dh);
}
public static void notifyWakeLockChanged(String topic, String state) {
GeckoApp.mAppContext.notifyWakeLockChanged(topic, state);
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)
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();
}
}
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:
{
if (Tabs.getInstance().isSelectedTab(tab)) {
Bitmap b = tab.getThumbnailBitmap();
b.copyPixelsFromBuffer(data);
GeckoApp.mAppContext.processThumbnail(tab, b, null);
}
break;
}
}
}
});
}
}

Просмотреть файл

@ -23,6 +23,7 @@ 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;
@ -116,6 +117,8 @@ public class GeckoEvent {
public short mScreenOrientation;
public ByteBuffer mBuffer;
private GeckoEvent(int evType) {
mType = evType;
}
@ -456,15 +459,17 @@ public class GeckoEvent {
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 token) {
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) {
GeckoEvent event = new GeckoEvent(SCREENSHOT);
event.mPoints = new Point[4];
event.mPoints = new Point[5];
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;
}

Просмотреть файл

@ -21,6 +21,7 @@ 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;
@ -65,6 +66,8 @@ public final class Tab {
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;
@ -140,6 +143,31 @@ public final class Tab {
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;
@ -161,13 +189,10 @@ public final class Tab {
public void run() {
if (b != null) {
try {
Bitmap bitmap = Bitmap.createScaledBitmap(b, getThumbnailWidth(), getThumbnailHeight(), false);
if (mState == Tab.STATE_SUCCESS)
saveThumbnailToDB(new BitmapDrawable(bitmap));
saveThumbnailToDB(new BitmapDrawable(b));
mThumbnail = new BitmapDrawable(bitmap);
b.recycle();
mThumbnail = new BitmapDrawable(b);
} catch (OutOfMemoryError oom) {
Log.e(LOGTAG, "Unable to create/scale bitmap", oom);
mThumbnail = null;

Просмотреть файл

@ -77,8 +77,10 @@ public class Tabs implements GeckoEventListener {
public void removeTab(int id) {
if (tabs.containsKey(id)) {
order.remove(getTab(id));
Tab tab = getTab(id);
order.remove(tab);
tabs.remove(id);
tab.freeBuffer();
Log.i(LOGTAG, "Removed a tab with id: " + id);
}
}

Просмотреть файл

@ -71,5 +71,11 @@ public class IntSize {
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;
}
}

Просмотреть файл

@ -134,8 +134,8 @@ public class LayerRenderer {
" gl_FragColor = texture2D(sTexture, vTexCoord);\n" +
"}\n";
public void setCheckerboardBitmap(Bitmap bitmap, RectF pageRect) {
mCheckerboardLayer.setBitmap(bitmap);
public void setCheckerboardBitmap(ByteBuffer data, int width, int height, RectF pageRect, Rect copyRect) {
mCheckerboardLayer.setBitmap(data, width, height, copyRect);
mCheckerboardLayer.beginTransaction();
try {
mCheckerboardLayer.setPosition(RectUtils.round(pageRect));

Просмотреть файл

@ -35,6 +35,7 @@ public class ScreenshotLayer extends SingleTileLayer {
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;
@ -44,6 +45,19 @@ public class ScreenshotLayer extends SingleTileLayer {
mHasImage = false;
}
void setBitmap(ByteBuffer data, int width, int height, Rect rect) {
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, rect);
} 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());
@ -116,7 +130,21 @@ public class ScreenshotLayer extends SingleTileLayer {
}
}
void setBitmap(Bitmap bitmap, int width, int height, int format) {
void copyBuffer(ByteBuffer src, ByteBuffer dst, Rect rect, int stride) {
int start = rect.left + rect.top * stride;
int end = Math.min(Math.min(rect.right + (rect.bottom - 1) * stride, src.capacity()), dst.limit());
dst.position(start);
src.position(start).limit(end);
dst.put(src);
}
synchronized void setBitmap(ByteBuffer data, int width, int height, int format, Rect rect) {
mSize = new IntSize(width, height);
mFormat = format;
copyBuffer(data.asReadOnlyBuffer(), mBuffer.duplicate(), rect, width * 2);
}
synchronized void setBitmap(Bitmap bitmap, int width, int height, int format) {
Bitmap tmp;
mSize = new IntSize(width, height);
mFormat = format;
@ -138,10 +166,10 @@ public class ScreenshotLayer extends SingleTileLayer {
}
@Override
public ByteBuffer getBuffer() { return mBuffer; }
synchronized public ByteBuffer getBuffer() { return mBuffer; }
@Override
public IntSize getSize() { return mSize; }
synchronized public IntSize getSize() { return mSize; }
@Override
public int getFormat() { return mFormat; }
synchronized public int getFormat() { return mFormat; }
}
}

Просмотреть файл

@ -88,7 +88,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V");
jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;Z)V");
jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V");
jNotifyScreenShot = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyScreenShot", "(Ljava/nio/ByteBuffer;IIIIII)V");
jNotifyScreenShot = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyScreenShot", "(Ljava/nio/ByteBuffer;IIIIIIII)V");
jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V");
jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)V");
@ -2369,9 +2369,13 @@ 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 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token)
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 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) {
@ -2432,21 +2436,18 @@ nsresult AndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt
nsPresContext::CSSPixelsToAppUnits(srcW / scale),
nsPresContext::CSSPixelsToAppUnits(srcH / scale));
PRUint32 stride = dstW * 2;
PRUint32 bufferSize = dstH * stride;
jobject buffer = Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(env, NULL, bufferSize);
if (!buffer)
return NS_OK;
PRUint32 stride = bufW * 2 /* 16 bpp */;
void* data = env->GetDirectBufferAddress(buffer);
memset(data, 0, bufferSize);
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(dstW, dstH), stride, gfxASurface::ImageFormatRGB16_565);
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(bufW, bufH), 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, dstW, dstH, token);
env->CallStaticVoidMethod(AndroidBridge::Bridge()->mGeckoAppShellClass, AndroidBridge::Bridge()->jNotifyScreenShot,
buffer, tabId, dstX, dstY, dstX + dstW, dstY + dstH, bufW, bufH, token);
return NS_OK;
}

Просмотреть файл

@ -155,7 +155,7 @@ public:
SCREENSHOT_UPDATE = 2
};
nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId, float scale, PRInt32 token);
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);
static void NotifyPaintedRect(float top, float left, float bottom, float right);

Просмотреть файл

@ -42,6 +42,7 @@ 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;
@ -103,6 +104,12 @@ jmethodID AndroidGeckoSurfaceView::jGetHolderMethod = 0;
#define getMethod(fname, ftype) \
((jmethodID) jEnv->GetMethodID(jClass, fname, ftype))
RefCountedJavaObject::~RefCountedJavaObject() {
if (mObject)
GetJNIForThread()->DeleteGlobalRef(mObject);
mObject = NULL;
}
void
mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
{
@ -154,6 +161,7 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
jBandwidthField = getField("mBandwidth", "D");
jCanBeMeteredField = getField("mCanBeMetered", "Z");
jScreenOrientationField = getField("mScreenOrientation", "S");
jByteBufferField = getField("mBuffer", "Ljava/nio/ByteBuffer;");
}
void
@ -498,7 +506,8 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
case SCREENSHOT: {
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
mFlags = jenv->GetIntField(jobj, jFlagsField);
ReadPointArray(mPoints, jenv, jPoints, 4);
ReadPointArray(mPoints, jenv, jPoints, 5);
mByteBuffer = new RefCountedJavaObject(jenv, jenv->GetObjectField(jobj, jByteBufferField));
break;
}

Просмотреть файл

@ -41,6 +41,27 @@ void InitAndroidJavaWrappers(JNIEnv *jEnv);
* handle it.
*/
class RefCountedJavaObject {
public:
RefCountedJavaObject(JNIEnv* env, jobject obj) : mRefCnt(0), 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() :
@ -576,6 +597,7 @@ public:
double Bandwidth() { return mBandwidth; }
bool CanBeMetered() { return mCanBeMetered; }
short ScreenOrientation() { return mScreenOrientation; }
RefCountedJavaObject* ByteBuffer() { return mByteBuffer; }
protected:
int mAction;
@ -600,6 +622,7 @@ protected:
double mBandwidth;
bool mCanBeMetered;
short mScreenOrientation;
nsRefPtr<RefCountedJavaObject> mByteBuffer;
void ReadIntArray(nsTArray<int> &aVals,
JNIEnv *jenv,
@ -653,6 +676,7 @@ protected:
static jfieldID jCanBeMeteredField;
static jfieldID jScreenOrientationField;
static jfieldID jByteBufferField;
public:
enum {

Просмотреть файл

@ -6,6 +6,8 @@
// 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"
@ -65,6 +67,34 @@ 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
@ -91,23 +121,15 @@ class AfterPaintListener : public nsIDOMEventListener {
if (!paintEvent)
return NS_OK;
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);
}
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);
return NS_OK;
}
@ -460,21 +482,13 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
break;
PRInt32 token = curEvent->Flags();
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;
PRInt32 tabId = curEvent->MetaState();
nsTArray<nsIntPoint> points = curEvent->Points();
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());
RefCountedJavaObject* buffer = curEvent->ByteBuffer();
nsCOMPtr<ScreenshotRunnable> sr =
new ScreenshotRunnable(mBrowserApp, tabId, points, token, buffer);
MessageLoop::current()->PostIdleTask(
FROM_HERE, NewRunnableMethod(sr.get(), &ScreenshotRunnable::Run));
break;
}