cacert-testmgr/external/ZendFramework-1.9.5/externals/dojo/util/doh/robot/DOHRobot.java
Markus Warg 8398c9048d initially import ZendFramework-1.9.5 into repository
code was modified slightly, so the code differs from the original downloadable 1.9.5 version
2010-03-31 10:12:32 +02:00

1555 lines
43 KiB
Java

import java.security.*;
import java.applet.Applet;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import netscape.javascript.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.awt.datatransfer.*;
public final class DOHRobot extends Applet{
// order of execution:
// wait for user to trust applet
// load security manager to prevent Safari hang
// discover document root in screen coordinates
// discover keyboard capabilities
// tell doh to continue with the test
// link to doh
// To invoke doh, call eval with window.eval("jsexp")
// Note that the "window" is an iframe!
// You might need to break out of the iframe with an intermediate function
// in the parent window.
private JSObject window = null;
// java.awt.Robot
// drives the test
// you need to sign the applet JAR for this to work
private Robot robot = null;
// In order to preserve the execution order of Robot commands,
// we have to serialize commands by having them join() the previous one.
// Otherwise, if you run doh.robot.typeKeys("dijit"), you frequently get something
// like "diijt"
private static Thread previousThread = null;
// Keyboard discovery.
// At init, the Robot types keys into a textbox and JavaScript tells the
// Robot what it got back.
// charMap maps characters to the KeyEvent that generates the character on
// the user's machine.
// charMap uses the Java 1.4.2 (lack of) template syntax for wider
// compatibility.
private static HashMap charMap = null;
// Java key constants to iterate over
// not all are available on all machines!
private Vector vkKeys = null;
// some state variables
private boolean shift = false;
private boolean altgraph = false;
private boolean ctrl = false;
private boolean alt = false;
private boolean meta = false;
// shake hands with JavaScript the first keypess to wake up FF2/Mac
private boolean jsready = false;
private String keystring = "";
// Firebug gets a little too curious about our applet for its own good
// setting firebugIgnore to true ensures Firebug doesn't break the applet
public boolean firebugIgnore = true;
private SecurityManager securitymanager;
private double key = -1;
// The screen x,y of the document upper left corner.
// We only set it once so people are less likely to take it over.
private boolean inited = false;
private int docScreenX = -100;
private int docScreenY = -100;
private int docScreenXMax;
private int docScreenYMax;
private boolean mouseSecurity = false;
// The last reported mouse x,y.
// If this is different from the real one, something's up.
private int lastMouseX;
private int lastMouseY;
// save a pointer to doh.robot for fast access
JSObject dohrobot = null;
// java.awt.Applet methods
public void stop(){
window = null;
dohrobot = null;
// only secure code run once
if(key != -2){
// prevent further execution of secure functions
key = -2;
// Java calls this when you close the window.
// It plays nice and restores the old security manager.
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
log("Stop");
securitymanager.checkTopLevelWindow(null);
log("Security manager reset");
return null;
}
});
}
}
final private class onvisible extends ComponentAdapter{
public void componentShown(ComponentEvent evt){
// sets the security manager to fix a bug in liveconnect in Safari on Mac
if(key != -1){ return; }
Thread thread = new Thread(){
public void run(){
window = (JSObject) JSObject.getWindow(applet());
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
log("> init Robot");
try{
SecurityManager oldsecurity = System.getSecurityManager();
boolean needsSecurityManager = applet().getParameter("needsSecurityManager").equals("true");
log("Socket connections managed? "+needsSecurityManager);
try{
securitymanager = oldsecurity;
securitymanager.checkTopLevelWindow(null);
// xdomain
if(charMap == null){
if(!confirm("DOH has detected that the current Web page is attempting to access DOH, but belongs to a different domain than the one you agreed to let DOH automate. If you did not intend to start a new DOH test by visiting this Web page, press Cancel now and leave the Web page. Otherwise, press OK to trust this domain to automate DOH tests.")){
stop();
return null;
}
}
log("Found old security manager");
}catch(Exception e){
e.printStackTrace();
log("Making new security manager");
securitymanager = new RobotSecurityManager(needsSecurityManager,
oldsecurity);
securitymanager.checkTopLevelWindow(null);
System.setSecurityManager(securitymanager);
}
// instantiate the Robot
robot = new Robot();
robot.setAutoWaitForIdle(true);
}catch(Exception e){
log("Error calling _init_: "+e.getMessage());
key = -2;
e.printStackTrace();
}
log("< init Robot");
return null;
}
});
if(key == -2){
// applet not trusted
// start the test without it
window.eval("doh.robot._appletDead=true;doh.run();");
}else{
// now that the applet has really started, let doh know it's ok to use it
log("_initRobot");
try{
dohrobot = (JSObject) window.eval("doh.robot");
dohrobot.call("_initRobot", new Object[]{ applet() });
}catch(Exception e){
e.printStackTrace();
}
}
}
};
thread.start();
}
}
public void init(){
// ensure isShowing = true
addComponentListener(new onvisible());
}
// loading functions
public void _setKey(double key){
if(key == -1){
return;
}else if(this.key == -1){
this.key = key;
}
}
private boolean mouseSecure() throws Exception{
// Use MouseInfo to ensure that mouse is inside browser.
// Only works in Java 1.5, but the DOHRobot must compile for 1.4.
if(!mouseSecurity){ return true; }
Class mouseInfoClass;
Class pointerInfoClass;
try{
mouseInfoClass = Class.forName("java.awt.MouseInfo");
pointerInfoClass = Class.forName("java.awt.PointerInfo");
}catch(ClassNotFoundException e){
// Java 1.4
e.printStackTrace();
return true;
}
Method getPointerInfo = mouseInfoClass.getMethod("getPointerInfo", new Class[0]);
Method getLocation = pointerInfoClass.getMethod("getLocation", new Class[0]);
Object pointer=null;
try{
pointer = getPointerInfo.invoke(pointerInfoClass,new Object[0]);
}catch(java.lang.reflect.InvocationTargetException e){
e.getTargetException().printStackTrace();
}
Point mousePosition = (Point)(getLocation.invoke(pointer,new Object[0]));
return mousePosition.x >= docScreenX
&& mousePosition.x <= docScreenXMax
&& mousePosition.y >= docScreenY
&& mousePosition.y <= docScreenYMax;
}
private boolean isSecure(double key){
boolean result = this.key != -1 && this.key != -2 && this.key == key;
try{
result=result&&mouseSecure();
}catch(Exception e){
e.printStackTrace();
result=false;
}
if(!result&&this.key!=-2){
this.key=-2;
window.eval("doh.robot._appletDead=true;");
log("User aborted test; mouse moved off of browser");
alert("User aborted test; mouse moved off of browser.");
}
log("Key secure: " + result);
return result;
}
public void _callLoaded(final double sec){
log("> _callLoaded Robot");
Thread thread = new Thread(){
public void run(){
if(!isSecure(sec)){
return;
}
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
Point p = getLocationOnScreen();
log("Document root: ~"+p.toString());
int x = p.x + 16;
int y = p.y + 8;
// click the mouse over the text box
try{
Thread.sleep(100);
}catch(Exception e){};
robot.mouseMove(x, y);
try{
Thread.sleep(100);
}catch(Exception e){};
robot.mousePress(InputEvent.BUTTON1_MASK);
try{
Thread.sleep(100);
}catch(Exception e){}
robot.mouseRelease(InputEvent.BUTTON1_MASK);
try{
Thread.sleep(100);
}catch(Exception e){}
log("< _callLoaded Robot");
return null;
}
});
}
};
thread.start();
}
// convenience functions
private DOHRobot applet(){
return this;
}
public void log(final String s){
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
System.out.println((new Date()).toString() + ": " + s);
return null;
}
});
}
private void alert(final String s){
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
window.eval("top.alert(\"" + s + "\");");
return null;
}
});
}
private boolean confirm(final String s){
return ((Boolean) AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
return ((Boolean) window.eval("top.confirm(\"" + s + "\");"));
}
})).booleanValue();
}
// mouse discovery code
public void setDocumentBounds(final double sec, int x, int y, int w, int h) throws Exception{
// call from JavaScript
// tells the Robot where the screen x,y of the upper left corner of the
// document are
// not screenX/Y of the window; really screenLeft/Top in IE, but not all
// browsers have this
log("> setDocumentBounds");
if(!isSecure(sec))
return;
if(!inited){
inited = true;
this.lastMouseX = this.docScreenX = x;
this.lastMouseY = this.docScreenY = y;
this.docScreenXMax = x + w;
this.docScreenYMax = y + h;
mouseSecurity=true;
}
log("< setDocumentBounds");
}
// keyboard discovery code
private void _mapKey(char charCode, int keyindex, boolean shift,
boolean altgraph){
log("_mapKey: " + charCode);
// if character is not in map, add it
if(!charMap.containsKey(new Integer(charCode))){
log("Notified: " + (char) charCode);
KeyEvent event = new KeyEvent(applet(), 0, 0,
(shift ? KeyEvent.SHIFT_MASK : 0)
+ (altgraph ? KeyEvent.ALT_GRAPH_MASK : 0),
((Integer) vkKeys.get(keyindex)).intValue(),
(char) charCode);
charMap.put(new Integer(charCode), event);
log("Mapped char " + (char) charCode + " to KeyEvent " + event);
if(((char) charCode) >= 'a' && ((char) charCode) <= 'z'){
// put shifted version of a-z in automatically
int uppercharCode = (int) Character
.toUpperCase((char) charCode);
event = new KeyEvent(applet(), 0, 0, KeyEvent.SHIFT_MASK
+ (altgraph ? KeyEvent.ALT_GRAPH_MASK : 0),
((Integer) vkKeys.get(keyindex)).intValue(),
(char) uppercharCode);
charMap.put(new Integer(uppercharCode), event);
log("Mapped char " + (char) uppercharCode + " to KeyEvent "
+ event);
}
}
}
public void _notified(final double sec, final String chars){
// decouple from JavaScript; thread join could hang it
Thread thread = new Thread("_notified"){
public void run(){
if(!isSecure(sec))
return;
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
try{
// wait for release shift/altgraph to resolve
if(previousThread != null){
previousThread.join();
}
}catch(Exception e){
}
keystring += chars;
if(altgraph && !shift){
shift = false;
// Set robot auto delay now that FF/Mac inited all of the keys.
// Good for DND.
robot.setAutoDelay(1);
try{
log(keystring);
int index = 0;
for (int i = 0; (i < vkKeys.size())
&& (index < keystring.length()); i++){
char c = keystring.charAt(index++);
_mapKey(c, i, false, false);
}
for (int i = 0; (i < vkKeys.size())
&& (index < keystring.length()); i++){
char c = keystring.charAt(index++);
_mapKey(c, i, true, false);
}
for (int i = 0; (i < vkKeys.size())
&& (index < keystring.length()); i++){
char c = keystring.charAt(index++);
_mapKey(c, i, false, true);
}
// notify DOH that the applet finished init
dohrobot.call("_onKeyboard", new Object[]{});
}catch(Exception e){
e.printStackTrace();
}
return null;
}else if(!shift){
shift = true;
}else{
shift = false;
altgraph = true;
}
pressNext();
// }
return null;
}
});
}
};
thread.start();
}
private void pressNext(){
final Thread myPreviousThread = previousThread;
Thread thread = new Thread("pressNext"){
public void run(){
try{
// wait for release shift/altgraph to resolve
if(myPreviousThread != null){
myPreviousThread.join();
}
}catch(Exception e){
}
// first time, press shift (have to do it here instead of
// _notified to avoid IllegalThreadStateException on Mac)
log("starting up, " + shift + " " + altgraph);
if(shift){
robot.keyPress(KeyEvent.VK_SHIFT);
log("Pressing shift");
}
try{
if(altgraph){
robot.keyPress(KeyEvent.VK_ALT_GRAPH);
log("Pressing alt graph");
}
}catch(Exception e){
log("Error pressing alt graph");
e.printStackTrace();
_notified(key, "");
return;
}
dohrobot.call("_nextKeyGroup", new Object[]{ new Integer(vkKeys.size()) });
for (int keyindex = 0; keyindex < vkKeys.size(); keyindex++){
try{
log("Press "
+ ((Integer) vkKeys.get(keyindex)).intValue());
robot.keyPress(((Integer) vkKeys.get(keyindex))
.intValue());
log("Release "
+ ((Integer) vkKeys.get(keyindex)).intValue());
robot.keyRelease(((Integer) vkKeys.get(keyindex))
.intValue());
if(altgraph && (keyindex == (vkKeys.size() - 1))){
robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
log("Releasing alt graph");
}
if(shift && (keyindex == (vkKeys.size() - 1))){
robot.keyRelease(KeyEvent.VK_SHIFT);
log("Releasing shift");
}
}catch(Exception e){
}
try{
log("Press space");
robot.keyPress(KeyEvent.VK_SPACE);
log("Release space");
robot.keyRelease(KeyEvent.VK_SPACE);
}catch(Exception e){
e.printStackTrace();
}
}
}
};
previousThread = thread;
thread.start();
}
public void _initWheel(final double sec){
log("> initWheel");
Thread thread=new Thread(){
public void run(){
if(!isSecure(sec))
return;
Thread.yield();
// calibrate the mouse wheel now that textbox is focused
int dir=1;
if(System.getProperty("os.name").toUpperCase().indexOf("MAC") != -1){
dir=-1;
}
robot.mouseWheel(dir);
try{
Thread.sleep(100);
}catch(Exception e){}
log("< initWheel");
}
};
thread.start();
}
public void _initKeyboard(final double sec){
log("> initKeyboard");
// javascript entry point to discover the keyboard
if(charMap != null){
dohrobot.call("_onKeyboard", new Object[]{});
return;
}
Thread thread = new Thread(){
public void run(){
if(!isSecure(sec))
return;
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
charMap = new HashMap();
KeyEvent event = new KeyEvent(applet(), 0, 0, 0,
KeyEvent.VK_SPACE, ' ');
charMap.put(new Integer(32), event);
try{
// a-zA-Z0-9 + 29 others
vkKeys = new Vector();
for (char i = 'a'; i <= 'z'; i++){
vkKeys.add(new Integer(KeyEvent.class.getField(
"VK_" + Character.toUpperCase((char) i))
.getInt(null)));
}
for (char i = '0'; i <= '9'; i++){
vkKeys.add(new Integer(KeyEvent.class.getField(
"VK_" + Character.toUpperCase((char) i))
.getInt(null)));
}
int[] mykeys = new int[]{ KeyEvent.VK_COMMA,
KeyEvent.VK_MINUS, KeyEvent.VK_PERIOD,
KeyEvent.VK_SLASH, KeyEvent.VK_SEMICOLON,
KeyEvent.VK_LEFT_PARENTHESIS,
KeyEvent.VK_NUMBER_SIGN, KeyEvent.VK_PLUS,
KeyEvent.VK_RIGHT_PARENTHESIS,
KeyEvent.VK_UNDERSCORE,
KeyEvent.VK_EXCLAMATION_MARK, KeyEvent.VK_DOLLAR,
KeyEvent.VK_CIRCUMFLEX, KeyEvent.VK_AMPERSAND,
KeyEvent.VK_ASTERISK, KeyEvent.VK_QUOTEDBL,
KeyEvent.VK_LESS, KeyEvent.VK_GREATER,
KeyEvent.VK_BRACELEFT, KeyEvent.VK_BRACERIGHT,
KeyEvent.VK_COLON, KeyEvent.VK_BACK_QUOTE,
KeyEvent.VK_QUOTE, KeyEvent.VK_OPEN_BRACKET,
KeyEvent.VK_BACK_SLASH, KeyEvent.VK_CLOSE_BRACKET,
KeyEvent.VK_EQUALS };
for (int i = 0; i < mykeys.length; i++){
vkKeys.add(new Integer(mykeys[i]));
}
}catch(Exception e){
e.printStackTrace();
}
robot.setAutoDelay(1);
// prime the event pump for Google Chome - so fast it doesn't even stop to listen for key events!
// send spaces until JS says to stop
int count=0;
boolean waitingOnSpace = true;
do{
log("Pressed space");
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
count++;
waitingOnSpace = ((Boolean)window.eval("doh.robot._spaceReceived")).equals(Boolean.FALSE);
log("JS still waiting on a space? "+waitingOnSpace);
}while(count<500&&waitingOnSpace);
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
robot.setAutoDelay(0);
log("< initKeyboard");
pressNext();
return null;
}
});
}
};
thread.start();
}
public void typeKey(double sec, final int charCode, final int keyCode,
final boolean alt, final boolean ctrl, final boolean shift, final boolean meta,
final int delay, final boolean async){
if(!isSecure(sec))
return;
// called by doh.robot._keyPress
// see it for details
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
try{
log("> typeKey Robot " + charCode + ", " + keyCode + ", " + async);
KeyPressThread thread = new KeyPressThread(charCode,
keyCode, alt, ctrl, shift, meta, delay, async?null:previousThread);
previousThread = async?previousThread:thread;
thread.start();
log("< typeKey Robot");
}catch(Exception e){
log("Error calling typeKey");
e.printStackTrace();
}
return null;
}
});
}
public void upKey(double sec, final int charCode, final int keyCode, final int delay){
// called by doh.robot.keyDown
// see it for details
// a nice name like "keyUp" is reserved in Java
if(!isSecure(sec))
return;
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
log("> upKey Robot " + charCode + ", " + keyCode);
KeyUpThread thread = new KeyUpThread(charCode, keyCode, delay, previousThread);
previousThread = thread;
thread.start();
log("< upKey Robot");
return null;
}
});
}
public void downKey(double sec, final int charCode, final int keyCode, final int delay){
// called by doh.robot.keyUp
// see it for details
// a nice name like "keyDown" is reserved in Java
if(!isSecure(sec))
return;
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
log("> downKey Robot " + charCode + ", " + keyCode);
KeyDownThread thread = new KeyDownThread(charCode, keyCode, delay, previousThread);
previousThread = thread;
thread.start();
log("< downKey Robot");
return null;
}
});
}
public void pressMouse(double sec, final boolean left,
final boolean middle, final boolean right, final int delay){
if(!isSecure(sec))
return;
// called by doh.robot.mousePress
// see it for details
// a nice name like "mousePress" is reserved in Java
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
log("> mousePress Robot " + left + ", " + middle + ", " + right);
MousePressThread thread = new MousePressThread(
(left ? InputEvent.BUTTON1_MASK : 0)
+ (middle ? InputEvent.BUTTON2_MASK : 0)
+ (right ? InputEvent.BUTTON3_MASK : 0), delay,
previousThread);
previousThread = thread;
thread.start();
log("< mousePress Robot");
return null;
}
});
}
public void releaseMouse(double sec, final boolean left,
final boolean middle, final boolean right, final int delay){
if(!isSecure(sec))
return;
// called by doh.robot.mouseRelease
// see it for details
// a nice name like "mouseRelease" is reserved in Java
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
log("> mouseRelease Robot " + left + ", " + middle + ", "
+ right);
MouseReleaseThread thread = new MouseReleaseThread(
(left ? InputEvent.BUTTON1_MASK : 0)
+ (middle ? InputEvent.BUTTON2_MASK : 0)
+ (right ? InputEvent.BUTTON3_MASK : 0), delay,
previousThread);
previousThread = thread;
thread.start();
log("< mouseRelease Robot");
return null;
}
});
}
public void moveMouse(double sec, final int x1, final int y1, final int d, final int duration){
// called by doh.robot.mouseMove
// see it for details
// a nice name like "mouseMove" is reserved in Java
if(!isSecure(sec))
return;
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
int x = x1 + docScreenX;
int y = y1 + docScreenY;
if(x > docScreenXMax || y > docScreenYMax){
// TODO: try to scroll view
log("Request to mouseMove denied");
return null;
}
int delay = d;
log("> mouseMove Robot " + x + ", " + y);
MouseMoveThread thread = new MouseMoveThread(x, y, delay,
duration, previousThread);
previousThread = thread;
thread.start();
log("< mouseMove Robot");
return null;
}
});
}
public void wheelMouse(double sec, final int amount, final int delay, final int duration){
// called by doh.robot.mouseWheel
// see it for details
if(!isSecure(sec))
return;
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
MouseWheelThread thread = new MouseWheelThread(amount, delay, duration,
previousThread);
previousThread = thread;
thread.start();
return null;
}
});
}
private int getVKCode(int charCode, int keyCode){
int keyboardCode = 0;
if(charCode >= 32){
// if it is printable, then it lives in our hashmap
KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode));
keyboardCode = event.getKeyCode();
}
else{
switch (keyCode){
case 13:
keyboardCode = KeyEvent.VK_ENTER;
break;
case 8:
keyboardCode = KeyEvent.VK_BACK_SPACE;
break;
case 25:// shift tab for Safari
case 9:
keyboardCode = KeyEvent.VK_TAB;
break;
case 12:
keyboardCode = KeyEvent.VK_CLEAR;
break;
case 16:
keyboardCode = KeyEvent.VK_SHIFT;
break;
case 17:
keyboardCode = KeyEvent.VK_CONTROL;
break;
case 18:
keyboardCode = KeyEvent.VK_ALT;
break;
case 63250:
case 19:
keyboardCode = KeyEvent.VK_PAUSE;
break;
case 20:
keyboardCode = KeyEvent.VK_CAPS_LOCK;
break;
case 27:
keyboardCode = KeyEvent.VK_ESCAPE;
break;
case 32:
log("it's a space");
keyboardCode = KeyEvent.VK_SPACE;
break;
case 63276:
case 33:
keyboardCode = KeyEvent.VK_PAGE_UP;
break;
case 63277:
case 34:
keyboardCode = KeyEvent.VK_PAGE_DOWN;
break;
case 63275:
case 35:
keyboardCode = KeyEvent.VK_END;
break;
case 63273:
case 36:
keyboardCode = KeyEvent.VK_HOME;
break;
/**
* Constant for the <b>left</b> arrow key.
*/
case 63234:
case 37:
keyboardCode = KeyEvent.VK_LEFT;
break;
/**
* Constant for the <b>up</b> arrow key.
*/
case 63232:
case 38:
keyboardCode = KeyEvent.VK_UP;
break;
/**
* Constant for the <b>right</b> arrow key.
*/
case 63235:
case 39:
keyboardCode = KeyEvent.VK_RIGHT;
break;
/**
* Constant for the <b>down</b> arrow key.
*/
case 63233:
case 40:
keyboardCode = KeyEvent.VK_DOWN;
break;
case 63272:
case 46:
keyboardCode = KeyEvent.VK_DELETE;
break;
case 63289:
case 144:
keyboardCode = KeyEvent.VK_NUM_LOCK;
break;
case 63249:
case 145:
keyboardCode = KeyEvent.VK_SCROLL_LOCK;
break;
/** Constant for the F1 function key. */
case 63236:
case 112:
keyboardCode = KeyEvent.VK_F1;
break;
/** Constant for the F2 function key. */
case 63237:
case 113:
keyboardCode = KeyEvent.VK_F2;
break;
/** Constant for the F3 function key. */
case 63238:
case 114:
keyboardCode = KeyEvent.VK_F3;
break;
/** Constant for the F4 function key. */
case 63239:
case 115:
keyboardCode = KeyEvent.VK_F4;
break;
/** Constant for the F5 function key. */
case 63240:
case 116:
keyboardCode = KeyEvent.VK_F5;
break;
/** Constant for the F6 function key. */
case 63241:
case 117:
keyboardCode = KeyEvent.VK_F6;
break;
/** Constant for the F7 function key. */
case 63242:
case 118:
keyboardCode = KeyEvent.VK_F7;
break;
/** Constant for the F8 function key. */
case 63243:
case 119:
keyboardCode = KeyEvent.VK_F8;
break;
/** Constant for the F9 function key. */
case 63244:
case 120:
keyboardCode = KeyEvent.VK_F9;
break;
/** Constant for the F10 function key. */
case 63245:
case 121:
keyboardCode = KeyEvent.VK_F10;
break;
/** Constant for the F11 function key. */
case 63246:
case 122:
keyboardCode = KeyEvent.VK_F11;
break;
/** Constant for the F12 function key. */
case 63247:
case 123:
keyboardCode = KeyEvent.VK_F12;
break;
/**
* Constant for the F13 function key.
*
* @since 1.2
*/
/*
* F13 - F24 are used on IBM 3270 keyboard; break; use
* random range for constants.
*/
case 124:
keyboardCode = KeyEvent.VK_F13;
break;
/**
* Constant for the F14 function key.
*
* @since 1.2
*/
case 125:
keyboardCode = KeyEvent.VK_F14;
break;
/**
* Constant for the F15 function key.
*
* @since 1.2
*/
case 126:
keyboardCode = KeyEvent.VK_F15;
break;
case 63302:
case 45:
keyboardCode = KeyEvent.VK_INSERT;
break;
case 47:
keyboardCode = KeyEvent.VK_HELP;
break;
}
}
log("Attempting to type " + (char) charCode + ":"
+ charCode + " " + keyCode);
log("Converted to " + keyboardCode);
return keyboardCode;
}
private boolean isUnsafe(int keyboardCode){
// run through exemption list
log("ctrl: "+ctrl+", alt: "+alt+", shift: "+shift);
if(((ctrl || alt) && keyboardCode == KeyEvent.VK_ESCAPE)
|| (alt && keyboardCode == KeyEvent.VK_TAB)
|| (ctrl && alt && keyboardCode == KeyEvent.VK_DELETE)){
log("You are not allowed to press this key combination!");
return true;
}else{
log("Safe to press.");
return false;
}
}
private void _typeKey(final int cCode, final int kCode, final boolean a,
final boolean c, final boolean s, final boolean m){
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
int charCode = cCode;
int keyCode = kCode;
boolean alt = a;
boolean ctrl = c;
boolean shift = s;
boolean meta = m;
boolean altgraph = false;
log("> _typeKey Robot " + charCode + ", " + keyCode);
try{
int keyboardCode=getVKCode(charCode, keyCode);
if(charCode >= 32){
// if it is printable, then it lives in our hashmap
KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode));
// see if we need to press shift to generate this
// character
if(!shift){
shift = event.isShiftDown();
}
altgraph = event.isAltGraphDown();
keyboardCode = event.getKeyCode();
}
// run through exemption list
if(!isUnsafe(keyboardCode)){
if(shift){
log("Pressing shift");
robot.keyPress(KeyEvent.VK_SHIFT);
}
if(alt){
log("Pressing alt");
robot.keyPress(KeyEvent.VK_ALT);
}
if(altgraph){
log("Pressing altgraph");
robot.keyPress(KeyEvent.VK_ALT_GRAPH);
}
if(ctrl){
log("Pressing ctrl");
robot.keyPress(KeyEvent.VK_CONTROL);
}
if(meta){
log("Pressing meta");
robot.keyPress(KeyEvent.VK_META);
}
if(keyboardCode != KeyEvent.VK_SHIFT
&& keyboardCode != KeyEvent.VK_ALT
&& keyboardCode != KeyEvent.VK_ALT_GRAPH
&& keyboardCode != KeyEvent.VK_CONTROL
&& keyboardCode != KeyEvent.VK_META){
try{
robot.keyPress(keyboardCode);
robot.keyRelease(keyboardCode);
}catch(Exception e){
log("Error while actually typing a key");
e.printStackTrace();
}
}
if(ctrl){
robot.keyRelease(KeyEvent.VK_CONTROL);
ctrl = false;
}
if(alt){
robot.keyRelease(KeyEvent.VK_ALT);
alt = false;
}
if(altgraph){
robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
altgraph = false;
}
if(shift){
log("Releasing shift");
robot.keyRelease(KeyEvent.VK_SHIFT);
shift = false;
}
if(meta){
log("Releasing meta");
robot.keyRelease(KeyEvent.VK_META);
meta = false;
}
}
}catch(Exception e){
log("Error in _typeKey");
e.printStackTrace();
}
log("< _typeKey Robot");
return null;
}
});
}
public boolean hasFocus(){
try{
return ((Boolean) window
.eval("var result=false;if(window.parent.document.hasFocus){result=window.parent.document.hasFocus();}else{result=true;}result;"))
.booleanValue();
}catch(Exception e){
// runs even after you close the window!
return false;
}
}
// Threads for common Robot tasks
// (so as not to tie up the browser rendering thread!)
// declared inside so they have private access to the robot
// we do *not* want to expose that guy!
final private class KeyPressThread extends Thread{
private int charCode;
private int keyCode;
private boolean alt;
private boolean ctrl;
private boolean shift;
private boolean meta;
private int delay;
private Thread myPreviousThread = null;
public KeyPressThread(int charCode, int keyCode, boolean alt,
boolean ctrl, boolean shift, boolean meta, int delay, Thread myPreviousThread){
log("KeyPressThread constructor " + charCode + ", " + keyCode);
this.charCode = charCode;
this.keyCode = keyCode;
this.alt = alt;
this.ctrl = ctrl;
this.shift = shift;
this.meta = meta;
this.delay = delay;
this.myPreviousThread = myPreviousThread;
}
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
// in different order so async works
while(!hasFocus()){
Thread.sleep(1000);
}
Thread.sleep(delay);
log("> run KeyPressThread");
_typeKey(charCode, keyCode, alt, ctrl, shift, meta);
}catch(Exception e){
log("Bad parameters passed to _typeKey");
e.printStackTrace();
}
log("< run KeyPressThread");
}
}
final private class KeyDownThread extends Thread{
private int charCode;
private int keyCode;
private int delay;
private Thread myPreviousThread = null;
public KeyDownThread(int charCode, int keyCode, int delay, Thread myPreviousThread){
log("KeyDownThread constructor " + charCode + ", " + keyCode);
this.charCode = charCode;
this.keyCode = keyCode;
this.delay = delay;
this.myPreviousThread = myPreviousThread;
}
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
Thread.sleep(delay);
log("> run KeyDownThread");
while(!hasFocus()){
Thread.sleep(1000);
}
int vkCode=getVKCode(charCode, keyCode);
if(charCode >= 32){
// if it is printable, then it lives in our hashmap
KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode));
// see if we need to press shift to generate this
// character
if(event.isShiftDown()){
robot.keyPress(KeyEvent.VK_SHIFT);
shift=true;
}
if(event.isAltGraphDown()){
robot.keyPress(KeyEvent.VK_ALT_GRAPH);
altgraph=true;
}
}else{
if(vkCode==KeyEvent.VK_ALT){
alt=true;
}else if(vkCode==KeyEvent.VK_CONTROL){
ctrl=true;
}else if(vkCode==KeyEvent.VK_SHIFT){
shift=true;
}else if(vkCode==KeyEvent.VK_ALT_GRAPH){
altgraph=true;
}else if(vkCode==KeyEvent.VK_META){
meta=true;
}
}
if(!isUnsafe(vkCode)){
robot.keyPress(vkCode);
}
}catch(Exception e){
log("Bad parameters passed to downKey");
e.printStackTrace();
}
log("< run KeyDownThread");
}
}
final private class KeyUpThread extends Thread{
private int charCode;
private int keyCode;
private int delay;
private Thread myPreviousThread = null;
public KeyUpThread(int charCode, int keyCode, int delay, Thread myPreviousThread){
log("KeyUpThread constructor " + charCode + ", " + keyCode);
this.charCode = charCode;
this.keyCode = keyCode;
this.delay = delay;
this.myPreviousThread = myPreviousThread;
}
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
Thread.sleep(delay);
log("> run KeyUpThread");
while(!hasFocus()){
Thread.sleep(1000);
}
int vkCode=getVKCode(charCode, keyCode);
if(charCode >= 32){
// if it is printable, then it lives in our hashmap
KeyEvent event = (KeyEvent) charMap.get(new Integer(charCode));
// see if we need to press shift to generate this
// character
if(event.isShiftDown()){
robot.keyRelease(KeyEvent.VK_SHIFT);
shift=false;
}
if(event.isAltGraphDown()){
robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
altgraph=false;
}
}else{
if(vkCode==KeyEvent.VK_ALT){
alt=false;
}else if(vkCode==KeyEvent.VK_CONTROL){
ctrl=false;
}else if(vkCode==KeyEvent.VK_SHIFT){
shift=false;
}else if(vkCode==KeyEvent.VK_ALT_GRAPH){
altgraph=false;
}else if(vkCode==KeyEvent.VK_META){
meta=false;
}
}
robot.keyRelease(vkCode);
}catch(Exception e){
log("Bad parameters passed to upKey");
e.printStackTrace();
}
log("< run KeyUpThread");
}
}
final private class MousePressThread extends Thread{
private int mask;
private int delay;
private Thread myPreviousThread = null;
public MousePressThread(int mask, int delay, Thread myPreviousThread){
this.mask = mask;
this.delay = delay;
this.myPreviousThread = myPreviousThread;
}
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
Thread.sleep(delay);
log("> run MousePressThread");
while(!hasFocus()){
Thread.sleep(1000);
}
robot.mousePress(mask);
robot.waitForIdle();
}catch(Exception e){
log("Bad parameters passed to mousePress");
e.printStackTrace();
}
log("< run MousePressThread");
}
}
final private class MouseReleaseThread extends Thread{
private int mask;
private int delay;
private Thread myPreviousThread = null;
public MouseReleaseThread(int mask, int delay, Thread myPreviousThread){
this.mask = mask;
this.delay = delay;
this.myPreviousThread = myPreviousThread;
}
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
Thread.sleep(delay);
log("> run MouseReleaseThread ");
while(!hasFocus()){
Thread.sleep(1000);
}
robot.mouseRelease(mask);
robot.waitForIdle();
}catch(Exception e){
log("Bad parameters passed to mouseRelease");
e.printStackTrace();
}
log("< run MouseReleaseThread ");
}
}
final private class MouseMoveThread extends Thread{
private int x;
private int y;
private int delay;
private int duration;
private Thread myPreviousThread = null;
public MouseMoveThread(int x, int y, int delay, int duration, Thread myPreviousThread){
this.x = x;
this.y = y;
this.delay = delay;
this.duration = duration;
this.myPreviousThread = myPreviousThread;
}
public double easeInOutQuad(double t, double b, double c, double d){
t /= d / 2;
if(t < 1)
return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
};
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
Thread.sleep(delay);
log("> run MouseMoveThread " + x + ", " + y);
while(!hasFocus()){
Thread.sleep(1000);
}
int x1 = lastMouseX;
int x2 = x;
int y1 = lastMouseY;
int y2 = y;
// shrink range by 1 px on both ends
// manually move this 1px to trip DND code
if(x1 != x2){
int dx = x - lastMouseX;
if(dx > 0){
x1 += 1;
x2 -= 1;
}else{
x1 -= 1;
x2 += 1;
}
}
if(y1 != y2){
int dy = y - lastMouseY;
if(dy > 0){
y1 += 1;
y2 -= 1;
}else{
y1 -= 1;
y2 += 1;
}
}
int delay = (int)Math.ceil(Math.log(duration+1));
robot.setAutoDelay(delay);
robot.mouseMove(x1, y1);
int d = duration/delay-2; // - start,end
for (int t = 0; t <= d; t++){
x1 = (int) easeInOutQuad((double) t, (double) lastMouseX,
(double) x2 - lastMouseX, (double) d);
y1 = (int) easeInOutQuad((double) t, (double) lastMouseY,
(double) y2 - lastMouseY, (double) d);
robot.mouseMove(x1, y1);
}
robot.mouseMove(x, y);
lastMouseX = x;
lastMouseY = y;
robot.waitForIdle();
robot.setAutoDelay(1);
}catch(Exception e){
log("Bad parameters passed to mouseMove");
e.printStackTrace();
}
log("< run MouseMoveThread");
}
}
final private class MouseWheelThread extends Thread{
private int amount;
private int delay;
private int duration;
private Thread myPreviousThread = null;
public MouseWheelThread(int amount, int delay, int duration, Thread myPreviousThread){
this.amount = amount;
this.delay = delay;
this.duration = duration;
this.myPreviousThread = myPreviousThread;
}
public void run(){
try{
if(myPreviousThread != null)
myPreviousThread.join();
Thread.sleep(delay);
log("> run MouseWheelThread " + amount);
while(!hasFocus()){
Thread.sleep(1000);
}
int dir = 1;
if(System.getProperty("os.name").toUpperCase().indexOf("MAC") != -1){
// yay for Apple
dir = -1;
}
robot.setAutoDelay(Math.max(duration/Math.abs(amount),1));
for(int i=0; i<Math.abs(amount); i++){
robot.mouseWheel(amount>0?dir:-dir);
}
robot.setAutoDelay(1);
}catch(Exception e){
log("Bad parameters passed to mouseWheel");
e.printStackTrace();
}
log("< run MouseWheelThread ");
}
}
final private class RobotSecurityManager extends SecurityManager{
// The applet's original security manager.
// There is a bug in some people's Safaris that causes Safari to
// basically hang on liveconnect calls.
// Our security manager fixes it.
private boolean isActive = false;
private boolean needsSecurityManager = false;
private SecurityManager oldsecurity = null;
public RobotSecurityManager(boolean needsSecurityManager, SecurityManager oldsecurity){
this.needsSecurityManager = needsSecurityManager;
this.oldsecurity = oldsecurity;
}
public boolean checkTopLevelWindow(Object window){
// If our users temporarily accept our cert for a session,
// then use the same session to browse to a malicious website also using our applet,
// that website can automatically execute the applet.
// To resolve this issue, RobotSecurityManager overrides checkTopLevelWindow
// to check the JVM to see if there are other instances of the applet running on different domains.
// If there are, it prompts the user to confirm that they want to run the applet before continuing.
// null is not supposed to be allowed
// so we allow it to distinguish our security manager.
if(window == null){
isActive = !isActive;
log("Active is now " + isActive);
}
return window == null ? true : oldsecurity
.checkTopLevelWindow(window);
}
public void checkPermission(Permission p){
// liveconnect SocketPermission resolve takes
// FOREVER (like 6 seconds) in Safari 3
// Java does like 50 of these on the first JS call
// 6*50=300 seconds!
if(isActive && needsSecurityManager
&& java.net.SocketPermission.class.isInstance(p)
&& p.getActions().matches(".*resolve.*")){
throw new SecurityException(
"DOH: liveconnect resolve locks up Safari 3. Denying resolve request.");
}else if(p.equals(new java.awt.AWTPermission("watchMousePointer"))){
// enable robot to watch mouse
}else{
oldsecurity.checkPermission(p);
}
}
public void checkPermission(Permission perm, Object context){
checkPermission(perm);
}
}
public void setClipboardText(double sec, final String data) {
if(!isSecure(sec))
return;
// called by doh.robot.setClipboard
// see it for details
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
StringSelection ss = new StringSelection(data);
getSystemClipboard().setContents(ss, ss);
return null;
}
});
}
public void setClipboardHtml(double sec, final String data) {
if(!isSecure(sec))
return;
// called by doh.robot.setClipboard when format=='text/html'
// see it for details
AccessController.doPrivileged(new PrivilegedAction(){
public Object run(){
String mimeType = "text/html;class=java.lang.String";//type + "; charset=" + charset;// + "; class=" + transferType;
TextTransferable transferable = new TextTransferable(mimeType, data);
getSystemClipboard().setContents(transferable, transferable);
return null;
}
});
}
private static java.awt.datatransfer.Clipboard getSystemClipboard() {
return Toolkit.getDefaultToolkit().getSystemClipboard();
}
private static class TextTransferable implements Transferable, ClipboardOwner {
private String data;
private static ArrayList htmlFlavors = new ArrayList();
static{
try{
htmlFlavors.add(new DataFlavor("text/plain;charset=UTF-8;class=java.lang.String"));
htmlFlavors.add(new DataFlavor("text/html;charset=UTF-8;class=java.lang.String"));
}catch(ClassNotFoundException ex){
ex.printStackTrace();
}
}
public TextTransferable(String mimeType, String data){
this.data = data;
}
public DataFlavor[] getTransferDataFlavors(){
return (DataFlavor[]) htmlFlavors.toArray(new DataFlavor[htmlFlavors.size()]);
}
public boolean isDataFlavorSupported(DataFlavor flavor){
return htmlFlavors.contains(flavor);
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException{
if (String.class.equals(flavor.getRepresentationClass())){
return data;
}
throw new UnsupportedFlavorException(flavor);
}
public void lostOwnership(java.awt.datatransfer.Clipboard clipboard, Transferable contents){
data = null;
}
}
}