# HG changeset patch # User Jaroslav Tulach # Date 1386888872 -3600 # Node ID 53634fd10e30d0fcfd8d05a2bdf2df36c50ee237 # Parent b18accb1660fb55cc3c7edf506d02b4fa0ea89be Load global scripts when first method in the class is called diff -r b18accb1660f -r 53634fd10e30 boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/boot-fx/src/test/java/net/java/html/boot/fx/FXBrowsersOnResourceTest.java Thu Dec 12 23:54:32 2013 +0100 @@ -0,0 +1,180 @@ +/** + * HTML via Java(tm) Language Bindings + * Copyright (C) 2013 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. apidesign.org + * designates this particular file as subject to the + * "Classpath" exception as provided by apidesign.org + * in the License file that accompanied this code. + * + * 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://wiki.apidesign.org/wiki/GPLwithClassPathException + */ +package net.java.html.boot.fx; + +import java.net.URL; +import java.util.concurrent.CountDownLatch; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.layout.BorderPane; +import javafx.scene.web.WebView; +import javafx.stage.Stage; +import net.java.html.js.JavaScriptBody; +import net.java.html.js.JavaScriptResource; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNotSame; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class FXBrowsersOnResourceTest { + + public FXBrowsersOnResourceTest() { + } + + @BeforeClass public void initFX() throws Throwable { + new Thread("initFX") { + @Override + public void run() { + App.launch(App.class); + } + }.start(); + App.CDL.await(); + } + + @Test + public void behaviorOfTwoWebViewsAtOnce() throws Throwable { + class R implements Runnable { + CountDownLatch DONE = new CountDownLatch(1); + Throwable t; + + @Override + public void run() { + try { + doTest(); + } catch (Throwable ex) { + t = ex; + } finally { + DONE.countDown(); + } + } + + private void doTest() throws Throwable { + URL u = FXBrowsersOnResourceTest.class.getResource("/org/apidesign/html/boot/fx/empty.html"); + assertNotNull(u, "URL found"); + FXBrowsers.load(App.getV1(), u, OnPages.class, "first"); + + } + } + R run = new R(); + Platform.runLater(run); + run.DONE.await(); + for (int i = 0; i < 100; i++) { + if (run.t != null) { + throw run.t; + } + if (System.getProperty("finalSecond") == null) { + Thread.sleep(100); + } + } + + + + assertEquals(Integer.getInteger("finalFirst"), Integer.valueOf(3), "Three times in view one"); + assertEquals(Integer.getInteger("finalSecond"), Integer.valueOf(2), "Two times in view one"); + } + + @JavaScriptResource("wnd.js") + public static class OnPages { + static Class first; + static Object firstWindow; + + public static void first() { + first = OnPages.class; + firstWindow = window(); + assertNotNull(firstWindow, "First window found"); + + assertEquals(increment(), 1, "Now it is one"); + + URL u = FXBrowsersOnResourceTest.class.getResource("/org/apidesign/html/boot/fx/empty.html"); + assertNotNull(u, "URL found"); + FXBrowsers.load(App.getV2(), u, OnPages.class, "second", "Hello"); + + assertEquals(increment(), 2, "Now it is two and not influenced by second view"); + System.setProperty("finalFirst", "" + increment()); + } + + public static void second(String... args) { + assertEquals(args.length, 1, "One string argument"); + assertEquals(args[0], "Hello", "It is hello"); + assertEquals(first, OnPages.class, "Both views share the same classloader"); + + Object window = window(); + assertNotNull(window, "Some window found"); + assertNotNull(firstWindow, "First window is known"); + assertNotSame(firstWindow, window, "The window objects should be different"); + + assertEquals(increment(), 1, "Counting starts from zero"); + System.setProperty("finalSecond", "" + increment()); + } + + @JavaScriptBody(args = {}, body = "return wnd;") + private static native Object window(); + + @JavaScriptBody(args = {}, body = "" + + "if (wnd.cnt) return ++wnd.cnt;" + + "return wnd.cnt = 1;" + ) + private static native int increment(); + } + + public static class App extends Application { + static final CountDownLatch CDL = new CountDownLatch(1); + private static BorderPane pane; + + /** + * @return the v1 + */ + static WebView getV1() { + return (WebView)System.getProperties().get("v1"); + } + + /** + * @return the v2 + */ + static WebView getV2() { + return (WebView)System.getProperties().get("v2"); + } + + @Override + public void start(Stage stage) throws Exception { + pane= new BorderPane(); + Scene scene = new Scene(pane, 800, 600); + stage.setScene(scene); + + System.getProperties().put("v1", new WebView()); + System.getProperties().put("v2", new WebView()); + + pane.setCenter(getV1()); + pane.setBottom(getV2()); + + stage.show(); + CDL.countDown(); + } + + + } +} diff -r b18accb1660f -r 53634fd10e30 boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/boot-fx/src/test/resources/net/java/html/boot/fx/wnd.js Thu Dec 12 23:54:32 2013 +0100 @@ -0,0 +1,27 @@ +/* + * HTML via Java(tm) Language Bindings + * Copyright (C) 2013 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. apidesign.org + * designates this particular file as subject to the + * "Classpath" exception as provided by apidesign.org + * in the License file that accompanied this code. + * + * 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://wiki.apidesign.org/wiki/GPLwithClassPathException + */ +if (typeof wnd !== 'undefined') { + throw 'Window should not be defined yet: ' + wnd; +} + +wnd = { +}; + diff -r b18accb1660f -r 53634fd10e30 boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java --- a/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Dec 12 18:33:11 2013 +0100 +++ b/boot/src/main/java/org/apidesign/html/boot/impl/FnUtils.java Thu Dec 12 23:54:32 2013 +0100 @@ -156,6 +156,7 @@ private String name; private int found; private ClassLoader loader; + private String resource; public FindInClass(ClassLoader l, ClassVisitor cv) { super(Opcodes.ASM4, cv); @@ -263,6 +264,14 @@ "org/apidesign/html/boot/spi/Fn", "define", "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;" ); + if (resource != null) { + super.visitLdcInsn(Type.getObjectType(FindInClass.this.name)); + super.visitLdcInsn(resource); + super.visitMethodInsn(Opcodes.INVOKESTATIC, + "org/apidesign/html/boot/spi/Fn", "preload", + "(Lorg/apidesign/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;" + ); + } super.visitInsn(Opcodes.DUP); super.visitFieldInsn( Opcodes.PUTSTATIC, FindInClass.this.name, @@ -487,11 +496,11 @@ public void visit(String attrName, Object value) { String relPath = (String) value; if (relPath.startsWith("/")) { - loadScript(loader, relPath); + resource = relPath; } else { int last = name.lastIndexOf('/'); String fullPath = name.substring(0, last + 1) + relPath; - loadScript(loader, fullPath); + resource = fullPath; } } } diff -r b18accb1660f -r 53634fd10e30 boot/src/main/java/org/apidesign/html/boot/spi/Fn.java --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Thu Dec 12 18:33:11 2013 +0100 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Thu Dec 12 23:54:32 2013 +0100 @@ -21,8 +21,15 @@ package org.apidesign.html.boot.spi; import java.io.Closeable; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; import net.java.html.js.JavaScriptBody; import org.apidesign.html.boot.impl.FnContext; @@ -87,6 +94,31 @@ return FnContext.currentPresenter().defineFn(code, names); } + private static final Map> LOADED = new HashMap>(); + public static Fn preload(final Fn fn, final Class caller, final String resource) { + return new Fn() { + @Override + public Object invoke(Object thiz, Object... args) throws Exception { + final Presenter p = FnContext.currentPresenter(); + Set there = LOADED.get(resource); + if (there == null) { + there = new HashSet(); + LOADED.put(resource, there); + } + if (there.add(p)) { + InputStream is = caller.getClassLoader().getResourceAsStream(resource); + try { + InputStreamReader r = new InputStreamReader(is, "UTF-8"); + p.loadScript(r); + } finally { + is.close(); + } + } + return fn.invoke(thiz, args); + } + }; + } + /** The currently active presenter. * * @return the currently active presenter or null