1.1 --- a/boot-fx/pom.xml Wed Sep 11 14:49:49 2013 +0200
1.2 +++ b/boot-fx/pom.xml Thu Sep 12 09:33:50 2013 +0200
1.3 @@ -23,6 +23,13 @@
1.4 <skip>false</skip>
1.5 </configuration>
1.6 </plugin>
1.7 + <plugin>
1.8 + <groupId>org.apache.maven.plugins</groupId>
1.9 + <artifactId>maven-surefire-plugin</artifactId>
1.10 + <configuration>
1.11 + <forkMode>always</forkMode>
1.12 + </configuration>
1.13 + </plugin>
1.14 </plugins>
1.15 </build>
1.16 <dependencies>
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java Thu Sep 12 09:33:50 2013 +0200
2.3 @@ -0,0 +1,74 @@
2.4 +/**
2.5 + * HTML via Java(tm) Language Bindings
2.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
2.7 + *
2.8 + * This program is free software: you can redistribute it and/or modify
2.9 + * it under the terms of the GNU General Public License as published by
2.10 + * the Free Software Foundation, version 2 of the License.
2.11 + *
2.12 + * This program is distributed in the hope that it will be useful,
2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.15 + * GNU General Public License for more details. apidesign.org
2.16 + * designates this particular file as subject to the
2.17 + * "Classpath" exception as provided by apidesign.org
2.18 + * in the License file that accompanied this code.
2.19 + *
2.20 + * You should have received a copy of the GNU General Public License
2.21 + * along with this program. Look for COPYING file in the top folder.
2.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
2.23 + */
2.24 +package net.java.html.boot.fx;
2.25 +
2.26 +import java.net.URL;
2.27 +import javafx.beans.value.ChangeListener;
2.28 +import javafx.beans.value.ObservableValue;
2.29 +import javafx.concurrent.Worker;
2.30 +import javafx.scene.web.WebView;
2.31 +import net.java.html.boot.BrowserBuilder;
2.32 +import org.apidesign.html.boot.fx.AbstractFXPresenter;
2.33 +
2.34 +/**
2.35 + *
2.36 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
2.37 + */
2.38 +public final class FXBrowsers {
2.39 + private FXBrowsers() {
2.40 + }
2.41 +
2.42 + public static void load(
2.43 + final WebView webView, final URL url,
2.44 + Class<?> onPageLoad, String methodName,
2.45 + String... args
2.46 + ) {
2.47 + class Load extends AbstractFXPresenter {
2.48 + @Override
2.49 + protected void waitFinished() {
2.50 + // don't wait
2.51 + }
2.52 +
2.53 + @Override
2.54 + protected WebView findView(URL resource) {
2.55 + final Worker<Void> w = webView.getEngine().getLoadWorker();
2.56 + w.stateProperty().addListener(new ChangeListener<Worker.State>() {
2.57 + @Override
2.58 + public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
2.59 + if (newState.equals(Worker.State.SUCCEEDED)) {
2.60 + onPageLoad();
2.61 + }
2.62 + if (newState.equals(Worker.State.FAILED)) {
2.63 + throw new IllegalStateException("Failed to load " + url);
2.64 + }
2.65 + }
2.66 + });
2.67 +
2.68 + return webView;
2.69 + }
2.70 + }
2.71 + BrowserBuilder.newBrowser(new Load()).
2.72 + loadPage(url.toExternalForm()).
2.73 + loadClass(onPageLoad).
2.74 + invoke(methodName, args).
2.75 + showAndWait();
2.76 + }
2.77 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/boot-fx/src/main/java/org/apidesign/html/boot/fx/AbstractFXPresenter.java Thu Sep 12 09:33:50 2013 +0200
3.3 @@ -0,0 +1,168 @@
3.4 +/**
3.5 + * HTML via Java(tm) Language Bindings
3.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.7 + *
3.8 + * This program is free software: you can redistribute it and/or modify
3.9 + * it under the terms of the GNU General Public License as published by
3.10 + * the Free Software Foundation, version 2 of the License.
3.11 + *
3.12 + * This program is distributed in the hope that it will be useful,
3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.15 + * GNU General Public License for more details. apidesign.org
3.16 + * designates this particular file as subject to the
3.17 + * "Classpath" exception as provided by apidesign.org
3.18 + * in the License file that accompanied this code.
3.19 + *
3.20 + * You should have received a copy of the GNU General Public License
3.21 + * along with this program. Look for COPYING file in the top folder.
3.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
3.23 + */
3.24 +package org.apidesign.html.boot.fx;
3.25 +
3.26 +import java.io.BufferedReader;
3.27 +import java.io.Reader;
3.28 +import java.net.URL;
3.29 +import java.util.ArrayList;
3.30 +import java.util.Arrays;
3.31 +import java.util.List;
3.32 +import java.util.logging.Level;
3.33 +import java.util.logging.Logger;
3.34 +import javafx.application.Platform;
3.35 +import javafx.scene.web.WebEngine;
3.36 +import javafx.scene.web.WebView;
3.37 +import netscape.javascript.JSObject;
3.38 +import org.apidesign.html.boot.spi.Fn;
3.39 +
3.40 +/**
3.41 + *
3.42 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.43 + */
3.44 +public abstract class AbstractFXPresenter implements Fn.Presenter {
3.45 + static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
3.46 + protected static int cnt;
3.47 + protected List<String> scripts;
3.48 + protected Runnable onLoad;
3.49 + protected WebEngine engine;
3.50 +
3.51 + @Override
3.52 + public Fn defineFn(String code, String... names) {
3.53 + StringBuilder sb = new StringBuilder();
3.54 + sb.append("(function() {");
3.55 + sb.append(" return function(");
3.56 + String sep = "";
3.57 + for (String n : names) {
3.58 + sb.append(sep).append(n);
3.59 + sep = ",";
3.60 + }
3.61 + sb.append(") {\n");
3.62 + sb.append(code);
3.63 + sb.append("};");
3.64 + sb.append("})()");
3.65 + if (LOG.isLoggable(Level.FINE)) {
3.66 + LOG.log(Level.FINE, "defining function #{0}", ++cnt);
3.67 + LOG.fine("-----");
3.68 + LOG.fine(code);
3.69 + LOG.fine("-----");
3.70 + }
3.71 + JSObject x = (JSObject) engine.executeScript(sb.toString());
3.72 + return new JSFn(this, x, cnt);
3.73 + }
3.74 +
3.75 + @Override
3.76 + public void loadScript(Reader code) throws Exception {
3.77 + BufferedReader r = new BufferedReader(code);
3.78 + StringBuilder sb = new StringBuilder();
3.79 + for (;;) {
3.80 + String l = r.readLine();
3.81 + if (l == null) {
3.82 + break;
3.83 + }
3.84 + sb.append(l).append('\n');
3.85 + }
3.86 + final String script = sb.toString();
3.87 + if (scripts != null) {
3.88 + scripts.add(script);
3.89 + }
3.90 + engine.executeScript(script);
3.91 + }
3.92 +
3.93 + protected final void onPageLoad() {
3.94 + if (scripts != null) {
3.95 + for (String s : scripts) {
3.96 + engine.executeScript(s);
3.97 + }
3.98 + }
3.99 + onLoad.run();
3.100 + }
3.101 +
3.102 + @Override
3.103 + public void displayPage(final URL resource, final Runnable onLoad) {
3.104 + this.onLoad = onLoad;
3.105 + final WebView view = findView(resource);
3.106 + this.engine = view.getEngine();
3.107 + try {
3.108 + if (FXInspect.initialize(engine)) {
3.109 + scripts = new ArrayList<String>();
3.110 + }
3.111 + } catch (Throwable ex) {
3.112 + ex.printStackTrace();
3.113 + }
3.114 +
3.115 + class Run implements Runnable {
3.116 +
3.117 + @Override
3.118 + public void run() {
3.119 + if (scripts != null) {
3.120 + view.setContextMenuEnabled(true);
3.121 + }
3.122 + engine.load(resource.toExternalForm());
3.123 + }
3.124 + }
3.125 + Run run = new Run();
3.126 + if (Platform.isFxApplicationThread()) {
3.127 + run.run();
3.128 + } else {
3.129 + Platform.runLater(run);
3.130 + }
3.131 + waitFinished();
3.132 + }
3.133 +
3.134 + protected abstract void waitFinished();
3.135 +
3.136 + protected abstract WebView findView(final URL resource);
3.137 +
3.138 + private static final class JSFn extends Fn {
3.139 +
3.140 + private final JSObject fn;
3.141 + private static int call;
3.142 + private final int id;
3.143 +
3.144 + public JSFn(AbstractFXPresenter p, JSObject fn, int id) {
3.145 + super(p);
3.146 + this.fn = fn;
3.147 + this.id = id;
3.148 + }
3.149 +
3.150 + @Override
3.151 + public Object handleInvoke(Object thiz, Object... args) throws Exception {
3.152 + try {
3.153 + if (LOG.isLoggable(Level.FINE)) {
3.154 + LOG.log(Level.FINE, "calling {0} function #{1}", new Object[]{++call, id});
3.155 + }
3.156 + List<Object> all = new ArrayList<Object>(args.length + 1);
3.157 + all.add(thiz == null ? fn : thiz);
3.158 + all.addAll(Arrays.asList(args));
3.159 + Object ret = fn.call("call", all.toArray()); // NOI18N
3.160 + return ret == fn ? null : ret;
3.161 + } catch (Error t) {
3.162 + t.printStackTrace();
3.163 + throw t;
3.164 + } catch (Exception t) {
3.165 + t.printStackTrace();
3.166 + throw t;
3.167 + }
3.168 + }
3.169 + }
3.170 +
3.171 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXBrwsr.java Thu Sep 12 09:33:50 2013 +0200
4.3 @@ -0,0 +1,169 @@
4.4 +/**
4.5 + * HTML via Java(tm) Language Bindings
4.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.7 + *
4.8 + * This program is free software: you can redistribute it and/or modify
4.9 + * it under the terms of the GNU General Public License as published by
4.10 + * the Free Software Foundation, version 2 of the License.
4.11 + *
4.12 + * This program is distributed in the hope that it will be useful,
4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.15 + * GNU General Public License for more details. apidesign.org
4.16 + * designates this particular file as subject to the
4.17 + * "Classpath" exception as provided by apidesign.org
4.18 + * in the License file that accompanied this code.
4.19 + *
4.20 + * You should have received a copy of the GNU General Public License
4.21 + * along with this program. Look for COPYING file in the top folder.
4.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
4.23 + */
4.24 +package org.apidesign.html.boot.fx;
4.25 +
4.26 +import java.net.URL;
4.27 +import java.util.concurrent.CountDownLatch;
4.28 +import java.util.concurrent.Executors;
4.29 +import javafx.application.Application;
4.30 +import javafx.application.Platform;
4.31 +import javafx.beans.value.ChangeListener;
4.32 +import javafx.beans.value.ObservableValue;
4.33 +import javafx.concurrent.Worker;
4.34 +import javafx.event.ActionEvent;
4.35 +import javafx.event.EventHandler;
4.36 +import javafx.geometry.Insets;
4.37 +import javafx.geometry.Pos;
4.38 +import javafx.scene.Scene;
4.39 +import javafx.scene.control.Button;
4.40 +import javafx.scene.layout.BorderPane;
4.41 +import javafx.scene.layout.VBox;
4.42 +import javafx.scene.text.Text;
4.43 +import javafx.scene.web.WebEvent;
4.44 +import javafx.scene.web.WebView;
4.45 +import javafx.stage.Modality;
4.46 +import javafx.stage.Stage;
4.47 +import org.openide.util.Exceptions;
4.48 +
4.49 +/** This is an implementation class, use {@link BrowserBuilder} API. Just
4.50 + * include this JAR on classpath and the {@link BrowserBuilder} API will find
4.51 + * this implementation automatically.
4.52 + */
4.53 +public class FXBrwsr extends Application {
4.54 + private static FXBrwsr INSTANCE;
4.55 + private static final CountDownLatch FINISHED = new CountDownLatch(1);
4.56 + private BorderPane root;
4.57 +
4.58 + public static synchronized WebView findWebView(final URL url, final FXPresenter onLoad) {
4.59 + if (INSTANCE == null) {
4.60 + Executors.newFixedThreadPool(1).submit(new Runnable() {
4.61 + @Override
4.62 + public void run() {
4.63 + try {
4.64 + FXBrwsr.launch(FXBrwsr.class);
4.65 + } catch (Throwable ex) {
4.66 + ex.printStackTrace();
4.67 + } finally {
4.68 + FINISHED.countDown();
4.69 + }
4.70 + }
4.71 + });
4.72 + }
4.73 + while (INSTANCE == null) {
4.74 + try {
4.75 + FXBrwsr.class.wait();
4.76 + } catch (InterruptedException ex) {
4.77 + // wait more
4.78 + }
4.79 + }
4.80 + if (!Platform.isFxApplicationThread()) {
4.81 + final WebView[] arr = {null};
4.82 + final CountDownLatch waitForResult = new CountDownLatch(1);
4.83 + Platform.runLater(new Runnable() {
4.84 + @Override
4.85 + public void run() {
4.86 + arr[0] = INSTANCE.newView(url, onLoad);
4.87 + waitForResult.countDown();
4.88 + }
4.89 + });
4.90 + for (;;) {
4.91 + try {
4.92 + waitForResult.await();
4.93 + break;
4.94 + } catch (InterruptedException ex) {
4.95 + Exceptions.printStackTrace(ex);
4.96 + }
4.97 + }
4.98 + return arr[0];
4.99 + } else {
4.100 + return INSTANCE.newView(url, onLoad);
4.101 + }
4.102 + }
4.103 +
4.104 + @Override
4.105 + public void start(Stage primaryStage) throws Exception {
4.106 + synchronized (FXBrwsr.class) {
4.107 + INSTANCE = this;
4.108 + FXBrwsr.class.notifyAll();
4.109 + }
4.110 + BorderPane r = new BorderPane();
4.111 + Scene scene = new Scene(r, 800, 600);
4.112 + primaryStage.setScene(scene);
4.113 + primaryStage.show();
4.114 + this.root = r;
4.115 + }
4.116 +
4.117 + private WebView newView(final URL url, final FXPresenter onLoad) {
4.118 + final WebView view = new WebView();
4.119 + view.setContextMenuEnabled(false);
4.120 + view.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() {
4.121 + @Override
4.122 + public void handle(WebEvent<String> t) {
4.123 + final Stage dialogStage = new Stage();
4.124 + dialogStage.initModality(Modality.WINDOW_MODAL);
4.125 + dialogStage.setTitle("Warning");
4.126 + final Button button = new Button("Close");
4.127 + final Text text = new Text(t.getData());
4.128 + VBox box = new VBox();
4.129 + box.setAlignment(Pos.CENTER);
4.130 + box.setSpacing(10);
4.131 + box.setPadding(new Insets(10));
4.132 + box.getChildren().addAll(text, button);
4.133 + dialogStage.setScene(new Scene(box));
4.134 + button.setCancelButton(true);
4.135 + button.setOnAction(new EventHandler<ActionEvent>() {
4.136 + @Override
4.137 + public void handle(ActionEvent t) {
4.138 + dialogStage.close();
4.139 + }
4.140 + });
4.141 + dialogStage.centerOnScreen();
4.142 + dialogStage.showAndWait();
4.143 + }
4.144 + });
4.145 + root.setCenter(view);
4.146 + final Worker<Void> w = view.getEngine().getLoadWorker();
4.147 + w.stateProperty().addListener(new ChangeListener<Worker.State>() {
4.148 + @Override
4.149 + public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
4.150 + if (newState.equals(Worker.State.SUCCEEDED)) {
4.151 + onLoad.onPageLoad();
4.152 + }
4.153 + if (newState.equals(Worker.State.FAILED)) {
4.154 + throw new IllegalStateException("Failed to load " + url);
4.155 + }
4.156 + }
4.157 + });
4.158 + return view;
4.159 + }
4.160 +
4.161 + static void waitFinished() {
4.162 + for (;;) {
4.163 + try {
4.164 + FINISHED.await();
4.165 + break;
4.166 + } catch (InterruptedException ex) {
4.167 + Exceptions.printStackTrace(ex);
4.168 + }
4.169 + }
4.170 + }
4.171 +
4.172 +}
5.1 --- a/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java Wed Sep 11 14:49:49 2013 +0200
5.2 +++ b/boot-fx/src/main/java/org/apidesign/html/boot/fx/FXPresenter.java Thu Sep 12 09:33:50 2013 +0200
5.3 @@ -29,33 +29,14 @@
5.4 import java.util.ArrayList;
5.5 import java.util.Arrays;
5.6 import java.util.List;
5.7 -import java.util.concurrent.CountDownLatch;
5.8 -import java.util.concurrent.Executors;
5.9 import java.util.logging.Level;
5.10 import java.util.logging.Logger;
5.11 -import javafx.application.Application;
5.12 import javafx.application.Platform;
5.13 -import javafx.beans.value.ChangeListener;
5.14 -import javafx.beans.value.ObservableValue;
5.15 -import javafx.concurrent.Worker;
5.16 -import javafx.event.ActionEvent;
5.17 -import javafx.event.EventHandler;
5.18 -import javafx.geometry.Insets;
5.19 -import javafx.geometry.Pos;
5.20 -import javafx.scene.Scene;
5.21 -import javafx.scene.control.Button;
5.22 -import javafx.scene.layout.BorderPane;
5.23 -import javafx.scene.layout.VBox;
5.24 -import javafx.scene.text.Text;
5.25 import javafx.scene.web.WebEngine;
5.26 -import javafx.scene.web.WebEvent;
5.27 import javafx.scene.web.WebView;
5.28 -import javafx.stage.Modality;
5.29 -import javafx.stage.Stage;
5.30 import net.java.html.boot.BrowserBuilder;
5.31 import netscape.javascript.JSObject;
5.32 import org.apidesign.html.boot.spi.Fn;
5.33 -import org.openide.util.Exceptions;
5.34 import org.openide.util.lookup.ServiceProvider;
5.35
5.36 /** This is an implementation class, use {@link BrowserBuilder} API. Just
5.37 @@ -65,9 +46,7 @@
5.38 * @author Jaroslav Tulach <jtulach@netbeans.org>
5.39 */
5.40 @ServiceProvider(service = Fn.Presenter.class)
5.41 -public final class FXPresenter implements Fn.Presenter {
5.42 - static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
5.43 -
5.44 +public final class FXPresenter extends AbstractFXPresenter {
5.45 static {
5.46 try {
5.47 try {
5.48 @@ -86,249 +65,12 @@
5.49 throw new LinkageError("Can't add jfxrt.jar on the classpath", ex);
5.50 }
5.51 }
5.52 -
5.53 - private List<String> scripts;
5.54 - private Runnable onLoad;
5.55 - private WebEngine engine;
5.56
5.57 - private static int cnt;
5.58 -
5.59 - @Override
5.60 - public Fn defineFn(String code, String... names) {
5.61 - StringBuilder sb = new StringBuilder();
5.62 - sb.append("(function() {");
5.63 - sb.append(" return function(");
5.64 - String sep = "";
5.65 - for (String n : names) {
5.66 - sb.append(sep).append(n);
5.67 - sep = ",";
5.68 - }
5.69 - sb.append(") {\n");
5.70 - sb.append(code);
5.71 - sb.append("};");
5.72 - sb.append("})()");
5.73 -
5.74 - if (LOG.isLoggable(Level.FINE)) {
5.75 - LOG.log(Level.FINE, "defining function #{0}", ++cnt);
5.76 - LOG.fine("-----");
5.77 - LOG.fine(code);
5.78 - LOG.fine("-----");
5.79 - }
5.80 -
5.81 - JSObject x = (JSObject) engine.executeScript(sb.toString());
5.82 - return new JSFn(x, cnt);
5.83 - }
5.84 -
5.85 - @Override
5.86 - public void loadScript(Reader code) throws Exception {
5.87 - BufferedReader r = new BufferedReader(code);
5.88 - StringBuilder sb = new StringBuilder();
5.89 - for (;;) {
5.90 - String l = r.readLine();
5.91 - if (l == null) {
5.92 - break;
5.93 - }
5.94 - sb.append(l).append('\n');
5.95 - }
5.96 - final String script = sb.toString();
5.97 - if (scripts != null) {
5.98 - scripts.add(script);
5.99 - }
5.100 - engine.executeScript(script);
5.101 - }
5.102 -
5.103 - final void onPageLoad() {
5.104 - if (scripts != null) {
5.105 - for (String s : scripts) {
5.106 - engine.executeScript(s);
5.107 - }
5.108 - }
5.109 - onLoad.run();
5.110 - }
5.111 -
5.112 - @Override
5.113 - public void displayPage(final URL resource, final Runnable onLoad) {
5.114 - this.onLoad = onLoad;
5.115 - final WebView view = FXBrwsr.findWebView(resource, this);
5.116 - this.engine = view.getEngine();
5.117 - try {
5.118 - if (FXInspect.initialize(engine)) {
5.119 - scripts = new ArrayList<String>();
5.120 - }
5.121 - } catch (Throwable ex) {
5.122 - ex.printStackTrace();
5.123 - }
5.124 - Platform.runLater(new Runnable() {
5.125 - @Override
5.126 - public void run() {
5.127 - if (scripts != null) {
5.128 - view.setContextMenuEnabled(true);
5.129 - }
5.130 - engine.load(resource.toExternalForm());
5.131 - }
5.132 - });
5.133 + protected void waitFinished() {
5.134 FXBrwsr.waitFinished();
5.135 }
5.136
5.137 - private static final class JSFn extends Fn {
5.138 - private final JSObject fn;
5.139 - private static int call;
5.140 - private final int id;
5.141 -
5.142 - public JSFn(JSObject fn, int id) {
5.143 - this.fn = fn;
5.144 - this.id = id;
5.145 - }
5.146 -
5.147 - @Override
5.148 - public Object invoke(Object thiz, Object... args) throws Exception {
5.149 - try {
5.150 - if (LOG.isLoggable(Level.FINE)) {
5.151 - LOG.log(Level.FINE, "calling {0} function #{1}", new Object[]{++call, id});
5.152 - }
5.153 - List<Object> all = new ArrayList<Object>(args.length + 1);
5.154 - all.add(thiz == null ? fn : thiz);
5.155 - all.addAll(Arrays.asList(args));
5.156 - Object ret = fn.call("call", all.toArray()); // NOI18N
5.157 - return ret == fn ? null : ret;
5.158 - } catch (Error t) {
5.159 - t.printStackTrace();
5.160 - throw t;
5.161 - } catch (Exception t) {
5.162 - t.printStackTrace();
5.163 - throw t;
5.164 - }
5.165 - }
5.166 + protected WebView findView(final URL resource) {
5.167 + return FXBrwsr.findWebView(resource, this);
5.168 }
5.169 -
5.170 - /** This is an implementation class, use {@link BrowserBuilder} API. Just
5.171 - * include this JAR on classpath and the {@link BrowserBuilder} API will find
5.172 - * this implementation automatically.
5.173 - */
5.174 - public static class FXBrwsr extends Application {
5.175 - private static FXBrwsr INSTANCE;
5.176 - private static final CountDownLatch FINISHED = new CountDownLatch(1);
5.177 -
5.178 - private BorderPane root;
5.179 -
5.180 - public synchronized static WebView findWebView(final URL url, final FXPresenter onLoad) {
5.181 - if (INSTANCE == null) {
5.182 - Executors.newFixedThreadPool(1).submit(new Runnable() {
5.183 - @Override
5.184 - public void run() {
5.185 - try {
5.186 - FXBrwsr.launch(FXBrwsr.class);
5.187 - } catch (Throwable ex) {
5.188 - ex.printStackTrace();
5.189 - } finally {
5.190 - FINISHED.countDown();
5.191 - }
5.192 - }
5.193 - });
5.194 - }
5.195 - while (INSTANCE == null) {
5.196 - try {
5.197 - FXBrwsr.class.wait();
5.198 - } catch (InterruptedException ex) {
5.199 - // wait more
5.200 - }
5.201 - }
5.202 - if (!Platform.isFxApplicationThread()) {
5.203 - final WebView[] arr = { null };
5.204 - final CountDownLatch waitForResult = new CountDownLatch(1);
5.205 - Platform.runLater(new Runnable() {
5.206 - @Override
5.207 - public void run() {
5.208 - arr[0] = INSTANCE.newView(url, onLoad);
5.209 - waitForResult.countDown();
5.210 - }
5.211 - });
5.212 - for (;;) {
5.213 - try {
5.214 - waitForResult.await();
5.215 - break;
5.216 - } catch (InterruptedException ex) {
5.217 - Exceptions.printStackTrace(ex);
5.218 - }
5.219 - }
5.220 - return arr[0];
5.221 - } else {
5.222 - return INSTANCE.newView(url, onLoad);
5.223 - }
5.224 - }
5.225 -
5.226 - @Override
5.227 - public void start(Stage primaryStage) throws Exception {
5.228 - synchronized (FXBrwsr.class) {
5.229 - INSTANCE = this;
5.230 - FXBrwsr.class.notifyAll();
5.231 - }
5.232 - BorderPane r = new BorderPane();
5.233 - Scene scene = new Scene(r, 800, 600);
5.234 - primaryStage.setScene(scene);
5.235 - primaryStage.show();
5.236 - this.root = r;
5.237 - }
5.238 -
5.239 - private WebView newView(final URL url, final FXPresenter onLoad) {
5.240 - final WebView view = new WebView();
5.241 - view.setContextMenuEnabled(false);
5.242 - view.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() {
5.243 - @Override
5.244 - public void handle(WebEvent<String> t) {
5.245 - final Stage dialogStage = new Stage();
5.246 - dialogStage.initModality(Modality.WINDOW_MODAL);
5.247 - dialogStage.setTitle("Warning");
5.248 - final Button button = new Button("Close");
5.249 - final Text text = new Text(t.getData());
5.250 -
5.251 - VBox box = new VBox();
5.252 - box.setAlignment(Pos.CENTER);
5.253 - box.setSpacing(10);
5.254 - box.setPadding(new Insets(10));
5.255 - box.getChildren().addAll(text, button);
5.256 -
5.257 - dialogStage.setScene(new Scene(box));
5.258 -
5.259 - button.setCancelButton(true);
5.260 - button.setOnAction(new EventHandler<ActionEvent>() {
5.261 - @Override
5.262 - public void handle(ActionEvent t) {
5.263 - dialogStage.close();
5.264 - }
5.265 - });
5.266 -
5.267 - dialogStage.centerOnScreen();
5.268 - dialogStage.showAndWait();
5.269 - }
5.270 - });
5.271 - root.setCenter(view);
5.272 -
5.273 - final Worker<Void> w = view.getEngine().getLoadWorker();
5.274 - w.stateProperty().addListener(new ChangeListener<Worker.State>() {
5.275 - @Override
5.276 - public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
5.277 - if (newState.equals(Worker.State.SUCCEEDED)) {
5.278 - onLoad.onPageLoad();
5.279 - }
5.280 - if (newState.equals(Worker.State.FAILED)) {
5.281 - throw new IllegalStateException("Failed to load " + url);
5.282 - }
5.283 - }
5.284 - });
5.285 - return view;
5.286 - }
5.287 -
5.288 - private static void waitFinished() {
5.289 - for (;;) {
5.290 - try {
5.291 - FINISHED.await();
5.292 - break;
5.293 - } catch (InterruptedException ex) {
5.294 - Exceptions.printStackTrace(ex);
5.295 - }
5.296 - }
5.297 - }
5.298 -
5.299 - }
5.300 }
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersTest.java Thu Sep 12 09:33:50 2013 +0200
6.3 @@ -0,0 +1,179 @@
6.4 +/**
6.5 + * HTML via Java(tm) Language Bindings
6.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.7 + *
6.8 + * This program is free software: you can redistribute it and/or modify
6.9 + * it under the terms of the GNU General Public License as published by
6.10 + * the Free Software Foundation, version 2 of the License.
6.11 + *
6.12 + * This program is distributed in the hope that it will be useful,
6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.15 + * GNU General Public License for more details. apidesign.org
6.16 + * designates this particular file as subject to the
6.17 + * "Classpath" exception as provided by apidesign.org
6.18 + * in the License file that accompanied this code.
6.19 + *
6.20 + * You should have received a copy of the GNU General Public License
6.21 + * along with this program. Look for COPYING file in the top folder.
6.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
6.23 + */
6.24 +package net.java.html.boot.fx;
6.25 +
6.26 +import java.net.URL;
6.27 +import java.util.concurrent.CountDownLatch;
6.28 +import javafx.application.Application;
6.29 +import javafx.application.Platform;
6.30 +import javafx.scene.Scene;
6.31 +import javafx.scene.layout.BorderPane;
6.32 +import javafx.scene.web.WebView;
6.33 +import javafx.stage.Stage;
6.34 +import net.java.html.js.JavaScriptBody;
6.35 +import static org.testng.Assert.assertEquals;
6.36 +import static org.testng.Assert.assertNotEquals;
6.37 +import static org.testng.Assert.assertNotNull;
6.38 +import static org.testng.Assert.assertNotSame;
6.39 +import org.testng.annotations.BeforeClass;
6.40 +import org.testng.annotations.Test;
6.41 +
6.42 +/**
6.43 + *
6.44 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.45 + */
6.46 +public class FXBrowsersTest {
6.47 +
6.48 + public FXBrowsersTest() {
6.49 + }
6.50 +
6.51 + @BeforeClass public void initFX() throws Throwable {
6.52 + new Thread("initFX") {
6.53 + @Override
6.54 + public void run() {
6.55 + App.launch(App.class);
6.56 + }
6.57 + }.start();
6.58 + App.CDL.await();
6.59 + }
6.60 +
6.61 + @Test
6.62 + public void behaviorOfTwoWebViewsAtOnce() throws Throwable {
6.63 + class R implements Runnable {
6.64 + CountDownLatch DONE = new CountDownLatch(1);
6.65 + Throwable t;
6.66 +
6.67 + @Override
6.68 + public void run() {
6.69 + try {
6.70 + doTest();
6.71 + } catch (Throwable ex) {
6.72 + t = ex;
6.73 + } finally {
6.74 + DONE.countDown();
6.75 + }
6.76 + }
6.77 +
6.78 + private void doTest() throws Throwable {
6.79 + URL u = FXBrowsersTest.class.getResource("/org/apidesign/html/boot/fx/empty.html");
6.80 + assertNotNull(u, "URL found");
6.81 + FXBrowsers.load(App.getV1(), u, OnPages.class, "first");
6.82 +
6.83 + }
6.84 + }
6.85 + R run = new R();
6.86 + Platform.runLater(run);
6.87 + run.DONE.await();
6.88 + for (int i = 0; i < 100; i++) {
6.89 + if (run.t != null) {
6.90 + throw run.t;
6.91 + }
6.92 + if (System.getProperty("finalSecond") == null) {
6.93 + Thread.sleep(100);
6.94 + }
6.95 + }
6.96 +
6.97 +
6.98 +
6.99 + assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one");
6.100 + assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one");
6.101 + }
6.102 +
6.103 + public static class OnPages {
6.104 + static Class<?> first;
6.105 + static Object firstWindow;
6.106 +
6.107 + public static void first() {
6.108 + first = OnPages.class;
6.109 + firstWindow = window();
6.110 + assertNotNull(firstWindow, "First window found");
6.111 +
6.112 + assertEquals(increment(), 1, "Now it is one");
6.113 +
6.114 + URL u = FXBrowsersTest.class.getResource("/org/apidesign/html/boot/fx/empty.html");
6.115 + assertNotNull(u, "URL found");
6.116 + FXBrowsers.load(App.getV2(), u, OnPages.class, "second", "Hello");
6.117 +
6.118 + assertEquals(increment(), 2, "Now it is two and not influenced by second view");
6.119 + System.setProperty("finalFirst", "" + increment());
6.120 + }
6.121 +
6.122 + public static void second(String... args) {
6.123 + assertEquals(args.length, 1, "One string argument");
6.124 + assertEquals(args[0], "Hello", "It is hello");
6.125 + assertEquals(first, OnPages.class, "Both views share the same classloader");
6.126 +
6.127 + Object window = window();
6.128 + assertNotNull(window, "Some window found");
6.129 + assertNotNull(firstWindow, "First window is known");
6.130 + assertNotSame(firstWindow, window, "The window objects should be different");
6.131 +
6.132 + assertEquals(increment(), 1, "Counting starts from zero");
6.133 + System.setProperty("finalSecond", "" + increment());
6.134 + }
6.135 +
6.136 + @JavaScriptBody(args = {}, body = "return window;")
6.137 + private static native Object window();
6.138 +
6.139 + @JavaScriptBody(args = {}, body = ""
6.140 + + "if (window.cnt) return ++window.cnt;"
6.141 + + "return window.cnt = 1;"
6.142 + )
6.143 + private static native int increment();
6.144 + }
6.145 +
6.146 + public static class App extends Application {
6.147 + static final CountDownLatch CDL = new CountDownLatch(1);
6.148 + private static BorderPane pane;
6.149 +
6.150 + /**
6.151 + * @return the v1
6.152 + */
6.153 + static WebView getV1() {
6.154 + return (WebView)System.getProperties().get("v1");
6.155 + }
6.156 +
6.157 + /**
6.158 + * @return the v2
6.159 + */
6.160 + static WebView getV2() {
6.161 + return (WebView)System.getProperties().get("v2");
6.162 + }
6.163 +
6.164 + @Override
6.165 + public void start(Stage stage) throws Exception {
6.166 + pane= new BorderPane();
6.167 + Scene scene = new Scene(pane, 800, 600);
6.168 + stage.setScene(scene);
6.169 +
6.170 + System.getProperties().put("v1", new WebView());
6.171 + System.getProperties().put("v2", new WebView());
6.172 +
6.173 + pane.setCenter(getV1());
6.174 + pane.setBottom(getV2());
6.175 +
6.176 + stage.show();
6.177 + CDL.countDown();
6.178 + }
6.179 +
6.180 +
6.181 + }
6.182 +}
7.1 --- a/boot-fx/src/test/java/org/apidesign/html/boot/fx/BootstrapTest.java Wed Sep 11 14:49:49 2013 +0200
7.2 +++ b/boot-fx/src/test/java/org/apidesign/html/boot/fx/BootstrapTest.java Thu Sep 12 09:33:50 2013 +0200
7.3 @@ -26,6 +26,8 @@
7.4 import java.util.List;
7.5 import java.util.concurrent.Executors;
7.6 import net.java.html.boot.BrowserBuilder;
7.7 +import org.apidesign.html.boot.impl.FnUtils;
7.8 +import org.apidesign.html.boot.spi.Fn;
7.9 import static org.testng.Assert.*;
7.10 import org.testng.annotations.Factory;
7.11 import org.testng.annotations.Test;
7.12 @@ -36,6 +38,7 @@
7.13 */
7.14 public class BootstrapTest {
7.15 private static Class<?> browserClass;
7.16 + private static Fn.Presenter browserPresenter;
7.17
7.18 public BootstrapTest() {
7.19 }
7.20 @@ -58,7 +61,7 @@
7.21 asSubclass(Annotation.class);
7.22 for (Method m : loadClass().getMethods()) {
7.23 if (m.getAnnotation(test) != null) {
7.24 - res.add(new KOFx(m));
7.25 + res.add(new KOFx(browserPresenter, m));
7.26 }
7.27 }
7.28 return res.toArray();
7.29 @@ -73,6 +76,7 @@
7.30
7.31 public static synchronized void ready(Class<?> browserCls) throws Exception {
7.32 browserClass = browserCls;
7.33 + browserPresenter = FnUtils.currentPresenter();
7.34 BootstrapTest.class.notifyAll();
7.35 }
7.36
8.1 --- a/boot-fx/src/test/java/org/apidesign/html/boot/fx/KOFx.java Wed Sep 11 14:49:49 2013 +0200
8.2 +++ b/boot-fx/src/test/java/org/apidesign/html/boot/fx/KOFx.java Thu Sep 12 09:33:50 2013 +0200
8.3 @@ -23,6 +23,8 @@
8.4 import java.lang.reflect.InvocationTargetException;
8.5 import java.lang.reflect.Method;
8.6 import javafx.application.Platform;
8.7 +import org.apidesign.html.boot.impl.FnUtils;
8.8 +import org.apidesign.html.boot.spi.Fn;
8.9 import org.testng.IHookCallBack;
8.10 import org.testng.IHookable;
8.11 import org.testng.ITest;
8.12 @@ -34,11 +36,13 @@
8.13 * @author Jaroslav Tulach <jtulach@netbeans.org>
8.14 */
8.15 public final class KOFx implements ITest, IHookable, Runnable {
8.16 + private final Fn.Presenter p;
8.17 private final Method m;
8.18 private Object result;
8.19 private Object inst;
8.20
8.21 - KOFx(Method m) {
8.22 + KOFx(Fn.Presenter p, Method m) {
8.23 + this.p = p;
8.24 this.m = m;
8.25 }
8.26
8.27 @@ -65,6 +69,7 @@
8.28 public synchronized void run() {
8.29 boolean notify = true;
8.30 try {
8.31 + FnUtils.currentPresenter(p);
8.32 if (inst == null) {
8.33 inst = m.getDeclaringClass().newInstance();
8.34 }
8.35 @@ -86,6 +91,7 @@
8.36 if (notify) {
8.37 notifyAll();
8.38 }
8.39 + FnUtils.currentPresenter(null);
8.40 }
8.41 }
8.42
9.1 --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Wed Sep 11 14:49:49 2013 +0200
9.2 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Thu Sep 12 09:33:50 2013 +0200
9.3 @@ -76,18 +76,22 @@
9.4 private Runnable onLoad;
9.5 private String methodName;
9.6 private String[] methodArgs;
9.7 + private final Object[] context;
9.8
9.9 - private BrowserBuilder() {
9.10 + private BrowserBuilder(Object[] context) {
9.11 + this.context = context;
9.12 }
9.13
9.14 /** Entry method to obtain a new browser builder. Follow by calling
9.15 * its instance methods like {@link #loadClass(java.lang.Class)} and
9.16 * {@link #loadPage(java.lang.String)}.
9.17 *
9.18 + * @param context any instances that should be available to the builder -
9.19 + * implemenation dependant
9.20 * @return new browser builder
9.21 */
9.22 - public static BrowserBuilder newBrowser() {
9.23 - return new BrowserBuilder();
9.24 + public static BrowserBuilder newBrowser(Object... context) {
9.25 + return new BrowserBuilder(context);
9.26 }
9.27
9.28 /** The class to load when the browser is initialized. This class
9.29 @@ -149,7 +153,6 @@
9.30 throw new NullPointerException("Need to specify resource via loadPage method");
9.31 }
9.32
9.33 - FImpl impl = new FImpl(clazz.getClassLoader());
9.34 URL url = null;
9.35 MalformedURLException mal = null;
9.36 try {
9.37 @@ -181,52 +184,79 @@
9.38 }
9.39 throw ise;
9.40 }
9.41 +
9.42 + Fn.Presenter dfnr = null;
9.43 + for (Object o : context) {
9.44 + if (o instanceof Fn.Presenter) {
9.45 + dfnr = (Fn.Presenter)o;
9.46 + break;
9.47 + }
9.48 + }
9.49
9.50 - for (Fn.Presenter dfnr : ServiceLoader.load(Fn.Presenter.class)) {
9.51 - final ClassLoader loader = FnUtils.newLoader(impl, dfnr, clazz.getClassLoader().getParent());
9.52 + if (dfnr == null) for (Fn.Presenter o : ServiceLoader.load(Fn.Presenter.class)) {
9.53 + dfnr = o;
9.54 + break;
9.55 + }
9.56 +
9.57 + if (dfnr == null) {
9.58 + throw new IllegalStateException("Can't find any Fn.Presenter");
9.59 + }
9.60 +
9.61 + final ClassLoader loader;
9.62 + if (FnUtils.isJavaScriptCapable(clazz.getClassLoader())) {
9.63 + loader = clazz.getClassLoader();
9.64 + } else {
9.65 + FImpl impl = new FImpl(clazz.getClassLoader());
9.66 + loader = FnUtils.newLoader(impl, dfnr, clazz.getClassLoader().getParent());
9.67 + }
9.68
9.69 - class OnPageLoad implements Runnable {
9.70 - @Override
9.71 - public void run() {
9.72 - try {
9.73 - Thread.currentThread().setContextClassLoader(loader);
9.74 - Class<?> newClazz = Class.forName(clazz.getName(), true, loader);
9.75 - if (browserClass != null) {
9.76 - browserClass[0] = newClazz;
9.77 - }
9.78 - if (onLoad != null) {
9.79 - onLoad.run();
9.80 - }
9.81 - INIT: if (methodName != null) {
9.82 - Throwable firstError = null;
9.83 - if (methodArgs.length == 0) {
9.84 - try {
9.85 - Method m = newClazz.getMethod(methodName);
9.86 - m.invoke(null);
9.87 - break INIT;
9.88 - } catch (Throwable ex) {
9.89 - firstError = ex;
9.90 - }
9.91 - }
9.92 + final Fn.Presenter currentP = dfnr;
9.93 + class OnPageLoad implements Runnable {
9.94 + @Override
9.95 + public void run() {
9.96 + try {
9.97 + Thread.currentThread().setContextClassLoader(loader);
9.98 + Class<?> newClazz = Class.forName(clazz.getName(), true, loader);
9.99 + if (browserClass != null) {
9.100 + browserClass[0] = newClazz;
9.101 + }
9.102 + if (onLoad != null) {
9.103 + onLoad.run();
9.104 + }
9.105 + INIT: if (methodName != null) {
9.106 + Throwable firstError = null;
9.107 + if (methodArgs.length == 0) {
9.108 try {
9.109 - Method m = newClazz.getMethod(methodName, String[].class);
9.110 - m.invoke(m, (Object) methodArgs);
9.111 + Method m = newClazz.getMethod(methodName);
9.112 + FnUtils.currentPresenter(currentP);
9.113 + m.invoke(null);
9.114 + break INIT;
9.115 } catch (Throwable ex) {
9.116 - if (firstError != null) {
9.117 - LOG.log(Level.SEVERE, "Can't call " + methodName, firstError);
9.118 - }
9.119 - LOG.log(Level.SEVERE, "Can't call " + methodName + " with args " + Arrays.toString(methodArgs), ex);
9.120 + firstError = ex;
9.121 + } finally {
9.122 + FnUtils.currentPresenter(null);
9.123 }
9.124 }
9.125 - } catch (ClassNotFoundException ex) {
9.126 - LOG.log(Level.SEVERE, "Can't load " + clazz.getName(), ex);
9.127 + try {
9.128 + Method m = newClazz.getMethod(methodName, String[].class);
9.129 + FnUtils.currentPresenter(currentP);
9.130 + m.invoke(m, (Object) methodArgs);
9.131 + } catch (Throwable ex) {
9.132 + if (firstError != null) {
9.133 + LOG.log(Level.SEVERE, "Can't call " + methodName, firstError);
9.134 + }
9.135 + LOG.log(Level.SEVERE, "Can't call " + methodName + " with args " + Arrays.toString(methodArgs), ex);
9.136 + } finally {
9.137 + FnUtils.currentPresenter(null);
9.138 + }
9.139 }
9.140 + } catch (ClassNotFoundException ex) {
9.141 + LOG.log(Level.SEVERE, "Can't load " + clazz.getName(), ex);
9.142 }
9.143 }
9.144 - dfnr.displayPage(url, new OnPageLoad());
9.145 - return;
9.146 }
9.147 - throw new IllegalStateException("Can't find any Fn.Definer");
9.148 + dfnr.displayPage(url, new OnPageLoad());
9.149 + return;
9.150 }
9.151
9.152 private static final class FImpl implements FindResources {
10.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Wed Sep 11 14:49:49 2013 +0200
10.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Sep 12 09:33:50 2013 +0200
10.3 @@ -35,12 +35,21 @@
10.4 * @author Jaroslav Tulach <jtulach@netbeans.org>
10.5 */
10.6 public final class FnUtils {
10.7 + private static final ThreadLocal<Fn.Presenter> CURRENT = new ThreadLocal<Fn.Presenter>();
10.8 +
10.9 private FnUtils() {
10.10 }
10.11 -
10.12 +
10.13 public static Fn define(Class<?> caller, String code, String... names) {
10.14 - JsClassLoader cl = (JsClassLoader)caller.getClassLoader();
10.15 - return cl.defineFn(code, names);
10.16 + return currentPresenter().defineFn(code, names);
10.17 + }
10.18 +
10.19 + public static boolean isJavaScriptCapable(ClassLoader l) {
10.20 + return l instanceof JsClassLoader;
10.21 + }
10.22 +
10.23 + public static boolean isValid(Fn fn) {
10.24 + return fn != null && fn.isValid();
10.25 }
10.26
10.27 public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
10.28 @@ -111,4 +120,18 @@
10.29 throw new IllegalStateException("Can't execute " + resource, ex);
10.30 }
10.31 }
10.32 +
10.33 + public static Fn.Presenter currentPresenter() {
10.34 + Fn.Presenter p = CURRENT.get();
10.35 + if (p == null) {
10.36 + throw new IllegalStateException("No current WebView context around!");
10.37 + }
10.38 + return p;
10.39 + }
10.40 +
10.41 + public static Fn.Presenter currentPresenter(Fn.Presenter p) {
10.42 + Fn.Presenter prev = CURRENT.get();
10.43 + CURRENT.set(p);
10.44 + return prev;
10.45 + }
10.46 }
11.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/JavaScriptProcesor.java Wed Sep 11 14:49:49 2013 +0200
11.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JavaScriptProcesor.java Thu Sep 12 09:33:50 2013 +0200
11.3 @@ -219,8 +219,18 @@
11.4 StringBuilder source = new StringBuilder();
11.5 source.append("package ").append(pkgName).append(";\n");
11.6 source.append("public final class $JsCallbacks$ {\n");
11.7 - source.append(" static final $JsCallbacks$ VM = new $JsCallbacks$();\n");
11.8 - source.append(" private $JsCallbacks$() {}\n");
11.9 + source.append(" static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
11.10 + source.append(" private final org.apidesign.html.boot.spi.Fn.Presenter p;\n");
11.11 + source.append(" private $JsCallbacks$ last;\n");
11.12 + source.append(" private $JsCallbacks$(org.apidesign.html.boot.spi.Fn.Presenter p) {\n");
11.13 + source.append(" this.p = p;\n");
11.14 + source.append(" }\n");
11.15 + source.append(" final $JsCallbacks$ current() {\n");
11.16 + source.append(" org.apidesign.html.boot.spi.Fn.Presenter now = org.apidesign.html.boot.impl.FnUtils.currentPresenter();\n");
11.17 + source.append(" if (now == p) return this;\n");
11.18 + source.append(" if (last != null && now == last.p) return last;\n");
11.19 + source.append(" return last = new $JsCallbacks$(now);\n");
11.20 + source.append(" }\n");
11.21 for (Map.Entry<String, ExecutableElement> entry : map.entrySet()) {
11.22 final String mangled = entry.getKey();
11.23 final ExecutableElement m = entry.getValue();
11.24 @@ -251,6 +261,7 @@
11.25 sep = ",";
11.26 }
11.27 source.append(" {\n");
11.28 + source.append(" org.apidesign.html.boot.spi.Fn.Presenter $$prev = org.apidesign.html.boot.impl.FnUtils.currentPresenter(p); try { \n");
11.29 source.append(" ");
11.30 if (m.getReturnType().getKind() != TypeKind.VOID) {
11.31 source.append("return ");
11.32 @@ -274,6 +285,7 @@
11.33 if (m.getReturnType().getKind() == TypeKind.VOID) {
11.34 source.append(" return null;\n");
11.35 }
11.36 + source.append(" } finally { org.apidesign.html.boot.impl.FnUtils.currentPresenter($$prev); }\n");
11.37 source.append(" }\n");
11.38 }
11.39 source.append("}\n");
12.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java Wed Sep 11 14:49:49 2013 +0200
12.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/JsClassLoader.java Thu Sep 12 09:33:50 2013 +0200
12.3 @@ -73,6 +73,9 @@
12.4 if (name.equals(Fn.class.getName())) {
12.5 return Fn.class;
12.6 }
12.7 + if (name.equals(Fn.Presenter.class.getName())) {
12.8 + return Fn.Presenter.class;
12.9 + }
12.10 if (name.equals(FnUtils.class.getName())) {
12.11 return FnUtils.class;
12.12 }
12.13 @@ -221,8 +224,13 @@
12.14 "Lorg/apidesign/html/boot/spi/Fn;"
12.15 );
12.16 super.visitInsn(Opcodes.DUP);
12.17 + super.visitMethodInsn(
12.18 + Opcodes.INVOKESTATIC,
12.19 + "org/apidesign/html/boot/impl/FnUtils", "isValid",
12.20 + "(Lorg/apidesign/html/boot/spi/Fn;)Z"
12.21 + );
12.22 Label ifNotNull = new Label();
12.23 - super.visitJumpInsn(Opcodes.IFNONNULL, ifNotNull);
12.24 + super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
12.25
12.26 // init Fn
12.27 super.visitInsn(Opcodes.POP);
12.28 @@ -346,6 +354,7 @@
12.29 int lastSlash = FindInClass.this.name.lastIndexOf('/');
12.30 String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
12.31 FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
12.32 + FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
12.33 FindInMethod.super.visitInsn(Opcodes.AASTORE);
12.34 }
12.35
13.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Wed Sep 11 14:49:49 2013 +0200
13.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Thu Sep 12 09:33:50 2013 +0200
13.3 @@ -22,13 +22,28 @@
13.4
13.5 import java.io.Reader;
13.6 import java.net.URL;
13.7 +import org.apidesign.html.boot.impl.FnUtils;
13.8
13.9 /**
13.10 *
13.11 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
13.12 */
13.13 public abstract class Fn {
13.14 - public abstract Object invoke(Object thiz, Object... args) throws Exception;
13.15 + private final Presenter presenter;
13.16 +
13.17 + protected Fn(Presenter presenter) {
13.18 + this.presenter = presenter;
13.19 + }
13.20 +
13.21 + public final boolean isValid() {
13.22 + return FnUtils.currentPresenter() == presenter;
13.23 + }
13.24 +
13.25 + public final Object invoke(Object thiz, Object... args) throws Exception {
13.26 + return handleInvoke(thiz, args);
13.27 + }
13.28 +
13.29 + protected abstract Object handleInvoke(Object thiz, Object... args) throws Exception;
13.30
13.31 public interface Presenter {
13.32 public Fn defineFn(String code, String... names);
14.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/FnTest.java Wed Sep 11 14:49:49 2013 +0200
14.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/FnTest.java Thu Sep 12 09:33:50 2013 +0200
14.3 @@ -34,12 +34,14 @@
14.4 import javax.script.ScriptException;
14.5 import org.apidesign.html.boot.spi.Fn;
14.6 import org.testng.annotations.BeforeClass;
14.7 +import org.testng.annotations.BeforeMethod;
14.8
14.9 /**
14.10 *
14.11 * @author Jaroslav Tulach <jtulach@netbeans.org>
14.12 */
14.13 public class FnTest extends JsClassLoaderBase {
14.14 + private static Fn.Presenter presenter;
14.15
14.16 public FnTest() {
14.17 }
14.18 @@ -79,9 +81,9 @@
14.19 sb.append("})()");
14.20 try {
14.21 final Object val = eng.eval(sb.toString());
14.22 - return new Fn() {
14.23 + return new Fn(this) {
14.24 @Override
14.25 - public Object invoke(Object thiz, Object... args) throws Exception {
14.26 + public Object handleInvoke(Object thiz, Object... args) throws Exception {
14.27 List<Object> all = new ArrayList<Object>(args.length + 1);
14.28 all.add(thiz == null ? val : thiz);
14.29 all.addAll(Arrays.asList(args));
14.30 @@ -111,9 +113,12 @@
14.31 }
14.32 Impl impl = new Impl();
14.33 ClassLoader loader = FnUtils.newLoader(impl, impl, parent);
14.34 -
14.35 + presenter = impl;
14.36
14.37 methodClass = loader.loadClass(JsMethods.class.getName());
14.38 }
14.39 -
14.40 +
14.41 + @BeforeMethod public void initPresenter() {
14.42 + FnUtils.currentPresenter(presenter);
14.43 + }
14.44 }
15.1 --- a/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderTest.java Wed Sep 11 14:49:49 2013 +0200
15.2 +++ b/boot/src/test/java/org/apidesign/html/boot/impl/JsClassLoaderTest.java Thu Sep 12 09:33:50 2013 +0200
15.3 @@ -34,12 +34,14 @@
15.4 import javax.script.ScriptException;
15.5 import org.testng.annotations.AfterClass;
15.6 import org.testng.annotations.BeforeClass;
15.7 +import org.testng.annotations.BeforeMethod;
15.8
15.9 /**
15.10 *
15.11 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
15.12 */
15.13 public class JsClassLoaderTest extends JsClassLoaderBase{
15.14 + private static Fn.Presenter loader;
15.15
15.16 @BeforeClass
15.17 public static void setUpClass() throws Exception {
15.18 @@ -49,13 +51,18 @@
15.19 final URL my = JsClassLoaderTest.class.getProtectionDomain().getCodeSource().getLocation();
15.20 ClassLoader parent = JsClassLoaderTest.class.getClassLoader().getParent();
15.21 final URLClassLoader ul = new URLClassLoader(new URL[] { my }, parent);
15.22 - JsClassLoader loader = new JsClassLoader(parent) {
15.23 + class MyCL extends JsClassLoader implements Fn.Presenter {
15.24 +
15.25 + public MyCL(ClassLoader parent) {
15.26 + super(parent);
15.27 + }
15.28 +
15.29 @Override
15.30 protected URL findResource(String name) {
15.31 return ul.getResource(name);
15.32 }
15.33 @Override
15.34 - protected Fn defineFn(String code, String... names) {
15.35 + public Fn defineFn(String code, String... names) {
15.36 StringBuilder sb = new StringBuilder();
15.37 sb.append("(function() {");
15.38 sb.append("return function(");
15.39 @@ -71,9 +78,9 @@
15.40 sb.append("})()");
15.41 try {
15.42 final Object val = eng.eval(sb.toString());
15.43 - return new Fn() {
15.44 + return new Fn(this) {
15.45 @Override
15.46 - public Object invoke(Object thiz, Object... args) throws Exception {
15.47 + public Object handleInvoke(Object thiz, Object... args) throws Exception {
15.48 List<Object> all = new ArrayList<Object>(args.length + 1);
15.49 all.add(thiz == null ? val : thiz);
15.50 all.addAll(Arrays.asList(args));
15.51 @@ -93,16 +100,27 @@
15.52
15.53 @Override
15.54 protected Enumeration<URL> findResources(String name) {
15.55 - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
15.56 + throw new UnsupportedOperationException();
15.57 }
15.58
15.59 @Override
15.60 - protected void loadScript(Reader code) throws ScriptException {
15.61 + public void loadScript(Reader code) throws ScriptException {
15.62 eng.eval(code);
15.63 }
15.64 +
15.65 + @Override
15.66 + public void displayPage(URL page, Runnable onPageLoad) {
15.67 + throw new UnsupportedOperationException();
15.68 + }
15.69 };
15.70
15.71 - methodClass = loader.loadClass(JsMethods.class.getName());
15.72 + MyCL l = new MyCL(parent);
15.73 + methodClass = l.loadClass(JsMethods.class.getName());
15.74 + loader = l;
15.75 + }
15.76 +
15.77 + @BeforeMethod public void initPresenter() {
15.78 + FnUtils.currentPresenter(loader);
15.79 }
15.80
15.81 @AfterClass
16.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java Wed Sep 11 14:49:49 2013 +0200
16.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/FXContext.java Thu Sep 12 09:33:50 2013 +0200
16.3 @@ -27,6 +27,8 @@
16.4 import javafx.application.Platform;
16.5 import net.java.html.js.JavaScriptBody;
16.6 import netscape.javascript.JSObject;
16.7 +import org.apidesign.html.boot.impl.FnUtils;
16.8 +import org.apidesign.html.boot.spi.Fn;
16.9 import org.apidesign.html.context.spi.Contexts;
16.10 import org.apidesign.html.json.spi.FunctionBinding;
16.11 import org.apidesign.html.json.spi.JSONCall;
16.12 @@ -44,35 +46,28 @@
16.13 *
16.14 * @author Jaroslav Tulach <jtulach@netbeans.org>
16.15 */
16.16 -@ServiceProvider(service = Contexts.Provider.class)
16.17 public final class FXContext
16.18 -implements Technology.BatchInit<JSObject>, Transfer, Contexts.Provider, WSTransfer<LoadWS> {
16.19 +implements Technology.BatchInit<JSObject>, Transfer, WSTransfer<LoadWS> {
16.20 static final Logger LOG = Logger.getLogger(FXContext.class.getName());
16.21 private static Boolean javaScriptEnabled;
16.22 + private final Fn.Presenter browserContext;
16.23 +
16.24 + public FXContext(Fn.Presenter browserContext) {
16.25 + this.browserContext = browserContext;
16.26 + }
16.27
16.28 @JavaScriptBody(args = {}, body = "return true;")
16.29 private static boolean isJavaScriptEnabledJs() {
16.30 return false;
16.31 }
16.32
16.33 - private static boolean isJavaScriptEnabled() {
16.34 + static boolean isJavaScriptEnabled() {
16.35 if (javaScriptEnabled != null) {
16.36 return javaScriptEnabled;
16.37 }
16.38 return javaScriptEnabled = isJavaScriptEnabledJs();
16.39 }
16.40
16.41 - @Override
16.42 - public void fillContext(Contexts.Builder context, Class<?> requestor) {
16.43 - if (isJavaScriptEnabled()) {
16.44 - context.register(Technology.class, this, 100);
16.45 - context.register(Transfer.class, this, 100);
16.46 - if (areWebSocketsSupported()) {
16.47 - context.register(WSTransfer.class, this, 100);
16.48 - }
16.49 - }
16.50 - }
16.51 -
16.52 final boolean areWebSocketsSupported() {
16.53 return LoadWS.isSupported();
16.54 }
16.55 @@ -153,11 +148,24 @@
16.56 }
16.57
16.58 @Override
16.59 - public void runSafe(Runnable r) {
16.60 + public void runSafe(final Runnable r) {
16.61 + class Wrap implements Runnable {
16.62 + @Override public void run() {
16.63 + Fn.Presenter prev = FnUtils.currentPresenter(browserContext);
16.64 + try {
16.65 + r.run();
16.66 + } finally {
16.67 + FnUtils.currentPresenter(prev);
16.68 + }
16.69 +
16.70 + }
16.71 + }
16.72 + Wrap w = new Wrap();
16.73 +
16.74 if (Platform.isFxApplicationThread()) {
16.75 - r.run();
16.76 + w.run();
16.77 } else {
16.78 - Platform.runLater(r);
16.79 + Platform.runLater(w);
16.80 }
16.81 }
16.82
16.83 @@ -175,4 +183,20 @@
16.84 public void close(LoadWS socket) {
16.85 socket.close();
16.86 }
16.87 +
16.88 + @ServiceProvider(service = Contexts.Provider.class)
16.89 + public static final class Prvdr implements Contexts.Provider {
16.90 + @Override
16.91 + public void fillContext(Contexts.Builder context, Class<?> requestor) {
16.92 + if (isJavaScriptEnabled()) {
16.93 + FXContext c = new FXContext(FnUtils.currentPresenter());
16.94 +
16.95 + context.register(Technology.class, c, 100);
16.96 + context.register(Transfer.class, c, 100);
16.97 + if (c.areWebSocketsSupported()) {
16.98 + context.register(WSTransfer.class, c, 100);
16.99 + }
16.100 + }
16.101 + }
16.102 + }
16.103 }
17.1 --- a/ko-fx/src/test/java/org/apidesign/html/kofx/KOFx.java Wed Sep 11 14:49:49 2013 +0200
17.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/KOFx.java Thu Sep 12 09:33:50 2013 +0200
17.3 @@ -23,6 +23,8 @@
17.4 import java.lang.reflect.InvocationTargetException;
17.5 import java.lang.reflect.Method;
17.6 import javafx.application.Platform;
17.7 +import org.apidesign.html.boot.impl.FnUtils;
17.8 +import org.apidesign.html.boot.spi.Fn;
17.9 import org.testng.ITest;
17.10 import org.testng.annotations.Test;
17.11
17.12 @@ -31,12 +33,14 @@
17.13 * @author Jaroslav Tulach <jtulach@netbeans.org>
17.14 */
17.15 public final class KOFx implements ITest, Runnable {
17.16 + private final Fn.Presenter p;
17.17 private final Method m;
17.18 private Object result;
17.19 private Object inst;
17.20 private int count;
17.21
17.22 - KOFx(Method m) {
17.23 + KOFx(Fn.Presenter p, Method m) {
17.24 + this.p = p;
17.25 this.m = m;
17.26 }
17.27
17.28 @@ -63,6 +67,7 @@
17.29 public synchronized void run() {
17.30 boolean notify = true;
17.31 try {
17.32 + FnUtils.currentPresenter(p);
17.33 if (inst == null) {
17.34 inst = m.getDeclaringClass().newInstance();
17.35 }
17.36 @@ -86,6 +91,7 @@
17.37 if (notify) {
17.38 notifyAll();
17.39 }
17.40 + FnUtils.currentPresenter(null);
17.41 }
17.42 }
17.43
18.1 --- a/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java Wed Sep 11 14:49:49 2013 +0200
18.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java Thu Sep 12 09:33:50 2013 +0200
18.3 @@ -36,6 +36,8 @@
18.4 import net.java.html.BrwsrCtx;
18.5 import net.java.html.boot.BrowserBuilder;
18.6 import net.java.html.js.JavaScriptBody;
18.7 +import org.apidesign.html.boot.impl.FnUtils;
18.8 +import org.apidesign.html.boot.spi.Fn;
18.9 import org.apidesign.html.context.spi.Contexts;
18.10 import org.apidesign.html.json.spi.Technology;
18.11 import org.apidesign.html.json.spi.Transfer;
18.12 @@ -55,6 +57,7 @@
18.13 @ServiceProvider(service = KnockoutTCK.class)
18.14 public final class KnockoutFXTest extends KnockoutTCK {
18.15 private static Class<?> browserClass;
18.16 + private static Fn.Presenter browserContext;
18.17
18.18 public KnockoutFXTest() {
18.19 }
18.20 @@ -99,7 +102,7 @@
18.21 asSubclass(Annotation.class);
18.22 for (Method m : c.getMethods()) {
18.23 if (m.getAnnotation(koTest) != null) {
18.24 - res.add(new KOFx(m));
18.25 + res.add(new KOFx(browserContext, m));
18.26 }
18.27 }
18.28 }
18.29 @@ -113,6 +116,7 @@
18.30
18.31 public static synchronized void initialized(Class<?> browserCls) throws Exception {
18.32 browserClass = browserCls;
18.33 + browserContext = FnUtils.currentPresenter();
18.34 KnockoutFXTest.class.notifyAll();
18.35 }
18.36
18.37 @@ -120,11 +124,12 @@
18.38 Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(KnockoutFXTest.class.getName());
18.39 Method m = classpathClass.getMethod("initialized", Class.class);
18.40 m.invoke(null, KnockoutFXTest.class);
18.41 + browserContext = FnUtils.currentPresenter();
18.42 }
18.43
18.44 @Override
18.45 public BrwsrCtx createContext() {
18.46 - FXContext fx = new FXContext();
18.47 + FXContext fx = new FXContext(browserContext);
18.48 Contexts.Builder cb = Contexts.newBuilder().
18.49 register(Technology.class, fx, 10).
18.50 register(Transfer.class, fx, 10);
19.1 --- a/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusFX.java Wed Sep 11 14:49:49 2013 +0200
19.2 +++ b/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusFX.java Thu Sep 12 09:33:50 2013 +0200
19.3 @@ -23,6 +23,8 @@
19.4 import java.lang.reflect.InvocationTargetException;
19.5 import java.lang.reflect.Method;
19.6 import javafx.application.Platform;
19.7 +import org.apidesign.html.boot.impl.FnUtils;
19.8 +import org.apidesign.html.boot.spi.Fn;
19.9 import org.testng.ITest;
19.10 import org.testng.annotations.Test;
19.11
19.12 @@ -31,12 +33,14 @@
19.13 * @author Jaroslav Tulach <jtulach@netbeans.org>
19.14 */
19.15 public final class TyrusFX implements ITest, Runnable {
19.16 + private final Fn.Presenter p;
19.17 private final Method m;
19.18 private Object result;
19.19 private Object inst;
19.20 private int count;
19.21
19.22 - TyrusFX(Method m) {
19.23 + TyrusFX(Fn.Presenter p, Method m) {
19.24 + this.p = p;
19.25 this.m = m;
19.26 }
19.27
19.28 @@ -63,6 +67,7 @@
19.29 public synchronized void run() {
19.30 boolean notify = true;
19.31 try {
19.32 + FnUtils.currentPresenter(p);
19.33 if (inst == null) {
19.34 inst = m.getDeclaringClass().newInstance();
19.35 }
19.36 @@ -86,6 +91,7 @@
19.37 if (notify) {
19.38 notifyAll();
19.39 }
19.40 + FnUtils.currentPresenter(null);
19.41 }
19.42 }
19.43
20.1 --- a/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusKnockoutTest.java Wed Sep 11 14:49:49 2013 +0200
20.2 +++ b/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusKnockoutTest.java Thu Sep 12 09:33:50 2013 +0200
20.3 @@ -36,6 +36,8 @@
20.4 import net.java.html.BrwsrCtx;
20.5 import net.java.html.boot.BrowserBuilder;
20.6 import net.java.html.js.JavaScriptBody;
20.7 +import org.apidesign.html.boot.impl.FnUtils;
20.8 +import org.apidesign.html.boot.spi.Fn;
20.9 import org.apidesign.html.context.spi.Contexts;
20.10 import org.apidesign.html.json.spi.Technology;
20.11 import org.apidesign.html.json.spi.Transfer;
20.12 @@ -56,6 +58,7 @@
20.13 @ServiceProvider(service = KnockoutTCK.class)
20.14 public final class TyrusKnockoutTest extends KnockoutTCK {
20.15 private static Class<?> browserClass;
20.16 + private static Fn.Presenter browserContext;
20.17
20.18 public TyrusKnockoutTest() {
20.19 }
20.20 @@ -92,7 +95,7 @@
20.21 asSubclass(Annotation.class);
20.22 for (Method m : c.getMethods()) {
20.23 if (m.getAnnotation(koTest) != null) {
20.24 - res.add(new TyrusFX(m));
20.25 + res.add(new TyrusFX(browserContext, m));
20.26 }
20.27 }
20.28 }
20.29 @@ -108,6 +111,7 @@
20.30
20.31 public static synchronized void initialized(Class<?> browserCls) throws Exception {
20.32 browserClass = browserCls;
20.33 + browserContext = FnUtils.currentPresenter();
20.34 TyrusKnockoutTest.class.notifyAll();
20.35 }
20.36
20.37 @@ -115,11 +119,12 @@
20.38 Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(TyrusKnockoutTest.class.getName());
20.39 Method m = classpathClass.getMethod("initialized", Class.class);
20.40 m.invoke(null, TyrusKnockoutTest.class);
20.41 + browserContext = FnUtils.currentPresenter();
20.42 }
20.43
20.44 @Override
20.45 public BrwsrCtx createContext() {
20.46 - FXContext fx = new FXContext();
20.47 + FXContext fx = new FXContext(browserContext);
20.48 TyrusContext tc = new TyrusContext();
20.49 Contexts.Builder cb = Contexts.newBuilder().
20.50 register(Technology.class, fx, 10).