mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
391 lines
14 KiB
Java
391 lines
14 KiB
Java
#filter substitution
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Firefox Mobile Test Framework.
|
|
*
|
|
* The Initial Developer of the Original Code is Mozilla.
|
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Trevor Fairey <tnfairey@gmail.com>
|
|
* David Burns <dburns@mozilla.com>
|
|
* Joel Maher <joel.maher@gmail.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
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.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;
|
|
|
|
public class FennecNativeActions implements Actions {
|
|
private Solo mSolo;
|
|
private Instrumentation mInstr;
|
|
private Activity mGeckoApp;
|
|
|
|
// 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) {
|
|
mSolo = robocop;
|
|
mInstr = instrumentation;
|
|
mGeckoApp = activity;
|
|
// 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("getSoftwareLayerClient");
|
|
Class gslc = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
|
|
mDrawListener = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
|
|
mSetDrawListener = gslc.getDeclaredMethod("setDrawListener", mDrawListener);
|
|
} catch (ClassNotFoundException e) {
|
|
e.printStackTrace();
|
|
} catch (SecurityException e) {
|
|
e.printStackTrace();
|
|
} catch (NoSuchMethodException e) {
|
|
e.printStackTrace();
|
|
} catch (IllegalArgumentException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
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.LOG_LEVEL_DEBUG,
|
|
"Waking up on "+methodName);
|
|
mEventExpecter.notifyOfEvent();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
class GeckoEventExpecter implements EventExpecter {
|
|
private final String mGeckoEvent;
|
|
private final Object[] mRegistrationParams;
|
|
private boolean mEventReceived;
|
|
|
|
GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
|
|
mGeckoEvent = geckoEvent;
|
|
mRegistrationParams = registrationParams;
|
|
}
|
|
|
|
public synchronized void blockForEvent() {
|
|
while (! mEventReceived) {
|
|
try {
|
|
this.wait();
|
|
} catch (InterruptedException ie) {
|
|
ie.printStackTrace();
|
|
break;
|
|
}
|
|
}
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
"unblocked on expecter for " + mGeckoEvent);
|
|
}
|
|
|
|
public synchronized boolean eventReceived() {
|
|
return mEventReceived;
|
|
}
|
|
|
|
void notifyOfEvent() {
|
|
try {
|
|
mUnregisterGEL.invoke(null, mRegistrationParams);
|
|
} catch (IllegalAccessException e) {
|
|
e.printStackTrace();
|
|
} catch (InvocationTargetException e) {
|
|
e.printStackTrace();
|
|
}
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
|
|
"received event " + mGeckoEvent);
|
|
synchronized (this) {
|
|
mEventReceived = true;
|
|
this.notifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
public EventExpecter expectGeckoEvent(String geckoEvent) {
|
|
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_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) {
|
|
e.printStackTrace();
|
|
} catch (InvocationTargetException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
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.LOG_LEVEL_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;
|
|
|
|
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() {
|
|
while (!mPaintDone) {
|
|
try {
|
|
this.wait();
|
|
} catch (InterruptedException ie) {
|
|
ie.printStackTrace();
|
|
break;
|
|
}
|
|
}
|
|
try {
|
|
mSetDrawListener.invoke(mLayerClient, (Object)null);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
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
|
|
while (!mPaintDone) {
|
|
try {
|
|
this.wait();
|
|
} catch (InterruptedException ie) {
|
|
ie.printStackTrace();
|
|
break;
|
|
}
|
|
}
|
|
// now wait for a period of millis where we don't get an event
|
|
long startTime = SystemClock.uptimeMillis();
|
|
while (true) {
|
|
try {
|
|
this.wait(millis);
|
|
} catch (InterruptedException ie) {
|
|
ie.printStackTrace();
|
|
break;
|
|
}
|
|
long 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) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
public RepeatedEventExpecter expectPaint() {
|
|
try {
|
|
return new PaintExpecter();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
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;
|
|
}
|
|
}
|