JavaFX WebView recreates window on reload

I am creating a page that will be opened either in a WebView from Java or manually in an external browser. If the page is loaded from Java - I need it to do certain callbacks because Java is used as the backend view, but if the page is loaded manually it has to load different data from its resources. So I'm trying to register a "JavaInit" JS object that could be called when the document is loaded. The HTML looks something 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 read about problems setting JS member before onload event on page here: javaFX webview window.onload 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 the "reload page". Upon reboot, the engine appears to recreate the window object and all registered members disappear.

You can 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 was only called after window.onload was started and by default init is called all the time. Which can be fixed by calling initialization 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 the control is processed from JS back to Java (if any) and then it goes back to the registered function and the program runs fine every time.

But it seems wrong to create an initialization delay in the hopes that Java will successfully register all members. So does anyone know of a "more correct" way to achieve this functionality? And is it possible that in production it might take longer and JS might call the delayed function before the members are registered? Or is there any guarantee that the WebEngine will handle Java events and JS execution continuously?

Thank!

+3


source to share


1 answer


I have the same problem and need to set the elements before the onload event is fired in JS. I added a warning 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 like the alert handler is running synchronously, so the script that will run when onload is enabled will see the members that were set in the alert handler

0


source







All Articles