com.codename1.javascript.JavascriptContext Maven / Gradle / Ivy
/*
* Copyright (c) 2012, Steve Hannah/Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.javascript;
import com.codename1.io.Log;
import com.codename1.io.Util;
import com.codename1.ui.BrowserComponent;
import com.codename1.ui.Display;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.events.BrowserNavigationCallback;
import com.codename1.util.Callback;
import com.codename1.util.CallbackAdapter;
import com.codename1.util.StringUtil;
import com.codename1.util.SuccessCallback;
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
/**
* Represents a Javascript context of a single BrowserComponent. This provides
* support for executing Javascript in the BrowserComponent, registering Java
* callbacks to allow Javascript to call Java functions, and returning values
* from Javascript to Java.
*
*
* NOTE: The {@link com.codename1.javascript } package is now
* deprecated. The preferred method of Java/Javascript interop is to use {@link BrowserComponent#execute(java.lang.String) }, {@link BrowserComponent#execute(java.lang.String, com.codename1.util.SuccessCallback) },
* {@link BrowserComponent#executeAndWait(java.lang.String) }, etc.. as these
* work asynchronously (except in the XXXAndWait() variants, which use
* invokeAndBlock() to make the calls synchronously.
*
*
* Typically you would obtain a context for a BrowserComponent via its constructor,
* passing the BrowserComponent to the context.
* E.g.
*
* WebBrowser b = new WebBrowser();
* BrowserComponent bc = (BrowserComponent)b.getInternal();
* JavascriptContext ctx = new JavascriptContext(bc);
* JSObject window = (JSObject)ctx.get("window");
*
*
* @author shannah
* @deprecated Use {@link BrowserComponent#execute(java.lang.String) } directly.
*/
public class JavascriptContext {
private int callbackId=0;
/**
* Flag to enable/disable logging to a debug log.
*/
public static boolean DEBUG=false;
/**
* The browser component on which this context operates.
* @see setBrowserComponent()
* @see getBrowserComponent()
*/
BrowserComponent browser;
/**
* Listener that listens for JavascriptEvents. A Javascript event
* is packaged by the JavascriptContext class in response to a
* BrowserNavigationCallback.
*/
private ActionListener scriptMessageListener;
/**
* A handler for navigation attempts. This intercepts URLs of the
* form cn1command:... . This is how Javascript communicates/calls
* methods in this context.
*/
private BrowserNavigationCallback browserNavigationCallback;
/**
* Stores the previous BrowserNavigationCallback object if one
* was registered on the BrowserComponent.
*/
private BrowserNavigationCallback previousNavigationCallback;
/**
* The name of the Javascript lookup table that is used to store and
* look up Javascript objects that have a JSObject proxy.
*/
String jsLookupTable;
/**
* A running counter for the next object ID that is to be assigned to
* the next JSObject. Each JSObject has an id associated with it which
* corresponds with its position in the Javascript lookup table.
*/
int objectId = 0;
/**
* Stores registered JSFunction callbacks which can be called in response
* to a JavascriptEvent.
*/
private Hashtable callbacks = new Hashtable();
/**
* Running counter to mark the context ID. Each javascript context has its
* own lookup table, and this running counter allows us to generate a unique
* name for each lookup table.
*/
private static int contextId = 0;
/**
* A dummy javascript variable that is used occasionally to workaround some bugs.
*/
static final String DUMMY_VAR = "ca_weblite_codename1_js_JavascriptContext_DUMMY_VAR";
/**
* Javascript variable to store the return value of get() requests so that the value can be
* returned.
*/
static final String RETURN_VAR = "ca_weblite_codename1_js_JavascriptContext_RETURN_VAR";
/**
* The base name of the lookup table. The actual name of the lookup table will have the
* contextId appended to it, and be stored as the member variable jsLookupTable.
*/
static final String LOOKUP_TABLE = "ca_weblite_codename1_js_JavascriptContext_LOOKUP_TABLE";
/**
* A map of JSObjects that is used for cleanup when they are no longer needed.
*/
private HashMap objectMap = new HashMap();
/**
* Whenever the objectMap exceeds this size, cleanup will be called whenever retain()
* is called.
*/
private int objectMapThresholdSize = 500;
private Random cleanupRandomizer = new Random();
private double cleanupProbability = 0.1;
private JSObject window;
/**
* Creates a Javascript context for the given BrowserComponent.
* @param c
*/
public JavascriptContext(BrowserComponent c){
jsLookupTable = LOOKUP_TABLE+(contextId++);
this.browserNavigationCallback = new NavigationCallback();
this.scriptMessageListener = new ScriptMessageListener();
this.setBrowserComponent(c);
}
/**
* Sets the BrowserComponent on which this javascript context runs.
*
* @param c The BrowserComponent on which the context runs.
*/
public final void setBrowserComponent(BrowserComponent c){
if ( c != browser ){
if ( browser != null ){
this.uninstall();
}
browser = c;
if ( browser != null ){
this.install();
}
}
}
/**
* Increments the reference count for a the javascript object wrapped by
* the given JSObject. This may also trigger a cleanup of the object map if
* the map grows too big, or it randomly decided to perform cleanup.
* @param obj
*/
void retain(JSObject obj){
objectMap.put(new Integer(obj.objectId), Display.getInstance().createSoftWeakRef(obj));
if ( objectMap.size() > objectMapThresholdSize || cleanupRandomizer.nextDouble() < cleanupProbability ){
cleanup();
}
}
/**
* Decrements the reference count for the javascript object with the given id.
* This ID was assigned inside the JSObject constructor and refers to the javascript
* lookup table location for the javascript object.
* @param id The ID of the javascript object.
*/
void release(int id){
String ID_KEY = JSObject.ID_KEY;
String PROP_REFCOUNT = JSObject.PROP_REFCOUNT;
String lt = jsLookupTable;
String p = lt+"["+id+"]";
String js = "var id = "+id+"; "+
"if (typeof(id) != 'undefined' && typeof("+lt+"[id]) != 'undefined' && "+lt+"[id]."+ID_KEY+"==id){"+
p+"."+PROP_REFCOUNT+"--;"+
"if ("+p+"."+PROP_REFCOUNT+"<=0){"+
"delete "+lt+"[id];"+
"}"+
"}";
exec(js, true);
}
/**
* Cleans up stale references to Javascript objects. This is triggered randomly whenever
* a JSObject is constructed (with a given probability threshold). If this is never called
* then the JS GC won't be able to free any objects that have ever been wrapped by JSObject
* because they are stored in the global lookup table.
*/
public void cleanup(){
if ( DEBUG ){
Log.p("Cleaning up Javascript lookup table.");
}
ArrayList remove = new ArrayList();
for ( Integer i : objectMap.keySet()){
if ( Display.getInstance().extractHardRef(objectMap.get(i)) == null ){
remove.add(i);
}
}
if ( DEBUG ){
Log.p("Found "+remove.size()+" objects to remove from the Javascript lookup table.");
}
for ( Integer i : remove ){
release(i.intValue());
objectMap.remove(i);
}
}
private String exec(String js){
synchronized (browser) {
return exec(js, false);
}
}
/**
* Executes a Javascript string and returns the string. It is synchronized
* to disallow multiple threads from running javascript on the same BrowserComponent.
*
* This is just a thin wrapper around the BrowserComponent.executeAndReturnString() method.
*
* @param js
* @return The string result of executing the Javascript string.
*/
private String exec(String js, boolean async){
synchronized (browser) {
if ( DEBUG ){
Log.p("About to execute("+async+") "+js);
//browser.execute("console.log(execute ca_weblite_codename1_js_JavascriptContext_LOOKUP_TABLE0[0])");
}
if (async) {
browser.execute(installCode()+";("+js+")");
return null;
} else {
return browser.executeAndReturnString(installCode()+";("+js+")");
}
}
}
/**
* Uninstalls the context from the browser component. This just includes
* the listeners that are registered with the BrowserComponent so that
* the context is informed of navigation callbacks and script message listeners.
*
* @see install()
*
*/
private void uninstall(){
//browser.removeWebEventListener("shouldLoadURL", urlListener);
browser.setBrowserNavigationCallback(previousNavigationCallback);
browser.removeWebEventListener("scriptMessageReceived", scriptMessageListener);
}
/**
* Installs the context in the current browser component. This effectively
* installs listeners in the browser component so that the context can
* be notified of events like navigation callbacks and script message received
* events.
*/
private void install(){
//browser.addWebEventListener("shouldLoadURL", urlListener);
previousNavigationCallback = browser.getBrowserNavigationCallback();
browser.setBrowserNavigationCallback(browserNavigationCallback);
browser.addWebEventListener("scriptMessageReceived", scriptMessageListener);
}
/**
* Executes a javascript string and returns the result of the execution as
* an appropriate object value depending on the type of value that was returned.
*
* Return value types will depend on the Javascript type returned. The following
* table shows the mappings:
*
*
* Javascript Type Java Return Type
*
*
* Number java.lang.Double
* String java.lang.String
* Boolean java.lang.Boolean
* Object JSObject
* Function JSObject
* null null
* undefined null
*
*
*
* Example
*
* //Get the window object
* JSObject window = (JSObject)ctx.get("window");
*
* // Create a new empty Javascript Object
* JSObject newObj = (JSObject)ctx.get("{}");
*
* // Get the current document body contents as a string.
* String html = (String)ctx.get("document.body.innerHTML");
*
* // Get a numerical result
* Double result = (Double)ctx.get("1+2");
*
* // Get a Javascript function object
* JSObject func = (JSObject)ctx.get("function(a,b){ return a+b }");
*
* // Get a boolean result
* Boolean res = (Boolean)ctx.get("1 < 2");
*
* @param javascript The javascript to be executed.
* @return The result of the javascript expression.
*/
public Object get(String javascript){
synchronized(browser) {
String returnVar = RETURN_VAR+"_"+(callId++);
try {
String js2 = returnVar+"=("+javascript+")";
String res = exec(js2);
String typeQuery = "typeof("+returnVar+")";
String type = browser.executeAndReturnString(typeQuery);
try {
if ( "string".equals(type)){
return res;
} else if ( "number".equals(type)){
return Double.valueOf(res);
} else if ( "boolean".equals(type)){
return "true".equals(res)?Boolean.TRUE:Boolean.FALSE;
} else if ( "object".equals(type) || "function".equals(type)){
return new JSObject(this, returnVar);
} else {
return null;
}
} catch ( Exception ex){
Log.e(new RuntimeException("Failed to get javascript "+js2+". The error was "+ex.getMessage()+". The result was "+res+". The type result was "+type));
return null;
}
} finally {
browser.execute(returnVar+"=undefined");
}
}
}
/**
* Returns a reference to the Javascript "window" object.
* @return The window object.
*/
public JSObject getWindow() {
if (window == null) {
window = (JSObject)this.get("window");
}
return window;
}
/**
* Returns the result of the provided javascript expression asynchronously.
* @param javascript A javascript expression.
* @param callback Callback to be called with the result of the expression.
*/
public void getAsync(String javascript, final Callback callback) {
final String callbackMethod = "callback$$"+callbackId;
callbackId++;
if (callbackId > 1000) {
callbackId = 0;
}
getWindow().set(callbackMethod, new JSFunction() {
public void apply(JSObject self, Object[] args) {
callback.onSucess(args[0]);
getWindow().set(callbackMethod, null, true);
}
}, true);
String js2 = callbackMethod+"("+javascript+")";
exec(js2, true);
}
/**
* Returns the result of the provided javascript expression asynchronously.
* @param javascript A javascript expression.
* @param callback Callback to be called with the result of the expression.
*/
public void getAsync(String javascript, final SuccessCallback callback) {
getAsync(javascript, new CallbackAdapter() {
@Override
public void onSucess(Object value) {
callback.onSucess(value);
}
});
}
/**
* Sets a Javascript value given a compatible Java object value. This is an abstraction
* upon javascript to execute key = value
.
*
* The key is any Javascript expression whose result can be assigned. The value
* is a Java object that will be converted into a Javascript object as follows:
*
*
*
* Java type Converted to
*
*
* Double Number
* Integer Number
* Float Number
* Long Number
* String String
* JSObject Object by ref
* null null
*
*
*
* Hence if you want to set a Javascript string value, you can just
* pass a Java string into this method and it will be converted.
*
* JSObject "By Ref"
* You may notice that if you pass a JSObject as the value parameter, the
* table above indicates that it is passed by reference. A JSObject merely
* stores a reference to a Javascript object from a lookup table in the
* Javascript runtime environment. It is this lookup that is ultimately
* assigned to the "key" when you pass a JSObject as the value. This has
* the effect of setting the actual Javascript Object to this value, which
* is effectively a pass-by-reference scenario.
*
* Examples
*
*
* // Set the window.location.href to a new URL
* ctx.set("window.location.href", "http://google.com");
*
* // Create a new JSObject, and set it as a property of another JSObject
* JSObject camera = (JSObject)ctx.get("{}");
* ctx.set("window.camera", camera);
*
* // Set the name of the camera via JSObject.set()
* camera.set("name", "My Camera");
*
* // Get the camera's name via Javascript
* String cameraName = (String)ctx.get("window.camera.name");
* // Should be "My Camera"
*
* // Set the camera name via context.set()
* ctx.set("camera.name", "New name");
*
* String newName = (String)camera.get("name");
* // Should be "New name"
*
*
* @param key A Javascript expression whose result is being assigned the value.
* @param value The object or value that is being assigned to the Javascript variable
* on the left.
*/
public void set(String key, Object value){
synchronized(browser) {
String lhs = key;
String rhs = "undefined";
if ( String.class.isInstance(value)){
String escaped = StringUtil.replaceAll((String)value, "\\", "\\\\");
escaped = StringUtil.replaceAll(escaped, "'", "\\'");
rhs = "'"+escaped+"'";
} else if ( value instanceof Integer || value instanceof Long || value instanceof Float || value instanceof Double ){
rhs =value.toString();
} else if ( JSObject.class.isInstance(value)){
rhs = ((JSObject)value).toJSPointer();
} else if (value instanceof Boolean){
rhs = ((Boolean)value).booleanValue()?"true":"false";
} else {
rhs = "null";
}
exec(lhs+"="+rhs);
}
}
/**
* Sets a Javascript value given a compatible Java object value. This is an abstraction
* upon javascript to execute key = value
. See {@link #set(java.lang.String, java.lang.Object) for a full description.
* @param key A Javascript expression whose result is being assigned the value.
* @param value The object or value that is being assigned to the Javascript variable
* on the left.
* @param async If true, the call is made asynchronously.
* @see #set(java.lang.String, java.lang.Object)
* @see #setAsync(java.lang.String, java.lang.Object)
*/
public void set(String key, Object value, boolean async) {
if (async) {
setAsync(key, value);
} else {
set(key, value);
}
}
/**
* Sets a Javascript value given a compatible Java object value asynchronously.
* @param key A Javascript expression whose result is being assigned the value.
* @param value The object or value that is being assigned to the Javascript variable
* on the left.
* @see #set(java.lang.String, java.lang.Object)
*/
public void setAsync(String key, Object value) {
String lhs = key;
String rhs = "undefined";
if ( String.class.isInstance(value)){
String escaped = StringUtil.replaceAll((String)value, "\\", "\\\\");
escaped = StringUtil.replaceAll(escaped, "'", "\\'");
rhs = "'"+escaped+"'";
} else if ( value instanceof Integer || value instanceof Long || value instanceof Float || value instanceof Double ){
rhs =value.toString();
} else if ( JSObject.class.isInstance(value)){
rhs = ((JSObject)value).toJSPointer();
} else if (value instanceof Boolean){
rhs = ((Boolean)value).booleanValue()?"true":"false";
} else {
rhs = "null";
}
exec(lhs+"="+rhs, true);
}
/**
* Calls the appropriate callback method given a URL that was received
* from the NavigationCallback. It is set up to accept URLs of the
* form cn1command:object.method?type1=value1&type2=value2&...&typen=valuen
*
* This method parses the URL and converts all arguments (including the
* object and method) into their associated Java representations, then
* generates a JavascriptEvent to fire on the scriptMessageReceived
* browser event.
*
* This method will usually be called on the native platform's GUI
* thread, but it dispatches the resulting JavascriptEvent on the EDT
* using Display.callSerially()
* @param request The URL representing the command that is being called.
*/
private void dispatchCallback(final String request){
Runnable r = new Runnable(){
public void run(){
String command = request.substring(request.indexOf("/!cn1command/")+"/!cn1command/".length());
// Get the callback id
String objMethod = command.substring(0, command.indexOf("?"));
command = command.substring(command.indexOf("?")+1);
final String self = objMethod.substring(0, objMethod.indexOf("."));
String method = objMethod.substring(objMethod.indexOf(".")+1);
// Now let's get the parameters
String[] keyValuePairs = Util.split(command, "&");
//Vector params = new Vector();
int len = keyValuePairs.length;
Object[] params = new Object[len];
for ( int i=0; iThis is intended to only process functions that are backed by
* a JSFunction object. Prior to this call, it is assumed that a JSFunction
* has been registered as a callback at the Javascript address provided via
* the JSObject.addCallback() or JavascriptContext.addCallback() methods.
*
* If there is no JSFunction registered to handle this command then nothing
* will happen as a result of the request.
*
* @see JSFunction
* @see addCallback()
* @see JSObject.addCallback()
*
*/
private class ScriptMessageListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
JavascriptEvent jevt = (JavascriptEvent)evt;
JSObject source = jevt.getSelf();
String method = jevt.getMethod();
String key = source.toJSPointer()+"."+method;
JSFunction func = (JSFunction)callbacks.get(key);
if ( func == null ){
// No callback is registered for this method.
return;
}
func.apply(source, jevt.getArgs());
evt.consume();
}
}
/**
* Stock Javascript code that is included before all javascript requests to create
* a lookup table for the JS objects if one hasn't been created yet.
* @return
*/
private String installCode(){
return "if (typeof("+jsLookupTable+") == 'undefined'){"+jsLookupTable+"=[]}";
}
/**
* Adds a JSFunction to handle calls to the specified Javascript object. This
* essentially installed a Javascript proxy method that sends a message via
* a navigation callback to the JavascriptContext so that it can cause Java
* code to be executed.
*
*
* @param source The Javascript object on which the callback is being registered
* as a member method.
* @param method The name of the method that will be created to execute our callback.
* @param callback The callback that is to be executed when source.method() is
* executed in Javascript.
*/
void addCallback(JSObject source, String method, JSFunction callback, boolean async){
String key = source.toJSPointer()+"."+method;
callbacks.put(key, callback);
String id = JSObject.ID_KEY;
//String lookup = LOOKUP_TABLE;
String self = source.toJSPointer();
String isSimulator = Display.getInstance().isSimulator() ? "true":"false";
String js = self+"."+method+"=function(){"+
"var len=arguments.length;var url='https://www.codenameone.com/!cn1command/"+self+"."+method+"?'; "+
"for (var i=0; iThis operates almost exactly like the Javascript Function.apply() method.
*
* Note that JSObject also has a couple of call()
methods
* that may be more convenient to use as they will automatically set the "self"
* parameter to the JSObject callee. This version of the method is handy in cases
* where you have been passed a function (perhaps as a callback) and you need to
* execute that function in a particular context.
*
* Example
*
*
* // Get the Array.push method as an object
* JSObject push = (JSObject)ctx.get("Array.prototype.push");
*
* // Create a new array
* JSObject colors = (JSObject)ctx.get("['red', 'green', 'blue']");
*
* // "Push" a new color onto the array directly using the JSObject's call()
* // method
* colors.call("push", "purple");
*
* // Alternate method using JavascriptContext.call()
* ctx.call(push, colors, "orange");
*
* // Check size of colors array now
* Double size = (Double)colors.get("length");
* // Should be 5.0
*
* // Get 4th color (should be purple)
* String purple = (String)colors.get(3);
*
* // Get 5th color (should be orange)
* String orange = (String)colors.get(4);
*
*
*
*
* @param func The Javascript function object that is being called.
* @param self Javascript Object that should act as "this" for the function call.
* @param params The parameters that should be passed to the function. These
* parameters should be passed as Java objects but will be converted into their
* associated Javascript version.
* @return The result of the function call. Javascript results will be automatically
* converted to their associated Java types.
*/
public Object call(JSObject func, JSObject self, Object[] params){
return call(func.toJSPointer(), self, params);
}
/**
* Calls a Javascript function (encapsulated in a JSObject) with a specified
* Javascript Object as the "this" context for the function call.
* @param func The Javascript function object that is being called.
* @param self Javascript Object that should act as "this" for the function call.
* @param params The parameters that should be passed to the function. These
* parameters should be passed as Java objects but will be converted into their
* associated Javascript version.
* @param callback The callback to pass the return value to.
*/
public void callAsync(JSObject func, JSObject self, Object[] params, Callback callback) {
callAsync(func.toJSPointer(), self, params, callback);
}
/**
* Calls a Javascript function (encapsulated in a JSObject) with a specified
* Javascript Object as the "this" context for the function call.
* @param func The Javascript function object that is being called.
* @param self Javascript Object that should act as "this" for the function call.
* @param params The parameters that should be passed to the function. These
* parameters should be passed as Java objects but will be converted into their
* associated Javascript version.
* @param callback The callback to pass the return value to.
*/
public void callAsync(JSObject func, JSObject self, Object[] params, final SuccessCallback callback) {
callAsync(func, self, params, new CallbackAdapter() {
@Override
public void onSucess(Object value) {
callback.onSucess(value);
}
});
}
/**
* Calls a Javascript function with the given parameters. This would translate
* roughly into executing the following javascript:
*
* jsFunc.call(self, param1, param1, ..., paramn)
*
*
*
* @param jsFunc A javascript expression that resolves to a function object that
* is to be called.
* @param self The Javascript object that is used as "this" for the method call.
* @param params Array of the Javascript parameters, as Java objects. These use
* the same conversions as are described in the docs for set().
*
* @return Returns the return value converted to the corresponding Java
* object type.
*/
public Object call(String jsFunc, JSObject self, Object[] params) {
return call(jsFunc, self, params, false, null);
}
/**
* Calls a Javascript function with the given parameters asynchronously. This would translate
* roughly into executing the following javascript:
*
* jsFunc.call(self, param1, param1, ..., paramn)
*
*
*
* @param jsFunc A javascript expression that resolves to a function object that
* is to be called.
* @param self The Javascript object that is used as "this" for the method call.
* @param params Array of the Javascript parameters, as Java objects. These use
* the same conversions as are described in the docs for set().
*
* @param callback Callback to pass the return value converted to the corresponding Java
* object type.
*/
public void callAsync(String jsFunc, JSObject self, Object[] params, Callback callback) {
call(jsFunc, self, params, true, callback);
}
/**
* Calls a Javascript function with the given parameters asynchronously. This would translate
* roughly into executing the following javascript:
*
* jsFunc.call(self, param1, param1, ..., paramn)
*
*
*
* @param jsFunc A javascript expression that resolves to a function object that
* is to be called.
* @param self The Javascript object that is used as "this" for the method call.
* @param params Array of the Javascript parameters, as Java objects. These use
* the same conversions as are described in the docs for set().
*
* @param callback Callback to pass the return value converted to the corresponding Java
* object type.
*/
public void callAsync(String jsFunc, JSObject self, Object[] params, final SuccessCallback callback) {
callAsync(jsFunc, self, params, new CallbackAdapter() {
@Override
public void onSucess(Object value) {
callback.onSucess(value);
}
});
}
long callId = 0;
/**
* Calls a Javascript function with the given parameters, and optionally to make the call asynchronously. This would translate
* roughly into executing the following javascript:
*
* jsFunc.call(self, param1, param1, ..., paramn)
* @param jsFunc A javascript expression that resolves to a function object that
* is to be called.
* @param self The Javascript object that is used as "this" for the method call.
* @param params Array of the Javascript parameters, as Java objects. These use
* the same conversions as are described in the docs for set().
* @param async If true, the call will be made asynchronously.
* @param callback Used if {@code async} is true to pass the return value.
* @return Returns the return value converted to the corresponding Java
* object type. This will always return null if {@code async} is {@code true}.
*/
public Object call(String jsFunc, JSObject self, Object[] params, boolean async, Callback callback){
if (callId > 10000000l) {
callId = 0;
}
String var = RETURN_VAR+"_call_"+(callId++);
String js = var+"=("+jsFunc+").call("+self.toJSPointer();
int len = params.length;
for ( int i=0; ijsFunc.call(self, param1, param1, ..., paramn)
* @param jsFunc A javascript expression that resolves to a function object that
* is to be called.
* @param self The Javascript object that is used as "this" for the method call.
* @param params Array of the Javascript parameters, as Java objects. These use
* the same conversions as are described in the docs for set().
* @param async If true, the call will be made asynchronously.
* @param callback Used if {@code async} is true to pass the return value.
* @return Returns the return value converted to the corresponding Java
* object type. This will always return null if {@code async} is {@code true}.
*/
public Object call(String jsFunc, JSObject self, Object[] params, boolean async, final SuccessCallback callback){
return call(jsFunc, self, params, async, new CallbackAdapter() {
@Override
public void onSucess(Object value) {
callback.onSucess(value);
}
});
}
}