ALL News Background articles Commentaries Development/Java IT Security Computer Guides & Tips Archive About & Follow

JavaFX WebView / WebKit error at JavaScript to Java communication (Java 8)

Added on 12/04/2017 by Frank Hissen

We are using Java for nearly two decades to build products. When an integrated WebKit was finally introduced to the Java platform - coming with JavaFX - development opportunities were boosted tremendously, because now you are able to integrate HTML5 web technologies into your desktop application easily.

JavaScript-to-Java Bridge

Every development platform with integrated browsers usually supports bidirectional JavaScript communication, so does the JavaFX WebView / WebEngine. It is very easy and intuitive to use. However, after an update of the JRE that we are embedding into one of our products, JavaScript communication seemed to be broken. The solution is easy, but the internal change of the JRE's behavior seemed to be undocumented and hence caught us by surprise.

JavaScript-to-Java Communication

When you want to make method calls (passing parameters in both direction is also possible but omitted in this example) from JavaScript to Java, you can only call methods of an Java object, so an arbitrary class' instance. Your corresponding Java class might look like this:

public class MyBridgeClass { public void doit() { //do something } }

In your HTML, the upcall from JS to Java will look something like this:

<p><span style="cursor: pointer;" onclick="myapp.doit()">Action from Java</span></p>

Now, to connect the Java and the JavaScript world, the following Java code is required to set up the WebView / WebEngine object of your JavaFX application:

//your WebView's WebEngine object engine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() { @Override public void changed(ObservableValue<? extends State> ov, State oldState, State newState) { if (newState == State.SUCCEEDED) { JSObject win = (JSObject) engine.executeScript("window"); win.setMember("myapp", new MyBridgeClass()); //<-- !!! } } });

This worked fine for a long time and was enough to fit our needs. After the JRE update, however, nothing seemed to happen when clicking the action in the integrated HTML page using the JavaScript call above. We ended up surrounding the JS call with try...catch and received the exception from JavaScript:
TypeError: 'doit' is not a function

Within Eclipse everything still worked fine! But when distributing our application using an embedded JRE, the upcall from JavaScript to Java had no effect at all. This change in the behavior of the JRE happened somewhere between versions u91 and u151. Since we still support Windows XP users, we still use an older Java 8 JRE because of issues using the integrated WebKit under XP. Again, to cut it short, the behavior of the JRE had changed! The object new MyBridgeClass() seems to get lost (garbage collected) during runtime. Using a global variable to hold a reference to the object / instance does the trick:

public class MyApplication{ public static void main(String[] args){ // your application code } //... private MyBridgeClass js2java_instance = new MyBridgeClass(); //... //your WebView's WebEngine object engine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() { @Override public void changed(ObservableValue<? extends State> ov, State oldState, State newState) { if (newState == State.SUCCEEDED) { JSObject win = (JSObject) engine.executeScript("window"); win.setMember("myapp", js2java_instance); //<-- !!! } } }); //...

This works just fine! By the way, the 'old' way of setting up your Java(FX) code is still part of the Oracle documentation / tutorial: Making Upcalls from JavaScript to JavaFX still shows the first version of the code by the time writing this article. There is also a corresponding thread on Stack Overflow - in case you want to join the discussion: Javascript bridge / upcall to JavaFX (via JSObject.setMember() method) breaks when distributing.

Of course, the solution is easy. And in many cases, you might not have struggled at all because you were already using global objects. This article is, however, for everybody using a simple version of the code in accordance with the original Oracle WebView tutorial.

About the Author: Frank Hissen (Computer Scientist & Java Software Engineer)

HissenIT is a small business company in Germany focusing on custom software development, programming and IT consulting. Founder and computer scientist Frank Hissen has over 18 years of experience in various positions in IT projects.

Keywords

Java 8, JavaFX, WebKit, WebView, WebEngine, JavaScript, Java-to-JavaScript, JavaScript-to-Java, Bridge, Communication, Upcalls

Categories: Development/Java


Comments

Post your comment

Share

If you like this page, it would be a great thing if you share it with others:

Mail Facebook Twitter Google+ Pinterest LinkedIn
reddit Digg StumbleUpon XING