JDK-8089260 : Calling java function with array arguments from javascript fails
  • Type: Bug
  • Component: javafx
  • Sub-Component: web
  • Affected Version: 8,9
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • Submitted: 2014-05-16
  • Updated: 2016-06-13
  • Resolved: 2016-06-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Resolved
Related Reports
Blocks :  
Description
Hi all,

When using a WebEngine and calling from Javascript a Java function having an argument being an array of a basic type (String[], int[], double[], etc.), the argument being an array of basic type is always null.

To reproduce the error run the code below:

---- Begin code

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
import netscape.javascript.JSObject;


public class Main extends Application {

    public static void main(String[] args) {
        Application.launch(Main.class);
    }

    public class ParentFunctions {
        public void parentfunc(String title, String[] args) {
            System.out.println(title);
            System.out.println(args);

            assert (title != null) : "title must not be null";
            assert (args != null) :  "args must not be null";
        }
    };

    @Override
    public void start(Stage stage) throws Exception {
        final WebEngine engine = new WebEngine();
        engine.loadContent("<script type='text/javascript'>function init(javaobj) { javaobj.parentfunc('title', ['arg1', 'arg2']); }</script>");

        engine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override
            public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) {
                if (newState == Worker.State.SUCCEEDED) {
                    JSObject window = (JSObject) engine.executeScript("window");
                    window.call("init", new Object[]{new ParentFunctions()});
                }
            }
        });
    }
}

---- End code

Does JavaFX WebEngine allows passing JavaScript array from JavaScript to Java ?

Thanks

Comments
Documentation link brokerage has been addressed in JDK-8089211. As mentioned in the earlier comment, JSObject should be used to receive JS array arguments.
13-06-2016

Earlier JDK-8127042 was resolved with suspecting temporary outage.
20-10-2015

According to the LiveConnect Data Type Conversion[1], JavaScript Array will be converted to netscape.javascript.JSObject. Looks like our official java doc for JSObject[2] has broken links to LiveConnect spec. Raised a bug[3] to fix the doc error. [1] http://docstore.mik.ua/orelly/webprog/jscript/ch22_05.htm [2] https://docs.oracle.com/javase/8/javafx/api/netscape/javascript/JSObject.html [3] https://bugs.openjdk.java.net/browse/JDK-8139654
15-10-2015

Unlike Java, JavaScript is not a statically typed language. So JavaScript array can hold any type of objects at the same time. e.g. var args = ['foo', 1, 1.3, 'bar']; Considering all these cases, it would become complex to support the implicit type conversion from the JavaFx WebEngine. However you have the option to receive the array param as an JSObject type. JSObject provides member functions to introspect the JavaScript array and retrieve the elements in it. PSB, I modified your test code using JSObject to receive the array. import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Worker; import javafx.scene.web.WebEngine; import javafx.stage.Stage; import netscape.javascript.JSObject; public class Main extends Application { public static void main(String[] args) { Application.launch(Main.class); } public class ParentFunctions { public void parentfunc(String title, JSObject args) { System.out.println(title); System.out.println(args); assert (title != null) : "title must not be null"; assert (args != null) : "args must not be null"; for (int i = 0; i < (Integer)args.getMember("length"); i++) { Object obj = args.getSlot(i); if (obj instanceof Integer) System.out.println("args[" + i + "] = " + (Integer)obj); else System.out.println("args[" + i + "] = \"" + (String)obj + "\""); } } }; @Override public void start(Stage stage) throws Exception { final WebEngine engine = new WebEngine(); engine.loadContent("<script type='text/javascript'>function init(javaobj) { javaobj.parentfunc('title', ['arg1', 'arg2', 3]); }</script>"); engine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { JSObject window = (JSObject) engine.executeScript("window"); window.call("init", new ParentFunctions()); } } }); } } Please check and let us know whether this solution works for your case.
12-10-2015