Java SWT web browser embedded in swing on the Raspberry PI

As I was working on a Rapsberry PI project I had a requirment to run a web browser inside a Java Swing project. I worked 3 month to get a browser on the Raspberry PI, I have tried various options including JavaFX webview, DJNativeSwing webBrowser, JCEF(embedded chromium) which all didnt work. finally after some time I found the answer: a native SWT web browser, and it worked with no errors, no exceptions, no missing DLLs.

here is the basic solution steps:
1. install requiered libraries:
sudo apt-get install libswt-gtk-3-jni libswt-gtk-3-java libswt-webkit-gtk-3-jni libwebkitgtk-1.0-0
2. create a simple java swt browser app as folows:

// Import SWT. import org.eclipse.swt.SWT; // Import UI components. import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; // Import the browser component. import org.eclipse.swt.browser.Browser; // Create a class for instantiating the encapsulating Java application. public final class Main { // Holds the Java window and its properties. private Display display; private Shell shell; private FillLayout layout; // Holds the browser widget. private Browser browser; // Application title and settings. private static final String title = "Sample JJR Application"; private static final int height = 800; private static final int width = 600; // RIA location. private static final String URL = "http://localhost/"; // Class constructor used for creating the basic Java UI, including the browser. public Main() { // Prepare window. this.display = new Display(); = new Shell(this.display);, Main.height);; this.layout = new FillLayout();; // Create browser. this.browser = new Browser(,SWT.NONE); this.browser.setUrl(Main.URL); // Add custom JavaScript function. new HelloWorld( this.browser, // Browser widget. "Hello" // JS function name. ); // Display window.; while (! { if (!this.display.readAndDispatch()) this.display.sleep(); } this.display.dispose(); } // Application entry point. public static void main(String[] args) { // Start the application. new Main(); } }

3. include the /usr/share/java/swt-gtk-3.8.jar in the class path or just import the jar in to your project, and export the project to a runnable jar.

to embed the browser on swing use the code above ( taken from )

import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.JPanel; import org.eclipse.swt.SWT; import org.eclipse.swt.awt.SWT_AWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /** * A simple canvas that encapsulates a SWT Browser instance. * Add it to a AWT or Swing container and call "connect()" after * the container has been made visible. */ public class BrowserCanvas extends Canvas { private Thread swtThread; private Browser swtBrowser; /** * Connect this canvas to a SWT shell with a Browser component * and starts a background thread to handle SWT events. This method * waits until the browser component is ready. */ public void connect() { if (this.swtThread == null) { final Canvas canvas = this; this.swtThread = new Thread() { @Override public void run() { try { Display display = new Display(); Shell shell = SWT_AWT.new_Shell(display, canvas); shell.setLayout(new FillLayout()); synchronized (this) { swtBrowser = new Browser(shell, SWT.NONE); this.notifyAll(); }; while (!isInterrupted() && !shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } shell.dispose(); display.dispose(); } catch (Exception e) { interrupt(); } } }; this.swtThread.start(); } // Wait for the Browser instance to become ready synchronized (this.swtThread) { while (this.swtBrowser == null) { try { this.swtThread.wait(100); } catch (InterruptedException e) { this.swtBrowser = null; this.swtThread = null; break; } } } } /** * Returns the Browser instance. Will return "null" * before "connect()" or after "disconnect()" has * been called. */ public Browser getBrowser() { return this.swtBrowser; } /** * Stops the swt background thread. */ public void disconnect() { if (swtThread != null) { swtBrowser = null; swtThread.interrupt(); swtThread = null; } } /** * Ensures that the SWT background thread * is stopped if this canvas is removed from * it's parent component (e.g. because the * frame has been disposed). */ @Override public void removeNotify() { super.removeNotify(); disconnect(); } /** * Opens a new JFrame with BrowserCanvas in it */ public static void main(String[] args) { // Required for Linux systems System.setProperty("sun.awt.xembedserver", "true"); // Create container canvas. Note that the browser // widget will not be created, yet. final BrowserCanvas browserCanvas = new BrowserCanvas(); browserCanvas.setPreferredSize(new Dimension(800, 600)); JPanel panel = new JPanel(new BorderLayout()); panel.add(browserCanvas, BorderLayout.CENTER); // Add container to Frame JFrame frame = new JFrame("My SWT Browser"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setContentPane(panel); frame.pack(); // This is VERY important: Make the frame visible BEFORE // connecting the SWT Shell and starting the event loop! frame.setVisible(true); browserCanvas.connect(); // Now we can open a webpage, but remember that we have // to use the SWT thread for this. browserCanvas.getBrowser().getDisplay().asyncExec(new Runnable() { @Override public void run() { browserCanvas.getBrowser().setUrl(""); Shell shell = browserCanvas.getBrowser().getShell(); shell.pack(); } }); } }

in this solution we use swing Jframe and a JPanel with a border layout. this layout works, but card layout does not work with this browser. you have to create the browser canvas and add it to the frame and set the frame visible before connecting to the swt browser, or else it will not work.

this solution works on the raspberry, but there are some problems. if you want to close the browser, and then display it again you simply call disconnect and connect, it will work on windows. on the raspberry when you try to call connect after disconnect it will crash the JVM. so you need to modify the code to keep the SWT Browser,Shell,Display not disposing, and do not disconnect the canvas from the JFrame. if you disconnect the canvas from the JFrame you cannot re connect the canvas. you need to dispose the SWT elements after the JFrame windowClosed event has fired on a WindowListener. its not a pretty solution but its the only solution I have found.

please feel free to comment about this, and ask questions.






הוסף תגובה

שם משתמש: