3

I'm creating a page that will be opened either in the WebView from Java, or manually in external browser. If page is loaded from Java - I need it to perform specific callbacks, cuz Java is used as kind of a backend, but if page is loaded manually it should load different data from its resources. So i'm trying to register a JS "JavaInit" object that could be called on document load. HTML looks sort of like this:

<html>
    <head>
        <script type='text/javascript' src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
        <script type="text/javascript">

            DefaultInit = {
                init: function(x) {
                    return "Default init: " + x;
                }
            };

            $(function() {

                var text;
                if (window["JavaInit"] && JavaInit.init)
                    text = JavaInit.init("passed text");
                else
                    text = DefaultInit.init("passed text");

                $("#title").html(text);
            });

        </script>
    </head>
    <body>
        <h1 id="title"></h1>
    </body>
</html>

I've read about problems setting a JS member before page onload event here: javaFX webview window.onload is fired before loadworker succeeds, so my Java code looks like this:

public class Test extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        WebView web = new WebView();
        Scene scene = new Scene(web, 400, 300);
        primaryStage.setScene(scene);
        primaryStage.show();
        initWeb(web);
    }

    private static void initWeb(WebView web) {

        WebEngine eng = web.getEngine();
        ((JSObject)eng.executeScript("window")).setMember("JavaInit", new JavaInit());

        URL url = Test.class.getResource("/test.html");
        eng.load(url.toString());
    }

    public static class JavaInit {

        public String init(Object o) {
            return "Java init: " + o;
        }
    }
}

And it works exactly as required, but only once, before anyone calls "Reload page". After reload engine seems to recreate the "window" object and all registered members disappear.

It is possible to place #setMember inside a "state property" listener:

private static void initWeb(WebView web) {

    WebEngine eng = web.getEngine();

    eng.getLoadWorker().stateProperty().addListener((val, oldState, newState) -> {
        if (newState == State.SUCCEEDED)
            ((JSObject)eng.executeScript("window")).setMember("JavaInit", new JavaInit());
    });

    URL url = Test.class.getResource("/test.html");
    eng.load(url.toString());
}

But then it's called only after window.onload were fired, and default init is called all the time. Which can be fixed by calling init code with minimal delay:

$(function() {

    setTimeout(function() {
        var text;
        if (window["JavaInit"] && JavaInit.init)
            text = JavaInit.init("passed text");
        else
            text = DefaultInit.init("passed text");

        $("#title").html(text);
    });
});

Then control got handled from JS back to Java (if present), and then gets back to registered function, and programm works fine everytime.

But it seems kinda wrong, to create init delay in hope that Java will successfully register all the members. So anybody knows any "more proper" way to achieve this kind of functionality? And is it possible that in the production it could take more time, and JS could call delayed function before members are registered? Or is there any guarantee WebEngine will handle Java events and JS execution continuously?

Thanks!

Community
  • 1
  • 1
  • same problem... anyone knows better solution? – Arek Nov 10 '15 at 17:22
  • 1
    Possible duplicate of [Does JavaFX8 WebEngine's executeScript("window") method refer to the JavaScript window object?](https://stackoverflow.com/questions/36599484/does-javafx8-webengines-executescriptwindow-method-refer-to-the-javascript) – Markus G Jul 04 '18 at 07:45

1 Answers1

1

I have the same issue and I need to set members before onload event will be fired in JS. I added alert in JS in the header:

    alert("command:inject");

And in Java code:

    webEngine.setOnAlert(new EventHandler<WebEvent<String>>() {
        @Override
        public void handle(WebEvent<String> event) {
            // set members here
        }
    });

So it seems, that on alert handle running synchronously, so the script, that will starts, when onload will fire, will see members, that were set in on alert handler

scorpion
  • 11
  • 1