Getting JavaFX WebEngine Interpreted Document


Question

Calling the getDocument() method on the WebEngine object for me only returns the source retrieved from the server, without the JavaScript being executed (there are still elements). This is the kind of source you would see if you used "View Source" in Chrome. How do I retrieve the interpreted source with the JavaScript already run?

public Browser() {
    WebView browser = new WebView();
    final WebEngine webEngine = browser.getEngine();
    webEngine.load("*******************************");

    webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
        @Override
        public void changed(ObservableValue<? extends State> ov, State oldState, State newState) {
            if (newState == State.SUCCEEDED) {
                Document doc = webEngine.getDocument();
                printDocument(doc);
            }
        }
    });
}
1
2
10/25/2014 12:29:45 PM

Accepted Answer

The problem you are facing is the following: the LoadWorker's state is set to SUCCEEDED before JavaScript is done running. JavaScript does in fact run (as shown in @James_D's reply) but there is no callback to signal when it finishes. AFAIK, there is no reliable way to detect when the WebEngine is done executing JS.

What you could do as a workaround is play a PauseTransition after the state changes to SUCCEEDED, which can be abused to act like a sleep on the JavaFX thread (JS is executed in the background thread that also loads the Document, so JS will not pause). However, sleeping (to wait for JS to finish) is inherently a violation of JavaFX's core principle never to block the UI thread. On top of that, waiting for a period of time does not guarantee that JS is done executing before that period passes.

I've faced the same problem and I have not found a decent solution. Let me know if you do!

1
3/9/2015 3:59:33 PM

This works as expected for me. In this example, the div contains a text node with the text that is set by the Javascript function:

import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class WebViewOnLoadExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        WebView webView = new WebView();
        webView.getEngine()
            .getLoadWorker()
            .stateProperty()
            .addListener((obs, oldState, newState) -> {
                if (newState == Worker.State.SUCCEEDED) {
                    Document doc = webView.getEngine().getDocument();
                    showNodeContent(doc, 0);
                }
            });
        BorderPane root = new BorderPane(webView);
        primaryStage.setScene(new Scene(root, 600, 400));
        primaryStage.show();

        webView.getEngine().loadContent("<html>"
                +"<head><script>"
                +"function setText() {"
                +"  document.getElementById(\"target\").appendChild(document.createTextNode(\"Hello World\"));"
                +"}"
                +"</script></head>"
                +"<body onload='setText()'>"
                +"<div id='target'></div></body></html>");
    }

    private void showNodeContent(Node n, int depth) {
        for (int i=0; i<depth; i++) {
            System.out.print(" ");
        }
        System.out.println(n.getNodeName()+":"+n.getNodeValue());
        NodeList children = n.getChildNodes() ;
        for (int i=0; i<children.getLength(); i++) {
            showNodeContent(children.item(i), depth+1);
        }
    }

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

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon