Java Embedded Browser

Why Embed a Browser?

So why embed a browser in an application, when anybody can just access a site via Firefox, Google Chrome, Internet Explorer or others? Why re-invent the wheel?

In terms of security, this is useful because it can restrict access to a web host through the application or it can prevent users from accessing third-party plugins to exploit vulnerabilities with the website.

Another reason is to provide a unique user experience with custom controls and functionality without creating individual plugins for each browser.

Although creating a browser application can lead to poor distribution, this is not important if the purpose is to develop a standalone application that requires access to other online facilities.

The following are code examples to use an embedded browser in a Java application using the following technologies:

  • JavaFX WebView
  • Standard Widget Toolkit (SWT)
  • JxBrowser

Each has their advantages and disadvantages.
After running each example, visit HTML5 Test to see how well it handles HTML5 components.

JavaFX WebView

While WebView has been introduced in the recent years as a JavaFX 2 feature, it is still has a lot of trouble rendering some HTML5 features.

It does, however, have excellent support in accessing DOM elements and events.

I would recommend using this for testing purposes or for creating a standalone web application.

public class WebViewExample extends Application {  
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("JavaFX: WebView");

        WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();

        String url = "http://www.google.com.au";
        webEngine.load(url);

        StackPane sp = new StackPane();
        sp.getChildren().add(webView);

        Scene root = new Scene(sp);

        stage.setScene(root);
        stage.show();
    }
}

Standard Widget Toolkit (SWT)

SWT has been around before JavaFX 1.0 and has very good compatibility across multiple platforms.

This is achieved by using the standard operating system containers and controls to construct the application, and makes for a clean and professional interface.

The other benefit is that the sites are rendered using the default installed browser engine, i.e. Windows = Internet Explorer, Mac OS X = Safari etc.

The main issue with SWT is that the browser is very limited in functionality except for display purposes.
It is difficult to query the DOM and capturing web element events must be performed through a series of JavaScript.

Since SWT is not part of the standard JDK, the following dependencies are required.

If you are using Eclipse then the libraries need to be added to the classpath:

1. Right click on the Java Project and click Properties

2. Select Java Build Path > Libraries > Add Variable...

3. Click the Configure Variables... button.

4. Click New to display the New Variable Entry dialog.

5. Enter the Name SWT_LIB and click File and browse to SWT library file.

NOTE: The SWT library is located in the Eclipse folder, e.g. C:/eclipse/plugins/org.eclipse.swt.win32.win32.x86_64_3.104.2.v20160212-1350.jar

6. Click OK out and go back to coding.

For Maven projects not using Eclipse, add the following to the POM.

For more details visit GitHub - maven-eclispe

<repositories>  
    <repository>
        <id>maven-eclipse-repo</id>
        <url>http://maven-eclipse.github.io/maven</url>
    </repository>
</repositories>

<properties>  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <swt.version>4.5.2</swt.version>
</properties>

<dependencies>  
    <dependency>
        <groupId>org.eclipse.swt</groupId>
        <artifactId>org.eclipse.swt.win32.win32.x86</artifactId>
        <version>${swt.version}</version>
        <!-- To use the debug jar, add this -->
        <classifier>debug</classifier>
    </dependency>
    <dependency>
        <groupId>org.eclipse.swt</groupId>
        <artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId>
        <version>${swt.version}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.swt</groupId>
        <artifactId>org.eclipse.swt.gtk.linux.x86</artifactId>
        <version>${swt.version}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.swt</groupId>
        <artifactId>org.eclipse.swt.gtk.linux.x86_64</artifactId>
        <version>${swt.version}</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.swt</groupId>
        <artifactId>org.eclipse.swt.cocoa.macosx.x86_64</artifactId>
        <version>${swt.version}</version>
    </dependency>
</dependencies>  

The following code creates a nice simple browser using SWT.

public final class SWTBrowserExample {  
    public static final void launch() {
        Display display = new Display();
        final Shell shell = new Shell(display);
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 3;
        shell.setLayout(gridLayout);
        ToolBar toolbar = new ToolBar(shell, SWT.NONE);
        ToolItem itemBack = new ToolItem(toolbar, SWT.PUSH);
        itemBack.setText("Back");
        ToolItem itemForward = new ToolItem(toolbar, SWT.PUSH);
        itemForward.setText("Forward");
        ToolItem itemStop = new ToolItem(toolbar, SWT.PUSH);
        itemStop.setText("Stop");
        ToolItem itemRefresh = new ToolItem(toolbar, SWT.PUSH);
        itemRefresh.setText("Refresh");
        ToolItem itemGo = new ToolItem(toolbar, SWT.PUSH);
        itemGo.setText("Go");

        GridData data = new GridData();
        data.horizontalSpan = 3;
        toolbar.setLayoutData(data);

        Label labelAddress = new Label(shell, SWT.NONE);
        labelAddress.setText("Address");

        final Text location = new Text(shell, SWT.BORDER);
        data = new GridData();
        data.horizontalAlignment = GridData.FILL;
        data.horizontalSpan = 2;
        data.grabExcessHorizontalSpace = true;
        location.setLayoutData(data);

        final Browser browser = new Browser(shell, SWT.NONE);
        data = new GridData();
        data.horizontalAlignment = GridData.FILL;
        data.verticalAlignment = GridData.FILL;
        data.horizontalSpan = 3;
        data.grabExcessHorizontalSpace = true;
        data.grabExcessVerticalSpace = true;
        browser.setLayoutData(data);

        final Label status = new Label(shell, SWT.NONE);
        data = new GridData(GridData.FILL_HORIZONTAL);
        data.horizontalSpan = 2;
        status.setLayoutData(data);

        final ProgressBar progressBar = new ProgressBar(shell, SWT.NONE);
        data = new GridData();
        data.horizontalAlignment = GridData.END;
        progressBar.setLayoutData(data);

        /* event handling */
        Listener listener = new Listener() {
            public void handleEvent(Event event) {
                ToolItem item = (ToolItem) event.widget;
                String string = item.getText();
                if (string.equals("Back"))
                    browser.back();
                else if (string.equals("Forward"))
                    browser.forward();
                else if (string.equals("Stop"))
                    browser.stop();
                else if (string.equals("Refresh"))
                    browser.refresh();
                else if (string.equals("Go"))
                    browser.setUrl(location.getText());
            }
        };

        browser.addProgressListener(new ProgressListener() {
            public void changed(ProgressEvent event) {
                if (event.total == 0)
                    return;
                int ratio = event.current * 100 / event.total;
                progressBar.setSelection(ratio);
            }

            public void completed(ProgressEvent event) {
                progressBar.setSelection(0);
            }
        });

        browser.addStatusTextListener(new StatusTextListener() {
            public void changed(StatusTextEvent event) {
                status.setText(event.text);
            }
        });

        browser.addLocationListener(new LocationListener() {
            public void changed(LocationEvent event) {
                if (event.top) {
                    location.setText(event.location);
                }
            }

            public void changing(LocationEvent event) {
            }
        });

        itemBack.addListener(SWT.Selection, listener);
        itemForward.addListener(SWT.Selection, listener);
        itemStop.addListener(SWT.Selection, listener);
        itemRefresh.addListener(SWT.Selection, listener);
        itemGo.addListener(SWT.Selection, listener);
        location.addListener(SWT.DefaultSelection, new Listener() {
            public void handleEvent(Event e) {
                browser.setUrl(location.getText());
            }
        });

        shell.open();
        browser.setUrl("http://www.google.com.au");

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

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

JxBrowser

JxBrowser is Chromium-based and is developed by TeamDev.

It is a very mature embedded browser that can be used in Java Swing or JavaFX 2.

The browser can support most of the HTML5 features, CSS3 and third-party plugins, such as Flash and Silverlight.

The only downside is that it is not free and it is quite expensive for the casual developer.

The pricing is as follows (as of 2016-03-31):

LICENSE TYPECOST
Per DeveloperUSD 1,799
ProjectUSD 5,299
Source CodeUSD 11,499
Company WideUSD 14,599

Refer to the JxBrowser - Licensing and Pricing section for more details.

However, you get what you pay for, and in addition to the JxBrowser library you get:

  • An API that is clean and easy to use
  • Multitude of functionality such as Audio & Video,
  • Printing, Zoom, Spell Checking and more.
  • Documentation that is organised, concise and with examples.
  • Support that has fast turnaround time and actually respond to feature requests and issues.
public class JxBrowserExample extends Application {  
    @Override
    public void init() throws Exception {
        // On Mac OS X Chromium engine must be initialized in non-UI thread.
        if (Environment.isMac()) {
            BrowserCore.initialize();
        }
    }

    @Override
    public void start(Stage primaryStage) {
        Browser browser = new Browser();
        BrowserView browserView = new BrowserView(browser);

        StackPane pane = new StackPane();
        pane.getChildren().add(browserView);
        Scene scene = new Scene(pane, 800, 600);
        primaryStage.setTitle("JxBrowser: JavaFX");
        primaryStage.setScene(scene);
        primaryStage.show();

        browser.loadURL("http://www.google.com.au");
    }

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

References


comments powered by Disqus