mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
400 lines
15 KiB
Java
400 lines
15 KiB
Java
#filter substitution
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package @ANDROID_PACKAGE_NAME@;
|
|
|
|
import java.lang.Class;
|
|
import java.lang.ClassLoader;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Proxy;
|
|
import java.lang.reflect.InvocationHandler;
|
|
import java.lang.Long;
|
|
import java.lang.NoSuchMethodException;
|
|
import java.util.concurrent.SynchronousQueue;
|
|
import java.util.ArrayList;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.app.Instrumentation;
|
|
import android.database.Cursor;
|
|
import android.os.SystemClock;
|
|
import android.view.View;
|
|
import android.view.KeyEvent;
|
|
import android.util.Log;
|
|
|
|
import org.json.*;
|
|
|
|
import com.jayway.android.robotium.solo.Solo;
|
|
|
|
import static @ANDROID_PACKAGE_NAME@.FennecNativeDriver.LogLevel;
|
|
|
|
public class FennecNativeActions implements Actions {
|
|
private Solo mSolo;
|
|
private Instrumentation mInstr;
|
|
private Activity mGeckoApp;
|
|
private Assert mAsserter;
|
|
|
|
// Objects for reflexive access of fennec classes.
|
|
private ClassLoader mClassLoader;
|
|
private Class mGel;
|
|
private Class mGe;
|
|
private Class mGas;
|
|
private Class mDrawListener;
|
|
private Method mRegisterGEL;
|
|
private Method mUnregisterGEL;
|
|
private Method mSendGE;
|
|
private Method mGetLayerClient;
|
|
private Method mSetDrawListener;
|
|
private static final String LOGTAG = "FennecNativeActions";
|
|
|
|
public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation, Assert asserter) {
|
|
mSolo = robocop;
|
|
mInstr = instrumentation;
|
|
mGeckoApp = activity;
|
|
mAsserter = asserter;
|
|
// Set up reflexive access of java classes and methods.
|
|
try {
|
|
mClassLoader = activity.getClassLoader();
|
|
mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
|
|
mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent");
|
|
mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
|
|
Class [] parameters = new Class[2];
|
|
parameters[0] = String.class;
|
|
parameters[1] = mGel;
|
|
mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters);
|
|
mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters);
|
|
parameters = new Class[1];
|
|
parameters[0] = mGe;
|
|
mSendGE = mGas.getMethod("sendEventToGecko", parameters);
|
|
|
|
mGetLayerClient = activity.getClass().getMethod("getLayerClient");
|
|
Class gslc = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoLayerClient");
|
|
mDrawListener = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoLayerClient$DrawListener");
|
|
mSetDrawListener = gslc.getDeclaredMethod("setDrawListener", mDrawListener);
|
|
} catch (ClassNotFoundException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (SecurityException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (NoSuchMethodException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (IllegalArgumentException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
}
|
|
}
|
|
|
|
class wakeInvocationHandler implements InvocationHandler {
|
|
private final GeckoEventExpecter mEventExpecter;
|
|
|
|
public wakeInvocationHandler(GeckoEventExpecter expecter) {
|
|
mEventExpecter = expecter;
|
|
}
|
|
|
|
public Object invoke(Object proxy, Method method, Object[] args) {
|
|
String methodName = method.getName();
|
|
//Depending on the method, return a completely different type.
|
|
if(methodName.equals("toString")) {
|
|
return "wakeInvocationHandler";
|
|
}
|
|
if(methodName.equals("equals")) {
|
|
return this == args[0];
|
|
}
|
|
if(methodName.equals("clone")) {
|
|
return this;
|
|
}
|
|
if(methodName.equals("hashCode")) {
|
|
return 314;
|
|
}
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
|
"Waking up on "+methodName);
|
|
mEventExpecter.notifyOfEvent();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class GeckoEventExpecter implements EventExpecter {
|
|
private final String mGeckoEvent;
|
|
private final Object[] mRegistrationParams;
|
|
private boolean mEventReceived;
|
|
private static final int MAX_WAIT_MS = 90000;
|
|
|
|
GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
|
|
mGeckoEvent = geckoEvent;
|
|
mRegistrationParams = registrationParams;
|
|
}
|
|
|
|
public synchronized void blockForEvent() {
|
|
long startTime = SystemClock.uptimeMillis();
|
|
long endTime = 0;
|
|
while (! mEventReceived) {
|
|
try {
|
|
this.wait(MAX_WAIT_MS);
|
|
} catch (InterruptedException ie) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, ie);
|
|
break;
|
|
}
|
|
endTime = SystemClock.uptimeMillis();
|
|
if (!mEventReceived && (endTime - startTime >= MAX_WAIT_MS)) {
|
|
mAsserter.ok(false, "GeckoEventExpecter",
|
|
"blockForEvent timeout: "+mGeckoEvent);
|
|
return;
|
|
}
|
|
}
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
|
"unblocked on expecter for " + mGeckoEvent);
|
|
}
|
|
|
|
public synchronized boolean eventReceived() {
|
|
return mEventReceived;
|
|
}
|
|
|
|
void notifyOfEvent() {
|
|
try {
|
|
mUnregisterGEL.invoke(null, mRegistrationParams);
|
|
} catch (IllegalAccessException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (InvocationTargetException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
}
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
|
"received event " + mGeckoEvent);
|
|
synchronized (this) {
|
|
mEventReceived = true;
|
|
this.notifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
public EventExpecter expectGeckoEvent(String geckoEvent) {
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
|
"waiting for "+geckoEvent);
|
|
try {
|
|
Class [] interfaces = new Class[1];
|
|
interfaces[0] = mGel;
|
|
Object[] finalParams = new Object[2];
|
|
finalParams[0] = geckoEvent;
|
|
|
|
GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
|
|
wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
|
|
Object proxy = Proxy.newProxyInstance(mClassLoader, interfaces, wIH);
|
|
finalParams[1] = proxy;
|
|
mRegisterGEL.invoke(null, finalParams);
|
|
|
|
return expecter;
|
|
} catch (IllegalAccessException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (InvocationTargetException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void sendGeckoEvent(String geckoEvent, String data) {
|
|
try {
|
|
Method cbe = mGe.getMethod("createBroadcastEvent", String.class, String.class);
|
|
Object event = cbe.invoke(null, geckoEvent, data);
|
|
mSendGE.invoke(null, event);
|
|
} catch (NoSuchMethodException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (IllegalAccessException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
} catch (InvocationTargetException e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
}
|
|
}
|
|
|
|
class DrawListenerProxy implements InvocationHandler {
|
|
private final PaintExpecter mPaintExpecter;
|
|
|
|
DrawListenerProxy(PaintExpecter paintExpecter) {
|
|
mPaintExpecter = paintExpecter;
|
|
}
|
|
|
|
public Object invoke(Object proxy, Method method, Object[] args) {
|
|
String methodName = method.getName();
|
|
if ("drawFinished".equals(methodName)) {
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.DEBUG,
|
|
"Received drawFinished notification");
|
|
mPaintExpecter.notifyOfEvent();
|
|
} else if ("toString".equals(methodName)) {
|
|
return "DrawListenerProxy";
|
|
} else if ("equals".equals(methodName)) {
|
|
return false;
|
|
} else if ("hashCode".equals(methodName)) {
|
|
return 0;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class PaintExpecter implements RepeatedEventExpecter {
|
|
private Object mLayerClient;
|
|
private boolean mPaintDone;
|
|
private static final int MAX_WAIT_MS = 90000;
|
|
|
|
PaintExpecter() throws IllegalAccessException, InvocationTargetException {
|
|
mLayerClient = mGetLayerClient.invoke(mGeckoApp);
|
|
mSetDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListener }, new DrawListenerProxy(this)));
|
|
}
|
|
|
|
void notifyOfEvent() {
|
|
synchronized (this) {
|
|
mPaintDone = true;
|
|
this.notifyAll();
|
|
}
|
|
}
|
|
|
|
public synchronized void blockForEvent() {
|
|
long startTime = SystemClock.uptimeMillis();
|
|
long endTime = 0;
|
|
while (!mPaintDone) {
|
|
try {
|
|
this.wait(MAX_WAIT_MS);
|
|
} catch (InterruptedException ie) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, ie);
|
|
break;
|
|
}
|
|
endTime = SystemClock.uptimeMillis();
|
|
if (!mPaintDone && (endTime - startTime >= MAX_WAIT_MS)) {
|
|
mAsserter.ok(false, "PaintExpecter", "blockForEvent timeout");
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
mSetDrawListener.invoke(mLayerClient, (Object)null);
|
|
} catch (Exception e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
}
|
|
}
|
|
|
|
public synchronized boolean eventReceived() {
|
|
return mPaintDone;
|
|
}
|
|
|
|
public synchronized void blockUntilClear(long millis) {
|
|
if (millis <= 0) {
|
|
throw new IllegalArgumentException("millis must be > 0");
|
|
}
|
|
// wait for at least one event
|
|
long startTime = SystemClock.uptimeMillis();
|
|
long endTime = 0;
|
|
while (!mPaintDone) {
|
|
try {
|
|
this.wait(MAX_WAIT_MS);
|
|
} catch (InterruptedException ie) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, ie);
|
|
break;
|
|
}
|
|
endTime = SystemClock.uptimeMillis();
|
|
if (!mPaintDone && (endTime - startTime >= MAX_WAIT_MS)) {
|
|
mAsserter.ok(false, "PaintExpecter", "blockUtilClear timeout");
|
|
return;
|
|
}
|
|
}
|
|
// now wait for a period of millis where we don't get an event
|
|
startTime = SystemClock.uptimeMillis();
|
|
while (true) {
|
|
try {
|
|
this.wait(millis);
|
|
} catch (InterruptedException ie) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, ie);
|
|
break;
|
|
}
|
|
endTime = SystemClock.uptimeMillis();
|
|
if (endTime - startTime >= millis) {
|
|
// success
|
|
break;
|
|
}
|
|
// we got a notify() before we could wait long enough, so we need to start over
|
|
startTime = endTime;
|
|
}
|
|
try {
|
|
mSetDrawListener.invoke(mLayerClient, (Object)null);
|
|
} catch (Exception e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public RepeatedEventExpecter expectPaint() {
|
|
try {
|
|
return new PaintExpecter();
|
|
} catch (Exception e) {
|
|
FennecNativeDriver.log(LogLevel.ERROR, e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void sendSpecialKey(SpecialKey button) {
|
|
switch(button) {
|
|
case DOWN:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
|
|
break;
|
|
case UP:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
|
|
break;
|
|
case LEFT:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
|
|
break;
|
|
case RIGHT:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
|
|
break;
|
|
case ENTER:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
|
|
break;
|
|
case MENU:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
|
|
break;
|
|
case BACK:
|
|
mInstr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void sendKeys(String input) {
|
|
mInstr.sendStringSync(input);
|
|
}
|
|
|
|
public void drag(int startingX, int endingX, int startingY, int endingY) {
|
|
mSolo.drag(startingX, endingX, startingY, endingY, 10);
|
|
}
|
|
|
|
public Cursor querySql(String dbPath, String sql) {
|
|
try {
|
|
ClassLoader classLoader = mGeckoApp.getClassLoader();
|
|
Class sqlClass = classLoader.loadClass("org.mozilla.gecko.sqlite.SQLiteBridge");
|
|
Class stringClass = String.class;
|
|
Class stringArrayClass = String[].class;
|
|
Class appshell = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
|
|
Class contextClass = Context.class;
|
|
|
|
Constructor bridgeConstructor = sqlClass.getConstructor(stringClass);
|
|
Method query = sqlClass.getMethod("rawQuery", stringClass, stringArrayClass);
|
|
Method loadSQLiteLibs = appshell.getMethod("loadSQLiteLibs", contextClass, stringClass);
|
|
|
|
Object bridge = bridgeConstructor.newInstance(dbPath);
|
|
|
|
String resourcePath = mGeckoApp.getApplication().getPackageResourcePath();
|
|
loadSQLiteLibs.invoke(null, mGeckoApp, resourcePath);
|
|
return (Cursor)query.invoke(bridge, sql, null);
|
|
} catch(ClassNotFoundException ex) {
|
|
Log.e(LOGTAG, "Error getting class", ex);
|
|
} catch(NoSuchMethodException ex) {
|
|
Log.e(LOGTAG, "Error getting method", ex);
|
|
} catch(InvocationTargetException ex) {
|
|
Log.e(LOGTAG, "Error invoking method", ex);
|
|
} catch(InstantiationException ex) {
|
|
Log.e(LOGTAG, "Error calling constructor", ex);
|
|
} catch(IllegalAccessException ex) {
|
|
Log.e(LOGTAG, "Error using field", ex);
|
|
}
|
|
return null;
|
|
}
|
|
}
|