When executing a JavaScript in a WebEngine, how do I get the DOM using WebEngine.getDocument
after the results of any deferred script execution? Here is the minimal code to demonstrate the question.
The following HTML when rendered in a standard browser does the following:
- Runs an inline script that dynamically adds an out-of-line script element
- The out-of-line script then adds a DOM element to the body
Here is the starting HTML:
<html>
<head>
<script>var y=document.createElement('script');
y.src = 'http://example.com/js/bar.js';
document.head.appendChild(y);
</script>
</head>
<body>
</body>
</html>
Here is what the out-of-line script bar.js contains:
var x=document.createElement('div'); x.id='bar'; document.body.appendChild(x);
After executing the above HTML in a standard browser, here is what the DOM looks like in a real browser but this is not what I'm getting with WebEngine (which I show later):
<html>
<head>
<script>var y=document.createElement('script'); y.src = 'http://example.com/js/bar.js'; document.head.appendChild(y);</script>
<script src="http://example.com/js/bar.js"></script>
</head>
<body>
<div id="bar"></div>
</body>
</html>
I am trying to simulate the above using a headless JavaFX WebEngine using the following code (this is complete Java 8 code, minus the imports):
public class StackOverflowQuestion extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
webEngine.setJavaScriptEnabled(true);
webEngine.loadContent("<html><head></head><body></body></html>");
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener() {
@Override public void changed(ObservableValue o,Object oldVal, Object newVal) {
if (newVal == State.SUCCEEDED) {
webEngine.executeScript("var y=document.createElement('script'); y.src = 'http://example.com/js/bar.js'; document.head.appendChild(y);");
Document document = webEngine.getDocument();
printDOM(document);
} else {
System.out.println("Did not succeed");
}
}
});
}
private void printDOM(Document document) {
Transformer transformer;
try {
transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "html");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(document), new StreamResult(new OutputStreamWriter(System.out, "UTF-8")));
} catch (Exception e) {
e.printStackTrace();
}
}
static public void main(String [] args) {
try {
Application.launch(args);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The above code outputs the following DOM structure. Note that the DOM contains the results of the executeScript
call, but not the effects of what the out-of-line script produces. How do I get the DOM to include the dynamically added div element with id bar?
<HTML xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<SCRIPT src="http://example.com/js/bar.js"></SCRIPT>
</HEAD>
<BODY></BODY>
</HTML>