How to open the default browser from JavaFX and hand the HTML document to show as a parameter?

188 Views Asked by At

The situation is as follows: where I'm working right now OAuth is used for authentication. I'm building a JavaFX application that will be installed on several user laptops and right now authenticating using OAuth is working when I use the WebView browser that shows the login page of the PKCE flow by calling webEngine.loadContent(response.body().toString());

Other applications used by the same users on the same machines are running in the browser and as the credentials they use are the same for the different applications, Single Sign On is the goal of the organization. That means I would like to switch from using the WebView by throwing the HTML-login page I get returned from the authentication server to the default browser in Windows.

But I don't see a way.

First I found the Document.getDesktop().browse action, but that doesn't work in JavaFX. Application.getHostServices().showDocument is what I found, but that wants an URL as input, where I would like to supply a String, just as with webEngine.loadContent

So, how to open the default browser from JavaFX and hand the HTML document as a String to show as a parameter?

The only thing I found on SO that comes close to what I want and might work is this: Unable to load an html file (from local file system) using JxBrowser But, jxbrowser somehow seems to be in the maven central repo (https://mvnrepository.com/artifact/com.teamdev.jxbrowser/jxbrowser/7.12.2), but actually isn't, 'mvn clean install' can't find it and that seems logical as clicking on the jar / View All from the URL above 404's

2

There are 2 best solutions below

3
Slaw On

As stated in the comments, one approach is to save the HTML string to a file, then browse the file. This will work with either javafx.application.HostServices or java.awt.Desktop.

Here's an example:

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.awt.Desktop;
import java.nio.file.Files;
import java.nio.file.Path;

public class Main extends Application {

    private static final String HTML_DOCUMENT = """
            <!DOCTYPE html>
            
            <html>
                <meta charset="utf-8"/>
                <head>
                    <title>Browse from JavaFX Test</title>
                </head>
                <body>
                    <h1>Hello, World!</h1>
                </body>
            </html>
            """.stripIndent();

    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Show HTML");
        button.setOnAction(this::handleButtonAction);
        primaryStage.setScene(new Scene(new StackPane(button), 500, 300));
        primaryStage.show();
    }

    private void handleButtonAction(ActionEvent event) {
        event.consume();
        // Virtual thread requires Java 21+, or to --enable-preview
        // on Java 19 or Java 20. Use the "old" way of starting a 
        // (platform) thread if you need to.
        Thread.ofVirtual().start(() -> {
            try {
                Path file = Files.createTempFile(null, ".html");
                Files.writeString(file, HTML_DOCUMENT);

                Desktop.getDesktop().browse(file.toUri());
                // getHostServices().showDocument(file.toUri().toString());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });
    }
}

Note: The above creates a temporary file but makes no attempt to delete it.

Comment the Destkop line and uncomment the getHostServices() line to try the other API.

Note I tried to use a data URI with the following:

byte[] bytes = HTML_DOCUMENT.getBytes(StandardCharsets.UTF_8);
String data = Base64.getEncoder().encodeToString(bytes);
getHostServices().showDocument("data:text/html;charset=utf-8;base64," + data);

But all I got was a "You'll need a new app to open this data link" popup on Windows 10. Then the only option was to look for an app on the store. I saw the same behavior with Desktop.

0
JeroenV On

I could / should have replied earlier, but the solution for my situation (implementing the OAuth PKCE-flow) actually is easier than finding a way to get a HTML-document / String to the default browser. That document is part of a response from the authentication server and I can simply start the flow by sending the request (URL) that gives that response to the browser instead of building an HTTPRequest in the Java FX code and sending that over a HTTPClient after which I get this document in the response.

So now I simply do this in the main application

this.getHostServices().showDocument(url + "?" + params);

As this doesn't actually answer the question I asked, I won't mark my own answer as solution. For the question regarding the HTML document Slaws answer is probably the way to go