How to debug JavaFX webview in 2022
Asked Answered
A

1

6

How to debug a JavaFX webview? I find two ways,

  1. one is to attach external chrome debugger to JavaFX webview.
  2. Another is to inject firebug javascript.

Method#1 seems to use a bit of hacks like set access to private fields etc. Method#2 depends on firebuglite which seems to be obsolete now.

How should this be done in 2022. Are there any other methods, or better methods?

I am trying firebug lite approach as of now, and have made the javascript part of the maven project and trying to inject it as a string versus an external URL, because firebug seems to be properly public hosted nowhere now.

Refering to https://mcmap.net/q/501885/-javafx-webview-webengine-firebuglite-or-some-other-debugger

This is where I am right now :

package xyz.jphil.internal_browser;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;

/**
 *
 * @author Ivan
 */
public class WebkitDebugUtils {
    private static int resourcesPort = 0;

    public static void createHttpServerForLocalFiles() {
        int portStart = 64321, endPort = 65534;
        resourcesPort = portStart + (int) ((endPort * 1d - portStart * 1d) * Math.random());
        System.out.println("Starting internal server on " + resourcesPort);
        try {
            HttpServer server = HttpServer.create(new InetSocketAddress(resourcesPort), 0);
            HttpContext context = server.createContext("/");
            context.setHandler((req) -> {
                try (req) {
                    var p = req.getRequestURI();
                    System.out.println("Serving @ localhost:"+resourcesPort+" "+p);
                    var s = WebkitDebugUtils.class.getResourceAsStream(p.toString()).readAllBytes();
                    req.sendResponseHeaders(200, s.length);
                    req.getResponseBody().write(s);
                }catch(Exception a){
                    a.printStackTrace();
                }
            });
            server.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class JavaBridge {
        public void log(String text) {
            System.out.println(text);
        }
    }

    // Maintain a strong reference to prevent garbage collection:
    // https://bugs.openjdk.java.net/browse/JDK-8154127
    private final static JavaBridge bridge = new JavaBridge();

    public static void enableDebug(final WebEngine e) {
        JSObject window = (JSObject) e.executeScript("window");
        window.setMember("java", bridge);
        e.executeScript("""
                        console.log = function(message)
                        {
                            java.log(message);
                        };""");
        e.executeScript("console.log('console.log enabled');");
    }
    
    public static void enableFirebug(final WebEngine e) {
        var firebugurl = "http://localhost:"+resourcesPort+"/firebug-lite/firebug-lite.js";
        //https://raw.githubusercontent.com/stefanXO/firebug-lite/master/firebug-lite.js
        //https://lupatec.eu/getfirebug/firebug-lite-compressed.js
        var script= """
                    console.log("Inside script block");
                    if (!document.getElementById("FirebugLite")) {
                        E = document["createElement" + "NS"] && document.documentElement.namespaceURI;
                        E = E ? 
                             document["createElement" + "NS"](E, "script") : 
                             document["createElement"]("script");
                        E["setAttribute"]("id", "FirebugLite");
                        E["setAttribute"]("src", "{$firebugurl}#startOpened");
                        E["setAttribute"]("FirebugLite", "4");
                        (
                             document["getElementsByTagName"]("head")[0] 
                             || 
                             document["getElementsByTagName"]("body")[0]
                        ).appendChild(E);
                        E = new Image();
                        E["setAttribute"]("src", "{$firebugurl}#startOpened");
                    }
                    console.log("executed script block");
                    """.replace("{$firebugurl}", firebugurl);
        System.out.println("+++firebug thing+++");
        System.out.println(script);
        System.out.println("---firebug thing---");
        e.executeScript(script);
    }
}

Main method of the application

    //...
    public static void main(String[]args){
        WebkitDebugUtils.createHttpServerForLocalFiles();
        launch(args);
    }
    //...

In the JavaFx Stage initializing code section

    @Override
    public void start(final Stage primaryStage) throws Exception {
        // ....
        wv = new WebView();
        // ....
        javafx.scene.web.WebEngine e = wv.getEngine();
        // ....
        e.getLoadWorker().stateProperty().addListener((ov, t, t1) -> WebkitDebugUtils.enableDebug(e));
        e.documentProperty().addListener((ov, t, t1) -> WebkitDebugUtils.enableFirebug(e));
        // ....
    }

I am serving the files for firebug from the resources folder of the jar

Snippter of the custom firebug.js file

console.log("inside firebug-lite");
(function(){
//...
//firebug lite 1.4.0 code 
//..
})();
console.log("outside firebug-lite");

Output

Starting internal server on 65148
console.log enabled
console.log enabled

// some application specific output

+++firebug thing+++
if (!document.getElementById("FirebugLite")) {
    E = document["createElement" + "NS"] && document.documentElement.namespaceURI;
    E = E ?
         document["createElement" + "NS"](E, "script") :
         document["createElement"]("script");
    E["setAttribute"]("id", "FirebugLite");
    E["setAttribute"]("src", "http://localhost:65148/firebug-lite/firebug-lite.js#startOpened");
    E["setAttribute"]("FirebugLite", "4");
    (
         document["getElementsByTagName"]("head")[0]
         ||
         document["getElementsByTagName"]("body")[0]
    ).appendChild(E);
    E = new Image();
    E["setAttribute"]("src", "http://localhost:65148/firebug-lite/firebug-lite.js#startOpened");
}

---firebug thing---
Inside script block
executed script block
Serving @ localhost:65148 /firebug-lite/firebug-lite.js

// some application specific output

console.log enabled

Summary: nothing is happening, nothing is showing up in the console, I don't know the error, firebug is not even opening.

Update2: I think I am using the wrong version of firebug 1.5 or something and the old version I don't see any HTML file and thus it is dependent on the default website which is down at the moment.

Arlberg answered 26/5, 2022 at 5:32 Comment(5)
"How should this be done in 2022. Are there any other methods, or better methods?" - One method would be to procrastinate. Wait until 2023. :-)Vinaigrette
However, if you are looking for bugs, this is wrong! } catch (Exception a) { }. If it can't find the resource, you silently ignore it and return an empty string. Squashing exceptions is a bad idea.Vinaigrette
@StephenC thanks for pointing out, this is a quick and dirty copy paste I gave. I am still working on it.Arlberg
@StephenC I modified the code quite a bit, it reflects the current state, where I am, I have added output also. Any tip, guide, or suggestion, would be really appreciated.Arlberg
@StephenC Unfortunately, procrastination did not work well in this case. I guess sometimes one must just seize the day :-) As shown in 2022 in Luc's answer, firebug lite was still available from the location in his answer, but, as of 2024, it is no longer available.Babul
M
4

