5

I can't get the Java / Javascript bridge to work on Java11 and Java13. The bridge seems to work fine i Java8 and Java10.

Here is essentially the same code as https://stackoverflow.com/a/34840552/11329518, which again works for me on Java8 and Java10:

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import javafx.application.Application;
import javafx.concurrent.Worker.State;
import javafx.scene.control.ButtonType;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    JavaBridge bridge;
    WebEngine webEngine;

    @Override
    public void start(Stage primaryStage) throws MalformedURLException {

        final URL url = new File("C:/test.html").toURI().toURL();

        WebView webView = new javafx.scene.web.WebView();
        webEngine = webView.getEngine();
        webEngine.load(url.toExternalForm());
        webEngine.setJavaScriptEnabled(true);
        webEngine.setOnAlert(Main::showAlert);
        webEngine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
            if (newState == State.SUCCEEDED) {
                System.out.println("READY");
                JSObject jsobj = (JSObject) webEngine.executeScript("window");
                bridge = new JavaBridge();
                jsobj.setMember("bridge", bridge);
            }

        });
        primaryStage.setScene(new javafx.scene.Scene(webView, 300, 300));
        primaryStage.show();
    }

    // Shows the alert, used in JS catch statement
    private static void showAlert(WebEvent<String> event) {
        javafx.scene.control.Dialog<ButtonType> alert = new javafx.scene.control.Dialog<>();
        alert.getDialogPane().setContentText(event.getData());
        alert.getDialogPane().getButtonTypes().add(ButtonType.OK);
        alert.showAndWait();
    }

    public class JavaBridge {
        public void hello() {
            System.out.println("hello");
        }
    }
}

with test.html containing the Javascript:

<button onclick="try{bridge.hello();}catch(err){alert(err.message);}">call java</button>

What's going on?

I get the following error when clicking the button:

bridge.hello is not a function. (In 'bridge.hello()', 'bridge.hello' is undefined)

Guillaume F.
  • 1,010
  • 7
  • 21
  • 1
    Is your code modular? "_If any Java class passed to JavaScript is in a named module, then it must be reflectively accessible to the javafx.web module. A class is reflectively accessible if the module opens the containing package to at least the javafx.web module. Otherwise, the method will not be called, and no error or warning will be produced_" – [`WebEngine`](https://openjfx.io/javadoc/13/javafx.web/javafx/scene/web/WebEngine.html). – Slaw Dec 29 '19 at 05:21
  • @Slaw I followed the directions for a [non-modular project](https://openjfx.io/openjfx-docs/#IDE-Intellij), so no. – Guillaume F. Dec 29 '19 at 06:43
  • 2
    That guide mentions having `--add-modules javafx.controls,javafx.fxml` as a VM option, did you also add the `javafx.web` module to the list of modules? – jewelsea Dec 29 '19 at 09:00

2 Answers2

2

I can't reproduce your issue with JavaFX 13 or 14-ea+6, using Java 11 (OpenJDK 11.0.2) or Java 13 (OpenJDK 13).

However I can reproduce the issue, if I remove the strong reference to JavaBridge and I use Java 11.

This:

jsobj.setMember("bridge", new JavaBridge());

fails with the same error you have posted, with Java 11. But when using Java 13 (OpenJDK 13), that works fine (and also with Java 12).

Are you using other Java vendors? Can you try with OpenJDK https://jdk.java.net/13/?

José Pereda
  • 44,311
  • 7
  • 104
  • 132
1

There are two things that resolved this issue with OpenJDK 14 and JavaFx 14

  1. Hard Reference to Bridge Object

    bridge = new JavaBridge(); // create Bridge before hand 
    webEngine.getLoadWorker().stateProperty().addListener((ov, oldState, 
    newState) -> {
        if (newState == State.SUCCEEDED) {
            System.out.println("READY");
            JSObject jsobj = (JSObject) webEngine.executeScript("window");
            jsobj.setMember("bridge", bridge);
        }
    });
    
    
  2. Use Bridge in Javascript/HTML page after the page is loaded

    window.onload = function() {
        bridge.hello();
    }
    
anantha ramu
  • 171
  • 4
  • Bridge.hello() doesn't work for me after the page is loaded, but if I create a button with bridge.hello(), then it works if I click on the button. What could be the problem? (JavaFX 18.0.1 and JDK 18.0.1) – Dmitriy Aug 03 '22 at 23:02