1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java Thu Dec 12 23:54:32 2013 +0100
1.3 @@ -0,0 +1,180 @@
1.4 +/**
1.5 + * HTML via Java(tm) Language Bindings
1.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, version 2 of the License.
1.11 + *
1.12 + * This program is distributed in the hope that it will be useful,
1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.15 + * GNU General Public License for more details. apidesign.org
1.16 + * designates this particular file as subject to the
1.17 + * "Classpath" exception as provided by apidesign.org
1.18 + * in the License file that accompanied this code.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License
1.21 + * along with this program. Look for COPYING file in the top folder.
1.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
1.23 + */
1.24 +package net.java.html.boot.fx;
1.25 +
1.26 +import java.net.URL;
1.27 +import java.util.concurrent.CountDownLatch;
1.28 +import javafx.application.Application;
1.29 +import javafx.application.Platform;
1.30 +import javafx.scene.Scene;
1.31 +import javafx.scene.layout.BorderPane;
1.32 +import javafx.scene.web.WebView;
1.33 +import javafx.stage.Stage;
1.34 +import net.java.html.js.JavaScriptBody;
1.35 +import net.java.html.js.JavaScriptResource;
1.36 +import static org.testng.Assert.assertEquals;
1.37 +import static org.testng.Assert.assertNotNull;
1.38 +import static org.testng.Assert.assertNotSame;
1.39 +import org.testng.annotations.BeforeClass;
1.40 +import org.testng.annotations.Test;
1.41 +
1.42 +/**
1.43 + *
1.44 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
1.45 + */
1.46 +public class FXBrowsersOnResourceTest {
1.47 +
1.48 + public FXBrowsersOnResourceTest() {
1.49 + }
1.50 +
1.51 + @BeforeClass public void initFX() throws Throwable {
1.52 + new Thread("initFX") {
1.53 + @Override
1.54 + public void run() {
1.55 + App.launch(App.class);
1.56 + }
1.57 + }.start();
1.58 + App.CDL.await();
1.59 + }
1.60 +
1.61 + @Test
1.62 + public void behaviorOfTwoWebViewsAtOnce() throws Throwable {
1.63 + class R implements Runnable {
1.64 + CountDownLatch DONE = new CountDownLatch(1);
1.65 + Throwable t;
1.66 +
1.67 + @Override
1.68 + public void run() {
1.69 + try {
1.70 + doTest();
1.71 + } catch (Throwable ex) {
1.72 + t = ex;
1.73 + } finally {
1.74 + DONE.countDown();
1.75 + }
1.76 + }
1.77 +
1.78 + private void doTest() throws Throwable {
1.79 + URL u = FXBrowsersOnResourceTest.class.getResource("/org/apidesign/html/boot/fx/empty.html");
1.80 + assertNotNull(u, "URL found");
1.81 + FXBrowsers.load(App.getV1(), u, OnPages.class, "first");
1.82 +
1.83 + }
1.84 + }
1.85 + R run = new R();
1.86 + Platform.runLater(run);
1.87 + run.DONE.await();
1.88 + for (int i = 0; i < 100; i++) {
1.89 + if (run.t != null) {
1.90 + throw run.t;
1.91 + }
1.92 + if (System.getProperty("finalSecond") == null) {
1.93 + Thread.sleep(100);
1.94 + }
1.95 + }
1.96 +
1.97 +
1.98 +
1.99 + assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one");
1.100 + assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one");
1.101 + }
1.102 +
1.103 + @JavaScriptResource("wnd.js")
1.104 + public static class OnPages {
1.105 + static Class<?> first;
1.106 + static Object firstWindow;
1.107 +
1.108 + public static void first() {
1.109 + first = OnPages.class;
1.110 + firstWindow = window();
1.111 + assertNotNull(firstWindow, "First window found");
1.112 +
1.113 + assertEquals(increment(), 1, "Now it is one");
1.114 +
1.115 + URL u = FXBrowsersOnResourceTest.class.getResource("/org/apidesign/html/boot/fx/empty.html");
1.116 + assertNotNull(u, "URL found");
1.117 + FXBrowsers.load(App.getV2(), u, OnPages.class, "second", "Hello");
1.118 +
1.119 + assertEquals(increment(), 2, "Now it is two and not influenced by second view");
1.120 + System.setProperty("finalFirst", "" + increment());
1.121 + }
1.122 +
1.123 + public static void second(String... args) {
1.124 + assertEquals(args.length, 1, "One string argument");
1.125 + assertEquals(args[0], "Hello", "It is hello");
1.126 + assertEquals(first, OnPages.class, "Both views share the same classloader");
1.127 +
1.128 + Object window = window();
1.129 + assertNotNull(window, "Some window found");
1.130 + assertNotNull(firstWindow, "First window is known");
1.131 + assertNotSame(firstWindow, window, "The window objects should be different");
1.132 +
1.133 + assertEquals(increment(), 1, "Counting starts from zero");
1.134 + System.setProperty("finalSecond", "" + increment());
1.135 + }
1.136 +
1.137 + @JavaScriptBody(args = {}, body = "return wnd;")
1.138 + private static native Object window();
1.139 +
1.140 + @JavaScriptBody(args = {}, body = ""
1.141 + + "if (wnd.cnt) return ++wnd.cnt;"
1.142 + + "return wnd.cnt = 1;"
1.143 + )
1.144 + private static native int increment();
1.145 + }
1.146 +
1.147 + public static class App extends Application {
1.148 + static final CountDownLatch CDL = new CountDownLatch(1);
1.149 + private static BorderPane pane;
1.150 +
1.151 + /**
1.152 + * @return the v1
1.153 + */
1.154 + static WebView getV1() {
1.155 + return (WebView)System.getProperties().get("v1");
1.156 + }
1.157 +
1.158 + /**
1.159 + * @return the v2
1.160 + */
1.161 + static WebView getV2() {
1.162 + return (WebView)System.getProperties().get("v2");
1.163 + }
1.164 +
1.165 + @Override
1.166 + public void start(Stage stage) throws Exception {
1.167 + pane= new BorderPane();
1.168 + Scene scene = new Scene(pane, 800, 600);
1.169 + stage.setScene(scene);
1.170 +
1.171 + System.getProperties().put("v1", new WebView());
1.172 + System.getProperties().put("v2", new WebView());
1.173 +
1.174 + pane.setCenter(getV1());
1.175 + pane.setBottom(getV2());
1.176 +
1.177 + stage.show();
1.178 + CDL.countDown();
1.179 + }
1.180 +
1.181 +
1.182 + }
1.183 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js Thu Dec 12 23:54:32 2013 +0100
2.3 @@ -0,0 +1,27 @@
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 +if (typeof wnd !== 'undefined') {
2.25 + throw 'Window should not be defined yet: ' + wnd;
2.26 +}
2.27 +
2.28 +wnd = {
2.29 +};
2.30 +
3.1 --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Dec 12 18:33:11 2013 +0100
3.2 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Dec 12 23:54:32 2013 +0100
3.3 @@ -156,6 +156,7 @@
3.4 private String name;
3.5 private int found;
3.6 private ClassLoader loader;
3.7 + private String resource;
3.8
3.9 public FindInClass(ClassLoader l, ClassVisitor cv) {
3.10 super(Opcodes.ASM4, cv);
3.11 @@ -263,6 +264,14 @@
3.12 "org/apidesign/html/boot/spi/Fn", "define",
3.13 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
3.14 );
3.15 + if (resource != null) {
3.16 + super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
3.17 + super.visitLdcInsn(resource);
3.18 + super.visitMethodInsn(Opcodes.INVOKESTATIC,
3.19 + "org/apidesign/html/boot/spi/Fn", "preload",
3.20 + "(Lorg/apidesign/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
3.21 + );
3.22 + }
3.23 super.visitInsn(Opcodes.DUP);
3.24 super.visitFieldInsn(
3.25 Opcodes.PUTSTATIC, FindInClass.this.name,
3.26 @@ -487,11 +496,11 @@
3.27 public void visit(String attrName, Object value) {
3.28 String relPath = (String) value;
3.29 if (relPath.startsWith("/")) {
3.30 - loadScript(loader, relPath);
3.31 + resource = relPath;
3.32 } else {
3.33 int last = name.lastIndexOf('/');
3.34 String fullPath = name.substring(0, last + 1) + relPath;
3.35 - loadScript(loader, fullPath);
3.36 + resource = fullPath;
3.37 }
3.38 }
3.39 }
4.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Thu Dec 12 18:33:11 2013 +0100
4.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Thu Dec 12 23:54:32 2013 +0100
4.3 @@ -21,8 +21,15 @@
4.4 package org.apidesign.html.boot.spi;
4.5
4.6 import java.io.Closeable;
4.7 +import java.io.InputStream;
4.8 +import java.io.InputStreamReader;
4.9 import java.io.Reader;
4.10 import java.net.URL;
4.11 +import java.util.HashMap;
4.12 +import java.util.HashSet;
4.13 +import java.util.Map;
4.14 +import java.util.Set;
4.15 +import java.util.WeakHashMap;
4.16 import net.java.html.js.JavaScriptBody;
4.17 import org.apidesign.html.boot.impl.FnContext;
4.18
4.19 @@ -87,6 +94,31 @@
4.20 return FnContext.currentPresenter().defineFn(code, names);
4.21 }
4.22
4.23 + private static final Map<String,Set<Presenter>> LOADED = new HashMap<String, Set<Presenter>>();
4.24 + public static Fn preload(final Fn fn, final Class<?> caller, final String resource) {
4.25 + return new Fn() {
4.26 + @Override
4.27 + public Object invoke(Object thiz, Object... args) throws Exception {
4.28 + final Presenter p = FnContext.currentPresenter();
4.29 + Set<Presenter> there = LOADED.get(resource);
4.30 + if (there == null) {
4.31 + there = new HashSet<Presenter>();
4.32 + LOADED.put(resource, there);
4.33 + }
4.34 + if (there.add(p)) {
4.35 + InputStream is = caller.getClassLoader().getResourceAsStream(resource);
4.36 + try {
4.37 + InputStreamReader r = new InputStreamReader(is, "UTF-8");
4.38 + p.loadScript(r);
4.39 + } finally {
4.40 + is.close();
4.41 + }
4.42 + }
4.43 + return fn.invoke(thiz, args);
4.44 + }
4.45 + };
4.46 + }
4.47 +
4.48 /** The currently active presenter.
4.49 *
4.50 * @return the currently active presenter or <code>null</code>