You can simply inject firebug-lite after loading your webpage.

I've tested on windows 10 with correto 18.0.2 JDK.

Here's an example: (Page can take some time to load)

package com.example.demo;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.scene.web.WebEngine;
import javafx.concurrent.Worker.State;

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

    @Override
    public void start(Stage myStage) {
        System.out.println("Demo for firebugLite");

        WebView myWebView = new WebView();
        myWebView.minHeight(1050);
        myWebView.prefWidth(1950);
        myWebView.prefHeight(1070);
        myWebView.minWidth(1050);
        final WebEngine myWebEngine = myWebView.getEngine();
        myWebEngine.getLoadWorker().stateProperty()
                .addListener((obs, oldValue, newValue) -> {
                    if (newValue == State.SUCCEEDED) {
                        System.out.println("finished loading");
                        myWebEngine.executeScript("var firebug=document.createElement('script');firebug.setAttribute('src','https://lupatec.eu/getfirebug/firebug-lite-compressed.js');document.body.appendChild(firebug);(function(){if(window.firebug.version){firebug.init();}else{setTimeout(arguments.callee);}})();void(firebug);");
                    }
                });
        myWebEngine.load("https://lupatec.eu/getfirebug/");
        VBox myBox = new VBox(myWebView);
        Scene myScene = new Scene(myBox, 1050, 600);
        myStage.setScene(myScene);
        myStage.show();
    }
}

Result: firebug lite in webview

Mathilda answered 26/7, 2022 at 14:1 Comment(1)
I tried this, and unfortunately, lupatec.eu did not resolve, so I firebug-lite is no longer available from that source.Babul

© 2022 - 2024 — McMap. All rights reserved.