bug 755070 - Scrolling causes after paint notifications which causes screenshotting which causes checkerboarding, chunk up large updates as well r=kats

--HG--
extra : rebase_source : a5e6c489fad35479a833216c30ac8e8a3ff17058
This commit is contained in:
Brad Lassey 2012-06-01 18:54:48 -04:00
Родитель 722da9452b 22103c8e9b
Коммит 5300717c62
15 изменённых файлов: 312 добавлений и 114 удалений

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

@ -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

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

@ -688,7 +688,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.*;
@ -83,8 +84,7 @@ 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 RESTORE_NONE = 0;
static public final int RESTORE_OOM = 1;
@ -98,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>>();
@ -128,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
@ -535,42 +528,7 @@ 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);
}
}
});
ScreenshotHandler.notifyScreenShot(data, tabId, x, y, width, height, token);
}
private static CountDownLatch sGeckoPendingAcks = null;
@ -2136,6 +2094,26 @@ 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);
}
}
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;
@ -2158,25 +2136,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);
}
@ -2203,7 +2196,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
@ -2224,14 +2219,96 @@ 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();
scheduleCheckerboardScreenshotEvent(tab.getId(), (int)sx, (int)sy, (int)sw, (int)sh, dx, dy, dw, dh, dw, dh);
}
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));
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;
}
}
}
});
}
}

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

@ -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;
@ -64,6 +65,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;
@ -138,6 +141,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;
@ -159,13 +187,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;

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

@ -76,8 +76,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 implements GLSurfaceView.Renderer {
" 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) {
mCheckerboardLayer.setBitmap(data, width, height);
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) {
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());
@ -116,7 +130,17 @@ public class ScreenshotLayer extends SingleTileLayer {
}
}
void setBitmap(Bitmap bitmap, int width, int height, int format) {
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) {
Bitmap tmp;
mSize = new IntSize(width, height);
mFormat = format;
@ -138,10 +162,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; }
}
}

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

@ -2348,9 +2348,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) {
@ -2415,21 +2419,17 @@ 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, srcX * dstW / srcW , srcY * dstH / srcH, 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) : 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"
@ -63,6 +65,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
@ -89,23 +119,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;
}
@ -425,21 +447,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;
}