2 Replies Latest reply: Nov 29, 2012 8:07 AM by Patrick Martin RSS

    Calling javascript from JavaFX

    Patrick Martin
      This is well documented, yet I am having trouble communicating with JavaScript from JavaFX. The relevant snippet is:
      WebView wv = new WebView();
      WebEngine we = wv.getEngine();
      
      String content = FileUtils.readFileToString(new File("test.html"));
      we.loadContent(content);
      
      we.executeScript("changeBackground();");
      Which reads the following content:

      test.html:
      <html>
      <body>
      <script type="text/javascript">
      document.bgColor = "#cccccc";
      function changeBackground()
      {
        document.bgColor = "#ff0000";
      }
      </script>
      </body>
      </html>
      giving error:
      netscape.javascript.JSException: ReferenceError: Can't find variable: changeBackground
           at com.sun.webpane.platform.WebPage.twkExecuteScript(Native Method)
           at com.sun.webpane.platform.WebPage.executeScript(WebPage.java:1438)
           at javafx.scene.web.WebEngine.executeScript(WebEngine.java:811)
           at com.javainc.tmi.WebTest.init(WebTest.java:30)
           at com.javainc.tmi.WebTest.start(WebTest.java:44)
           at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
           at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:206)
           at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:173)
           at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
           at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
           at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
           at java.lang.Thread.run(Unknown Source)
      I've tried it without the semi colons at the end as well as every permutation I can think of.

      When I remove the executeScript code, the webview displays and turns the screen grey as it should.

      Is this a bug or am I missing something obvious?

      I am using 64 bit jdk1.7.0_09 on Windows 7.

      - Pat
        • 1. Re: Calling javascript from JavaFX
          jsmith
          The document loads into the WebEngine asynchronously.
          You have to wait for it to finish loading before you try to run a script on it.

          For example, you can listen to the document property:
          we.documentProperty().addListener(new ChangeListener<Document>() {
            @Override public void changed(ObservableValue<? extends Document> observableValue, Document document, Document newDoc) {
              if (newDoc != null) {
                we.documentProperty().removeListener(this);
                we.executeScript("changeBackground();");
              }
            }
          });
          Or you could grab the WebEngine's LoadWorker and listen for it's state change.

          Some shorthand where you provide the load or loadContent method with a CallBack to be executed in the event of a successful page load might be convenient, so you could file a request in jira if you'd like that, but the listener methods work ok too.
          -------
          Full executable example (using your test.html):
          import javafx.application.Application;
          import javafx.beans.value.ChangeListener;
          import javafx.beans.value.ObservableValue;
          import javafx.scene.Scene;
          import javafx.scene.web.WebEngine;
          import javafx.scene.web.WebView;
          import javafx.stage.Stage;
          import org.w3c.dom.Document;
          
          import java.io.*;
          
          public class ScriptExecutive extends Application {
          
            public static void main(String[] args) { launch(args); }
          
            @Override public void start(final Stage stage) {
              final WebView wv = new WebView();
              final WebEngine we = wv.getEngine();
          
              we.documentProperty().addListener(new ChangeListener<Document>() {
                @Override public void changed(ObservableValue<? extends Document> observableValue, Document document, Document newDoc) {
                  if (newDoc != null) {
                    we.documentProperty().removeListener(this);
                    we.executeScript("changeBackground();");
                  }
                }
              });
          
              String content = FileUtils.readFileToString(new File("test.html"));
              we.loadContent(content);
          
              stage.setScene(new Scene(wv));
              stage.show();
            }
          
            static class FileUtils {
              public static String readFileToString(File file) {
                try {
                  StringBuilder builder = new StringBuilder();
          
                  String line;
                  BufferedReader reader = new BufferedReader(new FileReader(file));
                  while ((line = reader.readLine()) != null) builder.append(line);
          
                  return builder.toString();
                } catch (FileNotFoundException e) {
                  System.out.println("Cannot find: " + file);
                  return "";
                } catch (IOException e) {
                  System.out.println("Oh snap, it broke: " + e);
                  return "";
                }
              }
            }
          }
          • 2. Re: Calling javascript from JavaFX
            Patrick Martin
            Thanks John!

            Moving the script execution to correspond with a button push handled my particular situation.

            - Pat