# HG changeset patch # User Jaroslav Tulach # Date 1363249348 -3600 # Node ID 859804c78010bded0fa46b1450c87582dfda723e # Parent 023cda5b8b0bfd4cf3af11d8de17210125317754 Hacky way (relies on singleton) to get FX working. Can attach @On(event=CLICK) and show alert in FX WebView now diff -r 023cda5b8b0b -r 859804c78010 javaquery/api/pom.xml --- a/javaquery/api/pom.xml Wed Mar 13 16:20:03 2013 +0100 +++ b/javaquery/api/pom.xml Thu Mar 14 09:22:28 2013 +0100 @@ -73,5 +73,12 @@ ${project.version} test + + com.oracle + javafx + 2.2 + system + ${java.home}/lib/jfxrt.jar + diff -r 023cda5b8b0b -r 859804c78010 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Wed Mar 13 16:20:03 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Thu Mar 14 09:22:28 2013 +0100 @@ -265,7 +265,8 @@ } w.append(" }\n"); if (dispatchCnt > 0) { - w.append("class OnDispatch implements OnHandler {\n"); + // XXX: sun.misc.Trampoline cannot call public methods in non-public classes + w.append("public class OnDispatch implements OnHandler {\n"); w.append(" private final int dispatch;\n"); w.append(" OnDispatch(int d) { dispatch = d; }\n"); w.append(" public void onEvent(Object ev) {\n"); diff -r 023cda5b8b0b -r 859804c78010 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Wed Mar 13 16:20:03 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Thu Mar 14 09:22:28 2013 +0100 @@ -17,6 +17,8 @@ */ package org.apidesign.bck2brwsr.htmlpage.api; +import javafx.scene.web.WebEngine; +import netscape.javascript.JSObject; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** Represents a generic HTML element. @@ -73,13 +75,26 @@ + "};\n" ) final void on(OnEvent ev, OnHandler r) { + WebEngine e = web(); + JSObject add = (JSObject)e.executeScript( + "var x = {}; x.add = new Function('e','attr','r', " + + " 'window.document.getElementById(e)[attr] = function(ev) { var d = ev ? ev : null; try { r.onEvent(d); } catch (x) { alert(\"OK\" + r[\"onEvent\"] + \"e\" + d + x); } };'" + + "); x;"); + add.call("add", id, ev.id, r); + } + + private static WebEngine web() { + return (WebEngine) System.getProperties().get("webEngine"); } /** Shows alert message dialog in a browser. * @param msg the message to show */ @JavaScriptBody(args = "msg", body = "alert(msg);") - public static native void alert(String msg); + public static void alert(String msg) { + JSObject obj = (JSObject) web().executeScript("window"); + obj.call("alert", msg); + } /** Generic way to query any attribute of this element. * @param property name of the attribute diff -r 023cda5b8b0b -r 859804c78010 rt/launcher/pom.xml --- a/rt/launcher/pom.xml Wed Mar 13 16:20:03 2013 +0100 +++ b/rt/launcher/pom.xml Thu Mar 14 09:22:28 2013 +0100 @@ -58,6 +58,12 @@ ${project.groupId} vm4brwsr ${project.version} + + + emul.mini + org.apidesign.bck2brwsr + + diff -r 023cda5b8b0b -r 859804c78010 rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Wed Mar 13 16:20:03 2013 +0100 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Thu Mar 14 09:22:28 2013 +0100 @@ -91,6 +91,10 @@ public void addClassLoader(ClassLoader url) { this.loaders.add(url); } + + ClassLoader[] loaders() { + return loaders.toArray(new ClassLoader[loaders.size()]); + } public void showURL(String startpage) throws IOException { if (!startpage.startsWith("/")) { @@ -144,7 +148,7 @@ final ServerConfiguration conf = s.getServerConfiguration(); if (addClasses) { - conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); + conf.addHttpHandler(new VM(this, resources), "/bck2brwsr.js"); conf.addHttpHandler(new Classes(resources), "/classes/"); } return s; @@ -436,6 +440,27 @@ } } + void generateBck2BrwsrJS(StringBuilder sb, Bck2Brwsr.Resources loader) throws IOException { + Bck2Brwsr.generate(sb, loader); + sb.append( + "(function WrapperVM(global) {" + + " function ldCls(res) {\n" + + " var request = new XMLHttpRequest();\n" + + " request.open('GET', '/classes/' + res, false);\n" + + " request.send();\n" + + " if (request.status !== 200) return null;\n" + + " var arr = eval('(' + request.responseText + ')');\n" + + " return arr;\n" + + " }\n" + + " var prevvm = global.bck2brwsr;\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " args.unshift(ldCls);\n" + + " return prevvm.apply(null, args);\n" + + " };\n" + + "})(this);\n"); + } + private class Res implements Bck2Brwsr.Resources { @Override public InputStream get(String resource) throws IOException { @@ -498,27 +523,10 @@ private static class VM extends HttpHandler { private final String bck2brwsr; - public VM(Res loader) throws IOException { + public VM(Bck2BrwsrLauncher l, Res loader) throws IOException { StringBuilder sb = new StringBuilder(); - Bck2Brwsr.generate(sb, loader); - sb.append( - "(function WrapperVM(global) {" - + " function ldCls(res) {\n" - + " var request = new XMLHttpRequest();\n" - + " request.open('GET', '/classes/' + res, false);\n" - + " request.send();\n" - + " if (request.status !== 200) return null;\n" - + " var arr = eval('(' + request.responseText + ')');\n" - + " return arr;\n" - + " }\n" - + " var prevvm = global.bck2brwsr;\n" - + " global.bck2brwsr = function() {\n" - + " var args = Array.prototype.slice.apply(arguments);\n" - + " args.unshift(ldCls);\n" - + " return prevvm.apply(null, args);\n" - + " };\n" - + "})(this);\n" - ); + l.generateBck2BrwsrJS( + sb, loader); this.bck2brwsr = sb.toString(); } diff -r 023cda5b8b0b -r 859804c78010 rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Wed Mar 13 16:20:03 2013 +0100 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Thu Mar 14 09:22:28 2013 +0100 @@ -102,7 +102,7 @@ * @throws IOException if something goes wrong */ public static Closeable showURL(URLClassLoader classes, String startpage) throws IOException { - Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); + Bck2BrwsrLauncher l = new WebViewLauncher(); l.addClassLoader(classes); l.showURL(startpage); return l; @@ -116,7 +116,7 @@ * @exception IOException if something goes wrong. */ public static Closeable showDir(File directory, String startpage) throws IOException { - Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); + Bck2BrwsrLauncher l = new WebViewLauncher(); l.showDirectory(directory, startpage); return l; } diff -r 023cda5b8b0b -r 859804c78010 rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/WebViewLauncher.java --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/WebViewLauncher.java Wed Mar 13 16:20:03 2013 +0100 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/WebViewLauncher.java Thu Mar 14 09:22:28 2013 +0100 @@ -17,47 +17,32 @@ */ package org.apidesign.bck2brwsr.launcher; +import org.apidesign.bck2brwsr.launcher.impl.FXBrwsr; import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; -import java.util.List; import java.util.concurrent.Executors; -import javafx.application.Application; +import java.util.logging.Level; +import java.util.logging.Logger; import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.geometry.HPos; -import javafx.geometry.Insets; -import javafx.geometry.VPos; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.TextField; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.web.WebEngine; -import javafx.scene.web.WebView; -import javafx.stage.Stage; +import org.apidesign.bck2brwsr.launcher.impl.JVMBridge; +import org.apidesign.vm4brwsr.Bck2Brwsr; /** * * @author Jaroslav Tulach */ final class WebViewLauncher extends Bck2BrwsrLauncher { + private static final Logger LOG = Logger.getLogger(WebViewLauncher.class.getName()); static { try { Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); m.setAccessible(true); URL l = new URL("file://" + System.getProperty("java.home") + "/lib/jfxrt.jar"); - System.err.println("url : " + l); + LOG.log(Level.INFO, "url : {0}", l); m.invoke(ClassLoader.getSystemClassLoader(), l); } catch (Exception ex) { throw new LinkageError("Can't add jfxrt.jar on the classpath", ex); @@ -71,108 +56,39 @@ @Override protected Object[] showBrwsr(final URI url) throws IOException { try { + JVMBridge.registerClassLoaders(loaders()); Executors.newSingleThreadExecutor().submit(new Runnable() { @Override public void run() { - WebViewBrowser.launch(WebViewBrowser.class, url.toString()); + FXBrwsr.launch(FXBrwsr.class, url.toString()); } }); } catch (Throwable ex) { - ex.printStackTrace(); + LOG.log(Level.WARNING, "Can't open WebView", ex); } return null; } - + + @Override + void generateBck2BrwsrJS(StringBuilder sb, Bck2Brwsr.Resources loader) throws IOException { + sb.append("(function() {\n" + + " this.jvmBridge = new Array();\n" + + " this.jvmBridge.loadClass = Array.prototype.push;\n" + + " var bridge = this.jvmBridge;\n" + + " this.bck2brwsr = function() { return {\n" + + " loadClass : function(name) {\n" + + " return bridge.loadClass(name);\n" + + " }" + + " }; };\n" + + "})(window);\n" + ); + } + + + @Override public void close() throws IOException { Platform.exit(); } - - - /** - * Demonstrates a WebView object accessing a web page. - * - * @see javafx.scene.web.WebView - * @see javafx.scene.web.WebEngine - */ - public static class WebViewBrowser extends Application { - - @Override - public void start(Stage primaryStage) throws Exception { - Pane root = new WebViewPane(getParameters().getUnnamed()); - primaryStage.setScene(new Scene(root, 1024, 768)); - primaryStage.show(); - } - - /** - * Create a resizable WebView pane - */ - public class WebViewPane extends Pane { - - public WebViewPane(List params) { - VBox.setVgrow(this, Priority.ALWAYS); - setMaxWidth(Double.MAX_VALUE); - setMaxHeight(Double.MAX_VALUE); - - WebView view = new WebView(); - view.setMinSize(500, 400); - view.setPrefSize(500, 400); - final WebEngine eng = view.getEngine(); - final TextField locationField = new TextField(); - System.err.println("params : " + params); - if (params.size() > 0) { - eng.load(params.get(0)); - locationField.setText(params.get(0)); - } - locationField.setMaxHeight(Double.MAX_VALUE); - Button goButton = new Button("Go"); - goButton.setDefaultButton(true); - EventHandler goAction = new EventHandler() { - @Override - public void handle(ActionEvent event) { - eng.load(locationField.getText().startsWith("http://") ? locationField.getText() - : "http://" + locationField.getText()); - } - }; - goButton.setOnAction(goAction); - locationField.setOnAction(goAction); - eng.locationProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, String oldValue, String newValue) { - locationField.setText(newValue); - } - }); - GridPane grid = new GridPane(); - grid.setVgap(5); - grid.setHgap(5); - GridPane.setConstraints(locationField, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.SOMETIMES); - GridPane.setConstraints(goButton, 1, 0); - GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS); - grid.getColumnConstraints().addAll( - new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true), - new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true)); - grid.getChildren().addAll(locationField, goButton, view); - getChildren().add(grid); - } - - @Override - protected void layoutChildren() { - List managed = getManagedChildren(); - double width = getWidth(); - double height = getHeight(); - double top = getInsets().getTop(); - double right = getInsets().getRight(); - double left = getInsets().getLeft(); - double bottom = getInsets().getBottom(); - for (int i = 0; i < managed.size(); i++) { - Node child = managed.get(i); - layoutInArea(child, left, top, - width - left - right, height - top - bottom, - 0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER); - } - } - } - } - } diff -r 023cda5b8b0b -r 859804c78010 rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/FXBrwsr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/FXBrwsr.java Thu Mar 14 09:22:28 2013 +0100 @@ -0,0 +1,169 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.impl; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Application; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Worker; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.geometry.VPos; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebEvent; +import javafx.scene.web.WebView; +import javafx.stage.Stage; +import javax.swing.JOptionPane; +import netscape.javascript.JSObject; + +/** + * Demonstrates a WebView object accessing a web page. + * + * @see javafx.scene.web.WebView + * @see javafx.scene.web.WebEngine + */ +public class FXBrwsr extends Application { + private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName()); + + @Override + public void start(Stage primaryStage) throws Exception { + Pane root = new WebViewPane(getParameters().getUnnamed()); + primaryStage.setScene(new Scene(root, 1024, 768)); + primaryStage.show(); + } + + /** + * Create a resizable WebView pane + */ + private class WebViewPane extends Pane { + private final JVMBridge bridge = new JVMBridge(); + + public WebViewPane(List params) { + VBox.setVgrow(this, Priority.ALWAYS); + setMaxWidth(Double.MAX_VALUE); + setMaxHeight(Double.MAX_VALUE); + WebView view = new WebView(); + view.setMinSize(500, 400); + view.setPrefSize(500, 400); + final WebEngine eng = view.getEngine(); + final TextField locationField = new TextField(); + LOG.info("params : " + params); + if (params.size() > 0) { + eng.getLoadWorker().stateProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue ov, Worker.State t, Worker.State t1) { + LOG.info("about to init bck2brwsr"); + initBck2Brwsr(eng); + LOG.info("done init of bck2brwsr"); + } + }); + LOG.info("loading page " + params.get(0)); + eng.load(params.get(0)); + LOG.info("done loading page "); + locationField.setText(params.get(0)); + } + locationField.setMaxHeight(Double.MAX_VALUE); + Button goButton = new Button("Go"); + goButton.setDefaultButton(true); + EventHandler goAction = new EventHandler() { + @Override + public void handle(ActionEvent event) { + eng.load(locationField.getText().startsWith("http://") ? locationField.getText() : "http://" + locationField.getText()); + } + }; + goButton.setOnAction(goAction); + locationField.setOnAction(goAction); + eng.locationProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) { + locationField.setText(newValue); + } + }); + eng.setOnAlert(new EventHandler>() { + @Override + public void handle(WebEvent t) { + JOptionPane.showMessageDialog(null, t.getData()); + } + }); + GridPane grid = new GridPane(); + grid.setVgap(5); + grid.setHgap(5); + GridPane.setConstraints(locationField, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.SOMETIMES); + GridPane.setConstraints(goButton, 1, 0); + GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS); + grid.getColumnConstraints().addAll(new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true), new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true)); + grid.getChildren().addAll(locationField, goButton, view); + getChildren().add(grid); + } + + void initBck2Brwsr(WebEngine webEngine) { + JSObject jsobj = (JSObject) webEngine.executeScript("window"); + LOG.info("window: " + jsobj); + System.getProperties().put("webEngine", webEngine); + Object prev = jsobj.getMember("jvmBridge"); + if (prev instanceof JSObject) { + jsobj.setMember("jvmBridge", bridge); + JSObject jso = (JSObject) prev; + Object len = jso.getMember("length"); + if (len instanceof Integer) { + for (int i = 0; i < (Integer) len; i++) { + Object slt = jso.getSlot(i); + if (slt instanceof String) { + try { + bridge.loadClass((String) slt); + } catch (ClassNotFoundException ex) { + LOG.log(Level.SEVERE, null, ex); + } + } + } + LOG.info("bck2brwsr: " + jsobj.getMember("bck2brwsr")); + } + } + } + + @Override + protected void layoutChildren() { + List managed = getManagedChildren(); + double width = getWidth(); + double height = getHeight(); + double top = getInsets().getTop(); + double right = getInsets().getRight(); + double left = getInsets().getLeft(); + double bottom = getInsets().getBottom(); + for (int i = 0; i < managed.size(); i++) { + Node child = managed.get(i); + layoutInArea(child, left, top, width - left - right, height - top - bottom, 0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER); + } + } + } + +} diff -r 023cda5b8b0b -r 859804c78010 rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/JVMBridge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/JVMBridge.java Thu Mar 14 09:22:28 2013 +0100 @@ -0,0 +1,46 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.impl; + +/** + * + * @author Jaroslav Tulach + */ +public final class JVMBridge { + private static ClassLoader[] ldrs; + + public static void registerClassLoaders(ClassLoader[] loaders) { + ldrs = loaders.clone(); + } + + public Class loadClass(String name) throws ClassNotFoundException { + System.err.println("trying to load " + name); + ClassNotFoundException ex = null; + if (ldrs != null) for (ClassLoader l : ldrs) { + try { + return Class.forName(name, true, l); + } catch (ClassNotFoundException ex2) { + ex = ex2; + } + } + if (ex == null) { + ex = new ClassNotFoundException("No loaders"); + } + throw ex; + } +}