1

The following code works on Linux, but the callback does not work on Windows (VirtualBox VM). Can you please tell me why?

Java:

    public class WebViewTest extends Application {
      private Label label = new Label ("...");
      public static void main (String[] args) {
        launch (args);
      }
      @Override
      public void start (Stage stage) throws Exception {
        WebView webView = new WebView ();
        WebEngine webEngine = webView.getEngine ();
        webEngine.setJavaScriptEnabled (true);
        JSObject window = (JSObject) webView.getEngine ().executeScript ("window");
        window.setMember ("java", new Callback ());
        webEngine.load (getClass ().getResource ("WebViewTest.html").toString ());
        BorderPane pane = new BorderPane ();
        pane.setCenter (webView);
        pane.setBottom (label);
        Scene scene = new Scene (pane);
        stage.setScene (scene);
        stage.show ();
      }
      public class Callback {
        public void click () {
        label.setText ("Clicked :-)");
      }
    }
  }

HTML:

<!DOCTYPE html>
<html>
  <head>
    <title>Test</title>
    <meta charset="UTF-8">
  </head>
  <body>
    <button onclick="window.java.click ();">Test</button>
  </body>
</html>
Patrick
  • 3,578
  • 5
  • 31
  • 53
  • Have you inspected what happens with [Firebug](http://stackoverflow.com/questions/29534763/javafx-and-firebug-lite-in-web-page-inspector-mode)? – hotzst Jan 08 '17 at 20:29
  • I don't know how to use Firebug in JavaFX WebView. However, after some debugging, `window.java` has the value `undefined` on Windows. – Patrick Jan 08 '17 at 20:40
  • The link in my comment will tell you how to use Firebug with a JavaFX webview. – hotzst Jan 08 '17 at 20:45
  • OK thanks, but simple manual debugging already tells me that `java` callback is not defined, this is where I'm stuck. It's like if `window.setMember` was not called. – Patrick Jan 08 '17 at 20:50
  • 2
    Try loading the HTML first and then attach the Java bridge. After all the JavaScript can only be executed on a DOM that is present. – hotzst Jan 08 '17 at 20:53
  • I'm surprised that it works on your Linux box (and that it also works on my Mac). There must be some system dependent implementation detail in webkit that makes properties of the window persist when a new DOM is loaded on those OS. – James_D Jan 08 '17 at 21:01

3 Answers3

4

The trick was to create the Callback as a class field:

private Callback callback = new Callback ();

And then:

webEngine.load (getClass ().getResource ("WebViewTest.html").toString ());
JSObject window = (JSObject) webView.getEngine ().executeScript ("window");
// BUG // window.setMember ("java", new Callback ());
window.setMember ("java", callback);

Maybe there is some abusive garbage collecting on Windows? I don't know...

Patrick
  • 3,578
  • 5
  • 31
  • 53
  • 1
    It is possible that `window.setMember(...)` is storing the callback using a weak reference (e.g. via a `WeakHashMap`); though tbh this seems unlikely. – James_D Jan 08 '17 at 22:36
1

The window object is likely replaced when a new DOM is loaded into the web engine. Try setting the callback when the document is loaded:

Callback callback = new Callback();
webEngine.documentProperty().addListener((obs, oldDoc, newDoc) -> {
    if (newDoc != null) {
        JSObject window = (JSObject) webView.getEngine ().executeScript ("window");
        window.setMember ("java", callback);                
    }
});

(You can see that the window object changes by System.out.println(System.identityHashCode(webView.getEngine ().executeScript ("window")) before loading the HTML, and System.out.println(System.identityHashCode(window)) in the document listener.)

James_D
  • 201,275
  • 16
  • 291
  • 322
  • You helped me while I was bridging some Java functions to the JavaFx's WebView. I didn't know why my "window.utility" object became "undefined" randomly. I thought I was due to some JavaFx's internal procedure I couldn't catch. Your answer opened my eyes! – FonzTech Apr 02 '20 at 15:21
0

I had a similar problem and after digging deep, I found out that you are supposed to call window.setMember ("java", new Callback ()); after rendering your html so this should work:

JSObject window = (JSObject) webView.getEngine ().executeScript ("window");
webEngine.load (getClass ().getResource ("WebViewTest.html").toString ())
window.setMember ("java", new Callback ());

Also note that if you perform a reload or if you navigate to another page, you will lose your functionality again. A solid solution will be:

webEngine.documentProperty().addListener(((observable, oldValue, newValue) -> {
   if (Objects.nonNull(newValue)) {
        JSObject window = (JSObject) webEngine.executeScript("window");
        window.setMember("engine", callback);
    }
}));
Wafula Samuel
  • 530
  • 1
  • 8
  • 25