RSS

Monthly Archives: September 2013

WebView

So, on with my adventures in JavaFX. I had a brilliant idea when I was arguing with Chrome and Opera recently. I decided that browser “tabs” would be much better managed as a lazily loaded tree, which would, in theory, be easily manageable (and testable) with JavaFX’s WebView class. It turned out that it wasn’t so simple as I had imagined.

WebView is a JavaFX parent node with a purpose. (There are a few other WebViews out there that have similar properties, such as the one for Android, but they are not directly related to JavaFX and are only tangentially connected to this article.) It takes a URL in its location property and loads a web page from the internet, then displays that page, just like a browser, in its content.

Holy hell. Could it get any more all-purpose? Immediate access to just about any page, with a cut & paste browser based off of Google’s WebKit. Unfortunately, my verdict is either that WebKit is incomplete, or it is drastically misrepresented. The following is code for an adequate web browser.

package example;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFieldBuilder;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderPaneBuilder;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.scene.web.WebViewBuilder;
import javafx.stage.Stage;

public class ExampleBrowser extends Application {

    private TextField url;
    private WebView webView;

    /**
     * @param args
     *            the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        //set up url to act, more or less, like an address bar
        url = TextFieldBuilder.create()
                .onKeyTyped(new EventHandler<KeyEvent>() {

                    @Override
                    public void handle(KeyEvent keyEvent) {
                        if (keyEvent.getCode().equals(KeyCode.ENTER))
                            webView.getEngine().load(url.getText());
                    }
                }).onAction(new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent arg0) {
                        webView.getEngine().load(url.getText());
                    }
                }).build();
        
        //get a lasting hold on the web engine
        webView = WebViewBuilder.create().build();
        final WebEngine webEngine = webView.getEngine();

        //keep track of the current page
        webEngine.locationProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> ov,
                    String oldVal, String newVal) {
                url.setText(newVal);
            }
        });

        //this will update both the engine, and the address bar
        webEngine.load("http://java-buddy.blogspot.com/");
        
        //establish the root node
        BorderPane root = BorderPaneBuilder.create().center(webView).top(url)
                .build();

        //and, action.
        primaryStage.setScene(new Scene(root, 640, 480));
        primaryStage.show();
    }}

You could just use url.textProperty().bind(webEngine.locationProperty()); but as locationProperty is read-only this would make the text field uneditable. Run this program, and you will have, more or less, a rudimentary web browser (minus the need to type “http://&#8221; every time, but that’s just a question of editing the URL field with regular expressions, well outside of this topic).

Perfect HTML 5. Nice.

Perfect HTML 5. Nice.

So, generally, pretty easy. But what about expansions to it? Let’s try watching a YouTube video, as an example. These are generally rendered in Flash by default. Unfortunately, there is no known way (as of yet) to support Flash through JavaFX. Generally I’m fine with that, I hate Flash; and YouTube does have a setting that allows you to stream videos in HTML 5 format, which is great; but not all sites do and let’s face it, video and audio are important as often as they are annoying. So, no Pandora through WebView—yet. Believe me, when I find a hack for this, or when a relevant update is made, I’ll be all over it.

And yet, there is another problem. Try right-clicking on the WebView. You’ll get a drop down menu with a few options regarding the page itself; for me they aren’t entirely useless. I’ve got “copy image to clipboard”, “open link in new window”, you name it. Generally, they’re functional, too. However, note that you can’t, from anywhere in the API, determine the address of a node right beneath a screen coordinate. That’s a problem for me. You can even copy the link to the clipboard, but at no time can you say “I want the URL of the node beneath the mouse cursor”. In my mind, that should be at tops a two or three line operation. I’ve been told (but have not yet verified) that WebView and its peripherals account for half of the JavaFX download size, which leads me to wonder why it is that it’s the least versatile. My conclusion is that it’s incomplete.

But all is not lost. There’s another thing that I’ve been doing a lot of lately, which is Java Scripting Management. Some time back, an engine called Rhino made a show in compiling and running ECMAScript (some of you may still know it as JavaScript) at near native speeds. It happens to be included in the JDK. One of the great advantages is, ECMAScript is capable of bouncing information up to the JavaFX application in a (highly inappropriately named) JSObject. This includes everything that ECMAScript has access to, which is, more or less, the entire universe, inclusive of what the mouse is over.

Part two will be my proxy to ECMA, which will keep an essential script on board to insert into the header of the page; a property to express what the mouse is over (by URL) at all times, among other things; and depending on what I find, possibly a hook for Chrome plug-ins, but don’t count on that last one. I don’t feel that this should be necessary, but it is, and the resultant power will be appreciable.

In WebView’s defense, it was generally designed to be a quick way to pull in a web app from the internet and run it without a third party browser. It does that nicely, and I fully intend to take advantage of it in the future. For those looking for a WYSIWYG editor, you might consider an HTMLEditor node, which does not provide all of the features of a browser but is easily adaptable to HTML production. Until next time, I bid you adieu. Drop me a line for comments and questions.

 
Leave a comment

Posted by on September 24, 2013 in JavaFX, Programming

 

Tags: , , , , , , , , , , , , ,