I have a JavaFX application that makes heavy use of a WebView. I am trying to insert an object into the DOM that the JavaScript code can use, and I need these objects to be available as new pages are loaded.
However, when I run the program, FirebugLite shows the object in the DOM, but the functions do not execute.
According to some Oracle documentation, this seems to be the appropriate way to provide upcalls from JavaScript to Java. I've also seen a few StackOverflow posts explaining the same thing.
What am I missing? I'm using Java 8, Update 51, 64-bit on Windows 7.
Java:
public class DemoApplication extends Application {
Debug debug;
@Override
public void start(final Stage stage) throws Exception {
debug = new Debug();
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<Worker.State>() {
@Override
public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue, Worker.State newValue) {
if (newValue == Worker.State.SUCCEEDED) {
JSObject windowObject = (JSObject) webEngine.executeScript("window");
windowObject.setMember("Debug", debug);
}
}
}
);
webEngine.load("http://localhost:8080/page1.html");
stage.setScene(new Scene(browser));
stage.show();
}
}
public class Debug {
public void print(final Object text) {
System.err.println(text);
}
}
HTML/JavaScript:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="https://getfirebug.com/firebug-lite.js"></script>
<script>
Debug.print("Hello");
</script>
</head>
<body>
Page 1
<a href="page2.html">Page 2</a>
</body>
</html>
Firebug Screenshot:
I believe what is happening is that WebEngine loads the page, the ChangeListener is invoked at various points (SCHEDULED, RUNNING, SUCCEEDED, etc.). Once the Worker.State.SUCCEEDED event happens, the page has already finished loading all content and has finished executing that content as well. So basically my calls to Debug.print() in the JavaScript code were happening early and calling on an object that was undefined or null.
That's my best guess anyway, because if I add a JavaScript function that is executed by the Java portion after adding in the objects, everything works as expected.
This is how I modified the JavaScript side:
<script>
// callback that uses java objects
window.ready = function() {
Debug.print("Hello");
}
</script>
And this is how I modified the Java side:
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<Worker.State>() {
@Override
public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue, Worker.State newValue) {
if (newValue == Worker.State.SUCCEEDED) {
JSObject windowObject = (JSObject) webEngine.executeScript("window");
windowObject.setMember("Debug", debug); // insert object
windowObject.call("ready"); // execute callback
}
}
}
);
The key changes here are the ready() function in the JavaScript, and invoking that function after injecting the objects on the Java side. This ensures those objects are available before being called.
I tried this on a few different pages and when going from page to page, when the ready() function was called Debug.print() executed properly, even when using WebEngine.reload(), or WebHistory.go().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With