Search code examples
javascriptjavajavafxjavafx-2javafx-webengine

How to create a JavaFX WebBrowser javascript listener without a Member name


I have been using this snippet for creating js listener in my javafx browser:

       engine.getLoadWorker().stateProperty().addListener(
                (observable, oldValue, newValue) -> {
                    if (newValue == Worker.State.SUCCEEDED) {
                        System.out.println("changed event detected");
                        JSObject win = (JSObject) engine.executeScript("window");
                        win.setMember("java", new JavascriptListener());
                    }
                }
        );

But I would like to be able to call my functions without prefixing them with "java".

Is that possible?


Solution

  • Generally speaking, no.

    There are no (working) java/javascript functions to do this on a native scale. I tried the javascript Object.assign function, but it didn't work for variables or methods. Other, similar things wouldn't work properly on Java objects either

    If you are willing to expose yourself to the horrors of Java reflection, though, be my guest:

    public class JavaVariableAssigner {
    
                                                                     //javascript-stored objects do not appear to the
        private static ArrayList<Object> gcLock = new ArrayList();   //garbadge collector so we need to make sure
                                                                     //they don't get deleted by always keeping
                                                                     //a reference to them from this list
    
    
        public static void addAllMembers(WebEngine browser, Object obj) {
            gcLock.add(obj);
    
            ((JSObject) browser.executeScript("window")).setMember("java", obj);
    
            for(Method m : obj.getClass().getDeclaredMethods()) {
                ArrayList<String> parameterNames = new ArrayList();
                for(Parameter p : m.getParameters()) parameterNames.add(p.getName());
                String params = String.join(",", parameterNames.toArray(new String[0]));
                browser.executeScript("function "+m.getName()+"("+params+"){ return java."+m.getName()+"("+params+"); };");
            }
    
            for(Field  f : obj.getClass().getDeclaredFields()) {
                browser.executeScript("var "+f.getName()+" = java."+f.getName()+";");
            }
        }
    
    }
    

    All your code using the 'java' prefix will continue to work, but every method can now (hopefully) also just be accessed directly by typing its name.

    Like I (sort of) said, though, this method relies entirely on just stitching together javascript and evaling it so I would not recommend it.