Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't the JavaScript method execute when inserted into the DOM from JavaFX?

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:

enter image description here

like image 751
Cypher Avatar asked Oct 19 '22 03:10

Cypher


1 Answers

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().

like image 69
Cypher Avatar answered Oct 28 '22 22:10

Cypher