2

I have embedded JavaFX's WebView in an Eclipse SWT application and need to load html content. I need to block the execution of the current thread until the webengine has finished loading the contents. Anyone familiar with this scenario and can give some hints and/or best practices to achieve this? It seems that both SWT and JavaFX run in the same UI thread.

Thanks!

erdal.karaca
  • 693
  • 1
  • 4
  • 20

3 Answers3

1

Show a modal window while the loading is not finished. You can not block the application thread because SWT/JavaFX operate on the same

tomsontom
  • 5,856
  • 2
  • 23
  • 21
  • Thanks for the hint, Tom! I am using SWT's BusyIndicator instead of a modal dialog. – erdal.karaca Oct 08 '13 at 13:39
  • In addition to the busy indicator I decided to collect my subsequent browser commands in a queue instead of executing them immediately. After the loading is finished, that queue will be processed. Also see this related question: http://stackoverflow.com/questions/33971137/how-to-wait-for-webengine-browser-initialization-in-javafx-application/33981584#33981584 – Stefan Nov 29 '15 at 10:12
1

A JavaFX webengine loads asynchronously per implementation. Any listener to the loadworker's stateproperty receives callback on the JavaFX thread. This thread also runs your UI, which is why you should NEVER block the JavaFX thread. In event based UI systems such as JavaFX, if you want to do some heavy work, you would do this in your own thread. Never, ever block the JavaFX thread.

The reason I'm answering is because I actually have a similar yet different situation in which I do need the load to be synchronous. My first idea was to wrap the load in my own thread and make that block, but the load operation needs to be called from the JavaFX thread. However, I figured out a way around this obstacle:

First I start a new thread in which I declare a semaphore (with 0 permits) from where I launch a Platform.runLater(...) that initiates an engine, adds a listener to the stateproperty and loads a URL, followed by trying to aquire the semaphore. This blocks, because the semaphore has no permits. In the listener's callback, I start another new thread in which I do my heavy work and from there I set some global data variable, after which I release the semaphore. This terminates my latter new thread and then informs the former new thread that the data is loaded so it may continue. This never blocks the JavaFX thread either.

When wrapping that in a function, you can emulate a synchronous load operation, but you should be cautious when using it: only call the function from outside the JavaFX thread. However, that makes perfect sense, because you should never be calling a synchronous (aka blocking) load on the JavaFX thread.

RDM
  • 4,986
  • 4
  • 34
  • 43
1

I had the same problem, and I needed to load the HTML from certain page in synchronous manner, so and I came up with the following solution:

import org.w3c.dom.Document;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class SynchronousExample  extends Application {

private static String HTML = "";
private static Stage stage;

@Override
public void start(Stage primaryStage) throws Exception {
    System.out.print( SynchronousExample.loadUrl("http://google.com") );

}

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

private static void attachDocumentPropertyListener(WebView webView){
    webView.getEngine().documentProperty().addListener(new ChangeListener<Document>() {
          @Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc) {
            String html = webView.getEngine().executeScript(
              "document.documentElement.outerHTML"
            ).toString();

            HTML = html;
            //after page load autmatically close the window
            if(stage != null){
                stage.close();
            }
          }
        });
}

public static String loadUrl(String url){
    WebView webView = new WebView();
    attachDocumentPropertyListener(webView);
    webView.getEngine().load(url);
    stage = new Stage();
    Scene scene = new Scene(webView);
    stage.setWidth(1);
    stage.setHeight(1);
    stage.setScene(scene);
    stage.showAndWait();
    // here HTML will be available
    return HTML;
}

}
DominikStyp
  • 360
  • 6
  • 10