From 6d6987cc331118662b38ac6ed3e55e280067c699 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sat, 28 Sep 2019 13:12:55 +0200 Subject: [PATCH 01/16] ProtoPresenter as of version 1.6.2 rev. 2347b1057d4 --- browser/pom.xml | 118 +++ .../com/dukescript/presenters/Browser.java | 677 +++++++++++++ .../dukescript/presenters/BrowserTest.java | 98 ++ .../dukescript/presenters/DynamicHTTP.java | 216 ++++ .../com/dukescript/presenters/KOScript.java | 117 +++ .../dukescript/presenters/KoBrowserTest.java | 252 +++++ .../com/dukescript/presenters/ServerTest.java | 137 +++ .../com/dukescript/presenters/empty.html | 37 + .../com/dukescript/presenters/server.html | 38 + generic/pom.xml | 101 ++ .../dukescript/presenters/spi/Generic.java | 959 ++++++++++++++++++ .../com/dukescript/presenters/spi/Level.java | 41 + .../presenters/spi/ProtoPresenter.java | 60 ++ .../presenters/spi/ProtoPresenterBuilder.java | 294 ++++++ .../presenters/spi/ValueOfTest.java | 71 ++ .../presenters/spi/test/CallbackTest.java | 71 ++ .../dukescript/presenters/spi/test/Case.java | 121 +++ .../presenters/spi/test/Counter.java | 55 + .../presenters/spi/test/GenericTest.java | 102 ++ .../presenters/spi/test/SynchronizedTest.java | 37 + .../presenters/spi/test/Testing.java | 179 ++++ renderer/pom.xml | 52 + .../dukescript/presenters/renderer/AWT.java | 65 ++ .../dukescript/presenters/renderer/Cocoa.java | 483 +++++++++ .../dukescript/presenters/renderer/GTK.java | 450 ++++++++ .../dukescript/presenters/renderer/JSC.java | 293 ++++++ .../dukescript/presenters/renderer/Show.java | 116 +++ webkit/pom.xml | 155 +++ .../presenters/webkit/UnJarResources.java | 74 ++ .../presenters/webkit/WebKitPresenter.java | 536 ++++++++++ .../dukescript/presenters/webkit/Case.java | 113 +++ .../presenters/webkit/DynamicHTTP.java | 246 +++++ .../presenters/webkit/GtkJavaScriptTest.java | 107 ++ .../presenters/webkit/GtkKnockoutTest.java | 214 ++++ .../dukescript/presenters/webkit/empty.html | 35 + 35 files changed, 6720 insertions(+) create mode 100644 browser/pom.xml create mode 100644 browser/src/main/java/com/dukescript/presenters/Browser.java create mode 100644 browser/src/test/java/com/dukescript/presenters/BrowserTest.java create mode 100644 browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java create mode 100644 browser/src/test/java/com/dukescript/presenters/KOScript.java create mode 100644 browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java create mode 100644 browser/src/test/java/com/dukescript/presenters/ServerTest.java create mode 100644 browser/src/test/resources/com/dukescript/presenters/empty.html create mode 100644 browser/src/test/resources/com/dukescript/presenters/server.html create mode 100644 generic/pom.xml create mode 100644 generic/src/main/java/com/dukescript/presenters/spi/Generic.java create mode 100644 generic/src/main/java/com/dukescript/presenters/spi/Level.java create mode 100644 generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java create mode 100644 generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/test/Case.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java create mode 100644 generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java create mode 100644 renderer/pom.xml create mode 100644 renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java create mode 100644 renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java create mode 100644 renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java create mode 100644 renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java create mode 100644 renderer/src/main/java/com/dukescript/presenters/renderer/Show.java create mode 100644 webkit/pom.xml create mode 100644 webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java create mode 100644 webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java create mode 100644 webkit/src/test/java/com/dukescript/presenters/webkit/Case.java create mode 100644 webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java create mode 100644 webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java create mode 100644 webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java create mode 100644 webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html diff --git a/browser/pom.xml b/browser/pom.xml new file mode 100644 index 00000000..cbb2bc77 --- /dev/null +++ b/browser/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + browser + com.dukescript.presenters + jar + + com.dukescript.presenters + pom + 2.0-SNAPSHOT + + DukeScript Presenter for any Browser + + ${project.parent.basedir} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-deploy-plugin + + false + + + + org.apache.maven.plugins + maven-javadoc-plugin + + false + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.netbeans.html + html4j-maven-plugin + ${net.java.html.version} + + + process-test-classes + + process-js-annotations + + + + + ${project.build.directory}/test-classes + + + + + + + org.testng + testng + ${testng.version} + test + + + org.glassfish.grizzly + grizzly-http-server + 2.3.19 + + + org.netbeans.html + ko4j + ${net.java.html.version} + test + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + provided + + + org.netbeans.html + net.java.html.json.tck + ${net.java.html.version} + test + + + org.glassfish.grizzly + grizzly-websockets-server + test + 2.3.19 + + + com.dukescript.presenters + generic + ${project.version} + + + ${project.groupId} + renderer + ${project.version} + + + \ No newline at end of file diff --git a/browser/src/main/java/com/dukescript/presenters/Browser.java b/browser/src/main/java/com/dukescript/presenters/Browser.java new file mode 100644 index 00000000..11a7a414 --- /dev/null +++ b/browser/src/main/java/com/dukescript/presenters/Browser.java @@ -0,0 +1,677 @@ +package com.dukescript.presenters; + +/* + * #%L + * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.dukescript.presenters.renderer.Show; +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.Flushable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.glassfish.grizzly.PortRange; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.http.util.HttpStatus; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.boot.spi.Fn.Presenter; +import com.dukescript.presenters.spi.ProtoPresenter; +import com.dukescript.presenters.spi.ProtoPresenterBuilder; +import org.openide.util.lookup.ServiceProvider; + +/** Browser based {@link Presenter}. It starts local server and + * launches browser that connects to it. The actual browser to + * be launched can be influenced by value of + * com.dukescript.presenters.browser property. + * It can have following values: + * + * If the property is not specified the system tries GTK mode first, + * followed by AWT and then tries to execute xdg-open + * (default LINUX command to launch a browser from a shell script). + *

+ * To use this presenter specify following dependency: + *

+ * <dependency>
+ *   <groupId>com.dukescript.presenters</groupId>
+ *   <artifactId>browser</artifactId>
+ *   <version>1.x</version>
+ * </dependency>
+ * 
+ */ +@ServiceProvider(service = Presenter.class) +public final class Browser implements Fn.Presenter, Fn.KeepAlive, Flushable, +Executor, Closeable { + static final Logger LOG = Logger.getLogger(Browser.class.getName()); + private final Map SESSIONS = new HashMap(); + private final String app; + private HttpServer s; + private Runnable onPageLoad; + private Command current; + private final Config config; + + public Browser() throws Exception { + this(new Config()); + } + + public Browser(Config config) { + this(findCalleeClassName(), config); + } + + Browser(String app, Config config) { + this.app = app; + this.config = new Config(config); + } + + @Override + public final void execute(final Runnable r) { + current.runSafe(r, true); + } + + @Override + public void close() throws IOException { + s.shutdownNow(); + } + + HttpServer server() { + return s; + } + + static HttpServer findServer(Object obj) { + Command c = null; + if (obj instanceof Command) { + c = (Command) obj; + } else if (obj instanceof ProtoPresenter) { + c = ((ProtoPresenter) obj).lookup(Command.class); + } + return c.browser.server(); + } + + /** Shows URL in a browser. + * @param page the page to display in the browser + * @throws IOException if something goes wrong + */ + void show(URI page) throws IOException { + String impl = config.getBrowser(); + if ("none".equalsIgnoreCase(impl)) { // NOI18N + return; + } + if (impl != null) { + Show.show(impl, page); + } else { + IOException one, two; + try { + String ui = System.getProperty("os.name").contains("Mac") ? + "Cocoa" : "GTK"; + Show.show(ui, page); + return; + } catch (IOException ex) { + one = ex; + } + try { + Show.show("AWT", page); + return; + } catch (IOException ex) { + two = ex; + } + try { + Show.show(impl, page); + } catch (IOException ex) { + two.initCause(one); + ex.initCause(two); + throw ex; + } + } + } + + @Override + public Fn defineFn(String string, String... strings) { + throw new UnsupportedOperationException(); + } + + @Override + public void loadScript(Reader reader) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + public Fn defineFn(String string, String[] strings, boolean[] blns) { + throw new UnsupportedOperationException(); + } + + @Override + public void flush() throws IOException { + throw new UnsupportedOperationException(); + } + + private static HttpServer server(RootPage r, Config config) { + int from = 8080; + int to = 65535; + int port = config.getPort(); + if (port != -1) { + from = to = port; + } + HttpServer s = HttpServer.createSimpleServer(null, new PortRange(from, to)); + final ServerConfiguration conf = s.getServerConfiguration(); + conf.addHttpHandler(r, "/"); + return s; + } + + private static URI pageURL(String protocol, HttpServer server, final String page) { + NetworkListener listener = server.getListeners().iterator().next(); + int port = listener.getPort(); + try { + return new URI(protocol + "://localhost:" + port + page); + } catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public final void displayPage(URL page, Runnable onPageLoad) { + try { + this.onPageLoad = onPageLoad; + s = server(new RootPage(page), config); + s.start(); + show(pageURL("http", s, "/")); + } catch (IOException ex) { + Logger.getLogger(Browser.class.getName()).log(Level.SEVERE, null, ex); + } + } + + /** Parameters to configure {@link Browser}. + * Create an instance and pass it + * to {@link Browser#Browser(com.dukescript.presenters.Browser.Config) } + * constructor. + */ + public final static class Config { + String browser; + Integer port; + + public Config() { + } + + private Config(Config copy) { + this.browser = copy.browser; + this.port = copy.port; + } + + /** The command to use when invoking a browser. Possible values: + *
    + *
  • + * GTK - use Gtk WebKit implementation. Requires presence of appropriate native libraries + *
  • + *
  • + * AWT - use Desktop.browse(java.net.URI) to launch a browser + *
  • + *
  • + * NONE - just launches the server, useful together with {@link #port(int)} to specify a fixed port to open the server at + *
  • + *
  • + * any other value is interpreted as a command which is then launched on a command line with one parameter - the URL to connect to + *
  • + *
+ * + * @param executable browser to execute + * @return this instance + */ + public Config command(String executable) { + this.browser = executable; + return this; + } + + /** The port to start the server at. + * By default a random port is selected. + * @param port the port + * @return this instance + */ + public Config port(int port) { + this.port = port; + return this; + } + + final String getBrowser() { + if (browser != null) { + return browser; + } + return System.getProperty("com.dukescript.presenters.browser"); // NOI18N + } + + final int getPort() { + if (port != null) { + return port; + } + String port = System.getProperty("com.dukescript.presenters.browserPort"); // NOI18N + try { + return Integer.parseInt(port); + } catch (NumberFormatException ex) { + return -1; + } + } + } + + static void cors(Response r) { + r.setCharacterEncoding("UTF-8"); + r.addHeader("Access-Control-Allow-Origin", "*"); + r.addHeader("Access-Control-Allow-Credentials", "true"); + r.addHeader("Access-Control-Allow-Headers", "Content-Type"); + r.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); + } + + private final class RootPage extends HttpHandler { + private final URL page; + + public RootPage(URL page) { + this.page = page; + } + + @Override + public void service(Request rqst, Response rspns) throws Exception { + String path = rqst.getRequestURI(); + cors(rspns); + if ("/".equals(path) || "index.html".equals(path)) { + Reader is; + String prefix = "http://" + rqst.getServerName() + ":" + rqst.getServerPort() + "/"; + Writer w = rspns.getWriter(); + rspns.setContentType("text/html"); + final Command cmd = new Command(Browser.this, prefix); + try { + is = new InputStreamReader(page.openStream()); + } catch (IOException ex) { + w.write(""); + w.write("

Browser

"); + w.write("
");
+                    emitScript(w, prefix, cmd.id);
+                    w.write("");
+                    w.close();
+                    return;
+                }
+                SESSIONS.put(cmd.id, cmd);
+                int state = 0;
+                for (;;) {
+                    int ch = is.read();
+                    if (ch == -1) {
+                        break;
+                    }
+                    char lower = Character.toLowerCase((char)ch);
+                    switch (state) {
+                        case 1000: break;
+                        case 0: if (lower == '<') state = 1; break;
+                        case 1: if (lower == 'b') state = 2;
+                            else if (lower != ' ' && lower != '\n') state = 0;
+                            break;
+                        case 2: if (lower == 'o') state = 3; else state = 0; break;
+                        case 3: if (lower == 'd') state = 4; else state = 0; break;
+                        case 4: if (lower == 'y') state = 5; else state = 0; break;
+                        case 5: if (lower == '>') state = 500;
+                            else if (lower != ' ' && lower != '\n') state = 0;
+                            break;
+                    }
+                    w.write((char)ch);
+                    if (state == 500) {
+                        emitScript(w, prefix, cmd.id);
+                        state = 1000;
+                    }
+                }
+                if (state != 1000) {
+                    emitScript(w, prefix, cmd.id);
+                }
+                is.close();
+                w.close();
+            } else if (path.equals("/command.js")) {
+                String id = rqst.getParameter("id");
+                Command c = SESSIONS.get(id);
+                if (c == null) {
+                    rspns.getOutputBuffer().write("No command for " + id);
+                    rspns.setStatus(HttpStatus.NOT_FOUND_404);
+                    return;
+                }
+                c.service(rqst, rspns);
+            } else {
+                if (path.startsWith("/")) {
+                    path = path.substring(1);
+                }
+                URL relative = new URL(page, path);
+                InputStream is;
+                try {
+                    is = relative.openStream();
+                } catch (FileNotFoundException ex) {
+                    rspns.setStatus(HttpStatus.NOT_FOUND_404);
+                    return;
+                }
+                OutputStream out = rspns.getOutputStream();
+                for (;;) {
+                    int b = is.read();
+                    if (b == -1) {
+                        break;
+                    }
+                    out.write(b);
+                }
+                out.close();
+                is.close();
+            }
+        }
+
+        private void emitScript(Writer w, String prefix, String id) throws IOException {
+            w.write("  \n");
+        }
+    }
+
+    private static String findCalleeClassName() {
+        StackTraceElement[] frames = new Exception().getStackTrace();
+        for (StackTraceElement e : frames) {
+            String cn = e.getClassName();
+            if (cn.startsWith("com.dukescript.presenters.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("org.netbeans.html.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("net.java.html.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("java.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("javafx.")) { // NOI18N
+                continue;
+            }
+            if (cn.startsWith("com.sun.")) { // NOI18N
+                continue;
+            }
+            return cn;
+        }
+        return "org.netbeans.html"; // NOI18N
+    }
+    
+    private static final class Command extends Object
+    implements Executor, ThreadFactory {
+        private final Queue exec;
+        private final Browser browser;
+        private final String id;
+        private final String prefix;
+        private final Executor RUN;
+        private Thread RUNNER;
+        private Response suspended;
+        private boolean initialized;
+        private final ProtoPresenter presenter;
+
+        Command(Browser browser, String prefix) {
+            this.RUN = Executors.newSingleThreadExecutor(this);
+            this.id = UUID.randomUUID().toString();
+            this.exec = new LinkedList<>();
+            this.prefix = prefix;
+            this.browser = browser;
+            this.presenter = ProtoPresenterBuilder.newBuilder().
+                preparator(this::callbackFn, true).
+                loadJavaScript(this::loadJS, false).
+                app(browser.app).
+                dispatcher(this, true).
+                displayer(this::displayPage).
+                logger(this::log).
+                type("Browser").
+                register(this).
+                build();
+        }
+
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(r, "Processor for " + id);
+            RUNNER = t;
+            return t;
+        }
+
+        @Override
+        public final void execute(final Runnable r) {
+            runSafe(r, true);
+        }
+        
+        final void runSafe(final Runnable r, final boolean context) {
+            class Wrap implements Runnable {
+                @Override
+                public void run() {
+                    if (context) {
+                        Closeable c = Fn.activate(Command.this.presenter);
+                        try {
+                            r.run();
+                        } finally {
+                            try {
+                                c.close();
+                            } catch (IOException ex) {
+                                // ignore
+                            }
+                        }
+                    } else {
+                        r.run();
+                    }
+                }
+            }
+            if (RUNNER == Thread.currentThread()) {
+                if (context) {
+                    Runnable w = new Wrap();
+                    w.run();
+                } else {
+                    r.run();
+                }
+            } else {
+                Runnable w = new Wrap();
+                RUN.execute(w);
+            }
+        }
+        
+        final synchronized void add(Object obj) {
+            if (suspended != null) {
+                try {
+                    suspended.getWriter().write(obj.toString());
+                } catch (IOException ex) {
+                    LOG.log(Level.SEVERE, null, ex);
+                }
+                suspended.resume();
+                suspended = null;
+                return;
+            }
+            exec.add(obj);
+        }
+        
+        private synchronized Object take(Response rspns) {
+            Object o = exec.poll();
+            if (o != null) {
+                return o;
+            }
+            suspended = rspns;
+            rspns.suspend();
+            return null;
+        }
+        
+        void service(Request rqst, Response rspns) throws Exception {
+            final String methodName = rqst.getParameter("name");
+            Writer w = rspns.getWriter();
+            if (methodName == null) {
+                if (!initialized) {
+                    initialized = true;
+                    execute(browser.onPageLoad);
+                }
+                // send new request
+                Object obj = take(rspns);
+                if (obj == null) {
+                    LOG.log(Level.FINE, "Suspending response {0}", rspns);
+                    return;
+                }
+                final String s = obj.toString();
+                w.write(s);
+                LOG.log(Level.FINE, "Exec global: {0}", s);
+            } else {
+                List args = new ArrayList();
+                for (;;) {
+                    String p = rqst.getParameter("p" + args.size());
+                    if (p == null) {
+                        break;
+                    }
+                    args.add(p);
+                }
+                String res;
+                try {
+                    LOG.log(Level.FINE, "Call {0}", methodName + " with " + args);
+                    res = presenter.js2java(methodName,
+                        args.get(0), args.get(1), args.get(2), args.get(3)
+                    );
+                    LOG.log(Level.FINE, "Result: {0}", res);
+                } catch (Exception ex) {
+                    res = "error:" + ex.getMessage();
+                }
+                if (res != null) {
+                    w.write(res);
+                } else {
+                    w.write("null");
+                }
+            }
+            w.close();
+        }
+
+        void callbackFn(ProtoPresenterBuilder.OnPrepared onReady) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("this.toBrwsrSrvr = function(name, a1, a2, a3, a4) {\n"
+                + "var url = '").append(prefix).append("command.js?id=").append(id).append("&name=' + name;\n"
+                + "url += '&p0=' + encodeURIComponent(a1);\n"
+                + "url += '&p1=' + encodeURIComponent(a2);\n"
+                + "url += '&p2=' + encodeURIComponent(a3);\n"
+                + "url += '&p3=' + encodeURIComponent(a4);\n"
+                + "var request = new XMLHttpRequest();\n"
+                + "request.open('GET', url, false);\n"
+                + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
+                + "request.send();\n"
+                + "return request.responseText;\n"
+                + "};\n");
+            add(sb);
+            onReady.callbackIsPrepared("toBrwsrSrvr");
+        }
+
+        private static Level findLevel(int priority) {
+            if (priority >= Level.SEVERE.intValue()) {
+                return Level.SEVERE;
+            }
+            if (priority >= Level.WARNING.intValue()) {
+                return Level.WARNING;
+            }
+            if (priority >= Level.INFO.intValue()) {
+                return Level.INFO;
+            }
+            return Level.FINE;
+        }
+        
+        void log(int priority, String msg, Object... args) {
+            Level level = findLevel(priority);
+
+            if (args.length == 1 && args[0] instanceof Throwable) {
+                LOG.log(level, msg, (Throwable) args[0]);
+            } else {
+                LOG.log(level, msg, args);
+            }
+        }
+
+        final void loadJS(String js) {
+            add(js);
+        }
+
+        void dispatch(Runnable r) {
+            runSafe(r, false);
+        }
+
+
+        public void displayPage(URL url, Runnable r) {
+            throw new UnsupportedOperationException(url.toString());
+        }
+    } // end of Command  
+}
diff --git a/browser/src/test/java/com/dukescript/presenters/BrowserTest.java b/browser/src/test/java/com/dukescript/presenters/BrowserTest.java
new file mode 100644
index 00000000..b965b987
--- /dev/null
+++ b/browser/src/test/java/com/dukescript/presenters/BrowserTest.java
@@ -0,0 +1,98 @@
+package com.dukescript.presenters;
+
+/*
+ * #%L
+ * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project.
+ * 
+ * Dukehoff GmbH designates this particular file as subject to the "Classpath"
+ * exception as provided in the README.md file that accompanies this code.
+ * %%
+ * Copyright (C) 2015 - 2019 Dukehoff GmbH
+ * %%
+ * 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, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * 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.  If not, see
+ * .
+ * #L%
+ */
+
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import net.java.html.boot.BrowserBuilder;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.JavaScriptTCK;
+import org.netbeans.html.json.tck.KOTest;
+import org.testng.annotations.Factory;
+
+public class BrowserTest extends JavaScriptTCK {
+    private static Class browserClass;
+    private static Fn.Presenter browserPresenter;
+    
+    public BrowserTest() {
+    }
+
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser(new Browser("BrowserTest", new Browser.Config())).
+            loadClass(BrowserTest.class).
+            loadPage("empty.html").
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        List res = new ArrayList();
+        Class test = 
+            loadClass().getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+
+        Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null);
+        for (Class c : arr) {
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(test) != null) {
+                    res.add(new KOScript(browserPresenter, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+    
+    public static Class[] tests() {
+        return testClasses();
+    }
+
+    static synchronized Class loadClass() throws InterruptedException {
+        while (browserClass == null) {
+            BrowserTest.class.wait();
+        }
+        return browserClass;
+    }
+    
+    public static synchronized void ready(Class browserCls) throws Exception {
+        browserClass = browserCls;
+        browserPresenter = Fn.activePresenter();
+        BrowserTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Class classpathClass = ClassLoader.getSystemClassLoader().loadClass(BrowserTest.class.getName());
+        Method m = classpathClass.getMethod("ready", Class.class);
+        m.invoke(null, BrowserTest.class);
+    }
+}
diff --git a/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java b/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java
new file mode 100644
index 00000000..fc3e02b6
--- /dev/null
+++ b/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java
@@ -0,0 +1,216 @@
+package com.dukescript.presenters;
+
+/*
+ * #%L
+ * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project.
+ * 
+ * Dukehoff GmbH designates this particular file as subject to the "Classpath"
+ * exception as provided in the README.md file that accompanies this code.
+ * %%
+ * Copyright (C) 2015 - 2019 Dukehoff GmbH
+ * %%
+ * 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, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * 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.  If not, see
+ * .
+ * #L%
+ */
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private List resources = new ArrayList();
+    private final ServerConfiguration conf;
+    private final HttpServer server;
+    
+    DynamicHTTP(HttpServer s) {
+        server = s;
+        conf = s.getServerConfiguration();
+    }
+    
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List params = new ArrayList();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+    
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+    
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+    
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}
diff --git a/browser/src/test/java/com/dukescript/presenters/KOScript.java b/browser/src/test/java/com/dukescript/presenters/KOScript.java
new file mode 100644
index 00000000..a719a609
--- /dev/null
+++ b/browser/src/test/java/com/dukescript/presenters/KOScript.java
@@ -0,0 +1,117 @@
+package com.dukescript.presenters;
+
+/*
+ * #%L
+ * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project.
+ * 
+ * Dukehoff GmbH designates this particular file as subject to the "Classpath"
+ * exception as provided in the README.md file that accompanies this code.
+ * %%
+ * Copyright (C) 2015 - 2019 Dukehoff GmbH
+ * %%
+ * 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, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * 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.  If not, see
+ * .
+ * #L%
+ */
+
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.IHookCallBack;
+import org.testng.IHookable;
+import org.testng.ITest;
+import org.testng.ITestResult;
+import org.testng.annotations.Test;
+
+public final class KOScript implements ITest, IHookable, Runnable {
+    private final Fn.Presenter p;
+    private CountDownLatch finished;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int cnt;
+
+    KOScript(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getDeclaringClass().getSimpleName() + "." + m.getName();
+    }
+
+    @Test
+    public void executeTest() throws Exception {
+        for (;;) {
+            if (p instanceof Executor) {
+                finished = new CountDownLatch(1);
+                ((Executor)p).execute(this);
+                finished.await();
+            } else {
+                run();
+            }
+            if (result instanceof InterruptedException && cnt++ < 100) {
+                Thread.sleep(100);
+                result = null;
+                continue;
+            }
+            break;
+        }
+        if (result instanceof Exception) {
+            throw (Exception) result;
+        } else if (result instanceof Error) {
+            throw (Error) result;
+        }
+    }
+    
+    @Override
+    public void run() {
+        Closeable c = Fn.activate(p);
+        try {
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            try {
+                c.close();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+            if (finished != null) {
+                finished.countDown();
+            }
+        }
+    }
+
+    @Override
+    public void run(IHookCallBack ihcb, ITestResult itr) {
+        ihcb.runTestMethod(itr);
+    }
+    
+}
diff --git a/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java b/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java
new file mode 100644
index 00000000..0386221a
--- /dev/null
+++ b/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java
@@ -0,0 +1,252 @@
+package com.dukescript.presenters;
+
+/*
+ * #%L
+ * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project.
+ * 
+ * Dukehoff GmbH designates this particular file as subject to the "Classpath"
+ * exception as provided in the README.md file that accompanies this code.
+ * %%
+ * Copyright (C) 2015 - 2019 Dukehoff GmbH
+ * %%
+ * 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, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * 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.  If not, see
+ * .
+ * #L%
+ */
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.netbeans.html.ko4j.KO4J;
+import org.openide.util.lookup.ServiceProvider;
+import static org.testng.Assert.assertNotNull;
+import org.testng.annotations.Factory;
+
+@ServiceProvider(service = KnockoutTCK.class)
+public class KoBrowserTest extends KnockoutTCK {
+    private static final Logger LOG = Logger.getLogger(KoBrowserTest.class.getName());
+    private static Class browserClass;
+    private static Fn.Presenter browserPresenter;
+    
+    public KoBrowserTest() {
+    }
+
+    static Object[] showBrwsr(URI uri, String cmd) throws IOException {
+        LOG.log(Level.INFO, "Showing {0}", uri);
+        if (cmd == null) {
+            try {
+                LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[]{
+                    System.getProperty("java.vm.name"),
+                    System.getProperty("java.vm.vendor"),
+                    System.getProperty("java.vm.version"),});
+                java.awt.Desktop.getDesktop().browse(uri);
+                LOG.log(Level.INFO, "Desktop.browse successfully finished");
+                return null;
+            } catch (UnsupportedOperationException ex) {
+                LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage());
+                LOG.log(Level.FINE, null, ex);
+            }
+        }
+        {
+            String cmdName = cmd == null ? "xdg-open" : cmd;
+            String[] cmdArr = {
+                cmdName, uri.toString()
+            };
+            LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr));
+            final Process process = Runtime.getRuntime().exec(cmdArr);
+            return new Object[]{process, null};
+        }
+    }
+   
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Browser.LOG.setLevel(Level.FINE);
+        Browser.LOG.addHandler(new ConsoleHandler());
+        
+        final BrowserBuilder bb = BrowserBuilder.newBrowser(new Browser("KoBrowserTest", new Browser.Config())).
+            loadClass(KoBrowserTest.class).
+            loadPage("empty.html").
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+
+        List res = new ArrayList();
+        Class test = 
+            loadClass().getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+
+        Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null);
+
+        final HttpServer s = Browser.findServer(browserPresenter);
+        ServerConfiguration conf = s.getServerConfiguration();
+        conf.addHttpHandler(new DynamicHTTP(s), "/dynamic");
+        for (Class c : arr) {
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(test) != null) {
+                    res.add(new KOScript(browserPresenter, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+    
+    public static Class[] tests() {
+        return testClasses();
+    }
+
+    static synchronized Class loadClass() throws InterruptedException {
+        while (browserClass == null) {
+            KoBrowserTest.class.wait();
+        }
+        return browserClass;
+    }
+    
+    public static synchronized void ready(Class browserCls) throws Exception {
+        browserClass = browserCls;
+        browserPresenter = Fn.activePresenter();
+        KoBrowserTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        browserPresenter = Fn.activePresenter();
+        Class classpathClass = ClassLoader.getSystemClassLoader().loadClass(KoBrowserTest.class.getName());
+        Method m = classpathClass.getMethod("ready", Class.class);
+        m.invoke(null, KoBrowserTest.class);
+    }
+
+    @Override
+    public BrwsrCtx createContext() {
+        KO4J ko = new KO4J();
+        Contexts.Builder b = Contexts.newBuilder();
+        b.register(Technology.class, ko.knockout(), 7);
+        b.register(Transfer.class, ko.transfer(), 7);
+        assertNotNull(browserPresenter, "Presenter needs to be registered");
+        b.register(Executor.class, (Executor)browserPresenter, 10);
+        return b.build();
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        return true;
+    }
+
+    @Override
+    public Object createJSON(Map values) {
+        Object json = putValue(null, null, null);
+        for (Map.Entry entry : values.entrySet()) {
+            json = putValue(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+
+    private Fn jsonFn;
+    private Object putValue(Object json, String key, Object value) {
+        if (jsonFn == null) {
+            jsonFn = Fn.activePresenter().defineFn(
+                "if (json === null) json = new Object();"
+                + "if (key !== null) json[key] = value;"
+                + "return json;",
+                "json", "key", "value"
+            );
+        }
+        try {
+            return jsonFn.invoke(null, json, key, value);
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+    
+    private Fn executeScript;
+    @Override
+    public Object executeScript(String script, Object[] arguments) {
+        if (executeScript == null) {
+            executeScript = Fn.activePresenter().defineFn(
+                "var f = new Function(s); "
+              + "return f.apply(null, args);",
+              "s", "args"
+            );
+        }
+        try {
+            return executeScript.invoke(null, script, arguments);
+        } catch (Exception ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("¶m").append(i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+}
diff --git a/browser/src/test/java/com/dukescript/presenters/ServerTest.java b/browser/src/test/java/com/dukescript/presenters/ServerTest.java
new file mode 100644
index 00000000..a73b1873
--- /dev/null
+++ b/browser/src/test/java/com/dukescript/presenters/ServerTest.java
@@ -0,0 +1,137 @@
+package com.dukescript.presenters;
+
+/*
+ * #%L
+ * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project.
+ *
+ * Dukehoff GmbH designates this particular file as subject to the "Classpath"
+ * exception as provided in the README.md file that accompanies this code.
+ * %%
+ * Copyright (C) 2015 - 2019 Dukehoff GmbH
+ * %%
+ * 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, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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.  If not, see
+ * .
+ * #L%
+ */
+
+import com.dukescript.presenters.renderer.Show;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ServerSocket;
+import java.net.URI;
+import java.net.URL;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+import org.testng.annotations.Test;
+
+public class ServerTest {
+    @Test
+    public void useAsAServer() throws Exception {
+        final Thread main = Thread.currentThread();
+        final int[] loaded = { 0 };
+
+        int serverPort = selectFreePort();
+
+        Browser server = new Browser(
+            new Browser.Config()
+            .command("NONE")
+            .port(serverPort)
+        );
+        BrowserBuilder builder = BrowserBuilder.newBrowser(server)
+            .loadPage("server.html")
+            .loadFinished(() -> {
+                setLoaded("" + ++loaded[0]);
+                closeSoon(5000);
+            });
+        builder.showAndWait();
+
+        URL connect = new URL("http://localhost:" + serverPort);
+        InputStream is = connect.openStream();
+        Assert.assertNotNull(is, "Connection opened");
+        byte[] arr = new byte[4096];
+        int len = is.read(arr);
+        is.close();
+
+        final String page = new String(arr, 0, len, "UTF-8");
+        assertTrue(page.contains("

Server

"), "Server page loaded OK:\n" + page); + + show(connect.toURI()); + + awaitLoaded(1, loaded); + assertEquals(loaded[0], 1, "Connection has been opened"); + + show(connect.toURI()); + + awaitLoaded(2, loaded); + assertEquals(loaded[0], 2, "Second connection has been opened"); + + server.close(); + try { + InputStream unavailable = connect.openStream(); + fail("Stream can no longer be opened: " + unavailable); + } catch (IOException ex) { + // OK + } + } + + private static int selectFreePort() throws IOException { + ServerSocket temp = new ServerSocket(); + temp.bind(null); + int port = temp.getLocalPort(); + temp.close(); + return port; + } + + private static void awaitLoaded(int expected, int[] counter) throws InterruptedException { + int cnt = 100; + while (cnt-- > 0 && counter[0] != expected) { + Thread.sleep(100); + } + } + + @JavaScriptBody(args = { "value" }, body = "document.getElementById('loaded').innerHTML = value;") + private static native void setLoaded(String value); + + @JavaScriptBody(args = { "ms" }, body = "window.setTimeout(function() { window.close(); }, ms);") + private static native void closeSoon(int ms); + + private static void show(URI page) throws IOException { + IOException one, two; + try { + String ui = System.getProperty("os.name").contains("Mac") + ? "Cocoa" : "GTK"; + Show.show(ui, page); + return; + } catch (IOException ex) { + one = ex; + } + try { + Show.show("AWT", page); + return; + } catch (IOException ex) { + two = ex; + } + try { + Show.show(null, page); + } catch (IOException ex) { + two.initCause(one); + ex.initCause(two); + throw ex; + } + } +} diff --git a/browser/src/test/resources/com/dukescript/presenters/empty.html b/browser/src/test/resources/com/dukescript/presenters/empty.html new file mode 100644 index 00000000..3bb2554b --- /dev/null +++ b/browser/src/test/resources/com/dukescript/presenters/empty.html @@ -0,0 +1,37 @@ + + + + + Browser Test + + + + +

Browser Test

+
+ + + diff --git a/browser/src/test/resources/com/dukescript/presenters/server.html b/browser/src/test/resources/com/dukescript/presenters/server.html new file mode 100644 index 00000000..9f089e64 --- /dev/null +++ b/browser/src/test/resources/com/dukescript/presenters/server.html @@ -0,0 +1,38 @@ + + + + + Server + + + + +

Server

+
+ Loaded ? times. +
+ + diff --git a/generic/pom.xml b/generic/pom.xml new file mode 100644 index 00000000..cc907726 --- /dev/null +++ b/generic/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + + com.dukescript.presenters + pom + 2.0-SNAPSHOT + + + generic + DukeScript Generic Presenter + jar + 2.0-SNAPSHOT + + ${project.parent.basedir} + + + + + org.netbeans.html + net.java.html.boot + jar + ${net.java.html.version} + + + org.testng + testng + ${testng.version} + test + + + org.netbeans.html + net.java.html.json.tck + test + jar + ${net.java.html.version} + + + com.dukescript.api + strings + ${project.version} + provided + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + ${project.version} + + 1.6 + 1.6 + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-deploy-plugin + + false + + + + org.apache.maven.plugins + maven-javadoc-plugin + + com.dukescript.presenters.spi + + + + attach-javadocs + + jar + + + + + + org.netbeans.html + html4j-maven-plugin + ${net.java.html.version} + + + test-classes + process-test-classes + + process-js-annotations + + + ${project.build.directory}/test-classes + + + + + + + diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java b/generic/src/main/java/com/dukescript/presenters/spi/Generic.java new file mode 100644 index 00000000..4327b728 --- /dev/null +++ b/generic/src/main/java/com/dukescript/presenters/spi/Generic.java @@ -0,0 +1,959 @@ +package com.dukescript.presenters.spi; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + +import java.io.Flushable; +import java.io.IOException; +import java.io.Reader; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import org.netbeans.html.boot.spi.Fn; +import com.dukescript.api.strings.Texts; + +abstract class Generic implements Fn.Presenter, Fn.KeepAlive, Flushable { + private String msg; + private Item call; + private final NavigableSet exported; + private final int key; + private final boolean synchronous; + private final boolean evalJS; + private final String type; + private final String app; + private final CountDownLatch initialized = new CountDownLatch(1); + + Generic( + boolean synchronous, boolean evalJS, String type, String app + ) { + this.exported = new TreeSet(); + this.key = (int)(System.currentTimeMillis() / 777) % 1000; + this.synchronous = synchronous; + this.evalJS = evalJS; + this.type = type; + this.app = app; + } + + final Object lock() { + return initialized; + } + + abstract void log(Level level, String msg, Object... args); + + @Texts({ + "begin=try {\n" + + " @1('r', 'OK', 'OK', null, null);\n" + + "} catch (e) {\n" + + " console.warn(e);\n" + + "}\n", + + "init=(function(global) {" + + "\n var fncns = new Array();" + + "\n var js2j = new Array();" + + "\n function jobject(id,value) { this.id = id; this.v = value; return this; };" + + "\n jobject.prototype['native'] = true;" + + "\n jobject.prototype.valueOf = function() { return this.v ? this.v : '[jobject ' + this.id + ']'; };" + + "\n jobject.prototype.toString = jobject.prototype.valueOf;" + + "\n var toVM = global['@2'];" + + "\n delete global['@2'];" + + "\n if (typeof toVM !== 'function') {" + + "\n throw 'toVM should be a function: ' + toVM;" + + "\n }" + + "\n function toJava(method, r) {" + + "\n var t = typeof r;" + + "\n if (t === 'function') t = 'object';" + + "\n if (t === 'undefined' || r === null) {" + + "\n t = 'null';" + + "\n r = null;" + + "\n } else if (t === 'object') {" + + "\n if (r['native']) {" + + "\n t = 'java';" + + "\n r = r.id;" + + "\n } else if (Object.prototype.toString.call(r) === '[object Array]') {" + + "\n t = 'array';" + + "\n var l = r.length + ':';" + + "\n for (var i = 0; i < r.length; i++) {" + + "\n var toObj = toJava(null, r[i]);" + + "\n l += toObj.length + ':' + toObj;" + + "\n }" + + "\n r = l;" + + "\n } else {" + + "\n var size = js2j.length;" + + "\n js2j.push(r);" + + "\n r = size;" + + "\n }" + + "\n }" + + "\n if (method !== null) toVM(method, t, r, null, null);" + + "\n else return t + ':' + r;" + + "\n }" + + "\n var impl = {};" + + "\n impl.key = @1;" + + "\n global.ds = function(key) {" + + "\n if (key != impl.key) {" + + "\n impl = null;" + + "\n console.warn('Surprising access to Java with ' + key);" + + "\n }" + + "\n return impl;" + + "\n };" + + "\n impl.toJava = toJava;" + + "\n impl.rg = function(id, fn) {" + + "\n fncns[id] = fn;" + + "\n };" + + "\n impl.fn = function(index, n, self) {" + + "\n var args = Array.prototype.slice.call(arguments, 3);" + + "\n try {" + + "\n var fn = fncns[index];" + + "\n if (typeof fn !== 'function') throw 'Cannot find function at index: ' + index + ' in ' + fn + ' apply: ' + (fn ? fn.apply : undefined);" + + "\n var r = fn.apply(self, args);" + + "\n if (n) toJava('r', r);" + + "\n } catch (err) {" + + "\n if (typeof console !== 'undefined') console.warn('Error ' + err + ' at:\\n' + err.stack);" + + "\n if (n) toVM('r', 'error', '' + err + ' at:\\n' + err.stack, null, null);" + + "\n }" + + "\n };" + + "\n impl.o = function(i) {" + + "\n return js2j[i];" + + "\n };" + + "\n impl.j = function(n,v) {" + + "\n var r = new jobject(n,v);" + + "\n if (arguments.length > 2) {" + + "\n for (var i = 2; i < arguments.length; i++) {" + + "\n r[i - 2] = arguments[i];" + + "\n }" + + "\n r.length = arguments.length - 2;" + + "\n }" + + "\n return r;" + + "\n };" + + "\n impl.v = function(i) {" + + "\n return fncns[i];" + + "\n };" + + "\n impl.toVM = toVM;" + + "\n impl.toVM('r', 'OK', 'Initialized', null, null);" + + "\n})(this);", + + "error=Cannot initialize DukeScript: @1", + "version=$version" + }) + final void init() { + if (msg != null) { + for (;;) { + try { + log(Level.FINE, "Awaiting as of {0}", msg); + initialized.await(); + log(Level.FINE, "Waiting is over"); + return; + } catch (InterruptedException ex) { + log(Level.INFO, "Interrupt", ex); + } + } + } + this.msg = ""; + callbackFn(new ProtoPresenterBuilder.OnPrepared() { + @Override + public void callbackIsPrepared(String clbk) { + log(Level.FINE, "callbackReady with {0}", clbk); + loadJS(Strings.begin(clbk).toString()); + log(Level.FINE, "checking OK state"); + if (!assertOK()) { + final CharSequence err = Strings.error(msg); + log(Level.WARNING, "no OK: {0}", err); + throw new IllegalStateException(err.toString()); + } + log(Level.FINE, "assertOK"); + + loadJS(Strings.init(key, clbk).toString()); + + log(Level.FINE, "callbackReady: countingDown"); + initialized.countDown(); + } + }); + } + + /** @return the name of the callback function */ + abstract void callbackFn(ProtoPresenterBuilder.OnPrepared onReady); + abstract void loadJS(String js); + + public final String js2java(String method, + String a1, String a2, String a3, String a4 + ) throws Exception { + if ("r".equals(method)) { + result(a1, a2); + return null; + } else if ("c".equals(method)) { + return javacall(a1, a2, a3, a4); + } else if ("jr".equals(method)) { + return javaresult(); + } else { + throw new IllegalArgumentException(method); + } + } + + abstract void dispatch(Runnable r); + + /** Makes sure all pending calls into JavaScript are immediately + * performed. + * + * @throws IOException if something goes wrong + */ + @Override + public void flush() throws IOException { + if (initialized.getCount() == 0) { + flushImpl(); + } + } + + @Override + public Fn defineFn(String code, String[] names, boolean[] keepAlive) { + init(); + return new GFn(code, names, keepAlive); + } + + @Override + public Fn defineFn(String code, String... names) { + init(); + return new GFn(code, names, null); + } + + private static final class Key extends WeakReference { + private int hash; + + public Key(Object obj) { + super(obj); + this.hash = System.identityHashCode(obj); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 47 * hash + this.hash; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Key) { + Key other = (Key)obj; + if (hash != other.hash) { + return false; + } + return true; + } + return false; + } + } + + private Map ids = new HashMap(); + int identityHashCode(Object o) { + Key k = new Key(o); + Integer val = ids.get(k); + if (val == null) { + int s = ids.size(); + ids.put(k, s); + return s; + } + return val; + } + + final int registerObject(Object o, boolean weak, boolean[] justAdded, String[] valueOf) { + if (o instanceof Enum && valueOf != null) { + valueOf[0] = o.toString(); + } + int id = identityHashCode(o); + for (;;) { + Object exp = findObject(id); + if (o == exp) { + return id; + } + if (exp == null) { + if (justAdded != null) { + justAdded[0] = true; + } + exported.add(new Exported(id, weak, o)); + return id; + } + throw new IllegalStateException("Collision!"); + } + } + + final Object findObject(int id) { + Exported obj = exported.floor(new Exported(id, false, null)); + return obj == null || obj.id != id ? null : obj.get(); + } + + @Texts({ + "fnHead=var jsvm = {};\n", + "fnName=jsvm.@1 = function(", + "fnThiz=thiz", + "fnNoThiz= var thiz = null;\n", + "fnSep=,", + "fnParam=p@1", + "fnClose=) {\n", + "fnBegin= var encParams = ds(@1).toJava(null, [", + "fnPPar=@2 p@1", + "fnBody=]);\n" + + " var v = ds(@3).toVM('c', @1, '@2', thiz ? thiz.id : null, encParams);\n" + + " while (v !== null && v.indexOf && v.indexOf('javascript:') === 0) {\n" + + " var script = v.substring(11);\n" + + " try {\n" + + " var r = eval.call(null, script);\n" + + " } catch (e) { console.warn('error: ' + e + ' executing: ' + script + ' at:\\n' + e.stack); }\n" + + " v = ds(@3).toVM('jr', null, null, null, null);" + + " }\n" + + " return @4 ? eval('(' + v + ')') : v;\n" + + "};\n", + + "fnFoot=ds(@2).rg(@1, jsvm);\n" + }) + final Integer exportVm(Object vm) { + int jNumber = registerObject(vm, false, null, null); + int vmNumber = COUNTER.getAndIncrement(); + StringBuilder sb = new StringBuilder(); + sb.append(Strings.fnHead()); + for (Method m : vm.getClass().getMethods()) { + if (m.getDeclaringClass() == Object.class) { + continue; + } + final Class[] types = m.getParameterTypes(); + boolean instanceMethod = + types.length > 0 && + m.getName().startsWith(types[0].getName().replace('.', '_') + "$"); + int params = instanceMethod ? types.length - 1 : types.length; + sb.append(Strings.fnName(m.getName())); + String sep; + if (instanceMethod) { + sb.append(Strings.fnThiz()); + sep = Strings.fnSep(); + } else { + sep = ""; + } + for (int i = 0; i < params; i++) { + sb.append(sep); + sb.append(Strings.fnParam(i)); + sep = Strings.fnSep(); + } + sb.append(Strings.fnClose()); + if (!instanceMethod) { + sb.append(Strings.fnNoThiz()); + } + sb.append(Strings.fnBegin(key)); + for (int i = 0; i < params; i++) { + sb.append(Strings.fnPPar(i, i == 0 ? "" : ",")); + } + sb.append(Strings.fnBody(jNumber, m.getName(), key, evalJS)); + } + sb.append(Strings.fnFoot(vmNumber, key)); + deferExec(sb); + return vmNumber; + } + + @Texts({ + "v_null=null", + "v_number=number", + "v_java=java", + "v_object=object", + "v_array=array", + "v_boolean=boolean", + "v_error=error" + }) + final Object valueOf(String typeof, String res) { + if (Strings.v_null().equals(typeof)) { // NOI18N + return null; + } + if (Strings.v_number().equals(typeof)) { // NOI18N + return Double.valueOf(res); + } + if (Strings.v_java().equals(typeof)) { // NOI18N + return findObject(Integer.parseInt(res)); + } + if (Strings.v_object().equals(typeof)) { // NOI18N + return new JSObject(Integer.parseInt(res)); + } + if (Strings.v_array().equals(typeof)) { // NOI18N + int at = res.indexOf(':'); + int size = Integer.parseInt(res.substring(0, at)); + Object[] arr = new Object[size]; + at++; + for (int i = 0; i < size; i++) { + int next = res.indexOf(':', at); + int length = Integer.parseInt(res.substring(at, next)); + at = next + 1 + length; + arr[i] = valueOf(res.substring(next + 1, at)); + } + return arr; + } + if (Strings.v_boolean().equals(typeof)) { // NOI18N + return Boolean.valueOf(res); + } + if (Strings.v_error().equals(typeof)) { // NOI18N + throw new IllegalStateException(res); + } + return res; + } + + final Object valueOf(String typeAndValue) { + int colon = typeAndValue.indexOf(':'); + return valueOf(typeAndValue.substring(0, colon), typeAndValue.substring(colon + 1)); + } + + final void encodeObject(Object a, boolean weak, StringBuilder sb, int[] vmId) { + if (a == null) { + sb.append(Strings.v_null()); + } else if (a.getClass().isArray()) { + int len = Array.getLength(a); + sb.append('['); + String sep = ""; + for (int i = 0; i < len; i++) { + Object o = Array.get(a, i); + sb.append(sep); + encodeObject(o, weak, sb, null); + sep = ","; + } + sb.append(']'); + } else if (a instanceof Number) { + sb.append(a.toString()); + } else if (a instanceof String) { + sb.append('"'); + String s = (String)a; + int len = s.length(); + for (int i = 0; i < len; i++) { + char ch = s.charAt(i); + switch (ch) { + case '\\': sb.append("\\\\"); break; + case '\n': sb.append("\\n"); break; + case '\"': sb.append("\\\""); break; + default: + sb.append(ch); + break; + } + } + sb.append('"'); + } else if (a instanceof Boolean) { + sb.append(a.toString()); + } else if (a instanceof Character) { + sb.append((int)(Character)a); + } else if (a instanceof JSObject) { + sb.append("ds(").append(key).append(").o(").append(((JSObject) a).index).append(")"); + } else { + if (vmId != null) { + sb.append("ds(").append(key).append(").v(").append(vmId[0]).append(")"); + } else { + String[] valueOf = { null }; + sb.append("ds(").append(key).append(").j(").append(registerObject(a, weak, null, valueOf)); + sb.append(","); + encodeObject(valueOf[0], weak, sb, null); + if (a instanceof Object[]) { + for (Object n : ((Object[])a)) { + sb.append(","); + encodeObject(n, weak, sb, null); + } + } + sb.append(")"); + } + } + } + + interface OnReady { + void callbackReady(String name); + } + + private class Item implements Runnable { + final Item prev; + Boolean done; + + final Method method; + final Object thiz; + final Object[] params; + Object result; + + Item(Item prev, Method method, Object thiz, Object[] params) { + this.prev = prev; + this.method = method; + this.thiz = thiz; + this.params = adaptParams(method, Arrays.asList(params)); + this.toExec = null; + } + + + protected final String inJavaScript(boolean[] finished) { + if (this.method != null) { + return js(finished); + } else { + return sj(finished); + } + } + protected final void inJava() { + if (this.method == null) { + return; + } + if (done == null) { + done = false; + try { + log(Level.FINE, "Calling {0}", method); + result = method.invoke(thiz, params); + } catch (Exception ex) { + log(Level.SEVERE, "Cannot invoke " + method + " on " + thiz + " with " + Arrays.toString(params), ex); + } finally { + done = true; + log(Level.FINE, "Result: {0}", result); + } + } + } + + @Override public void run() { + synchronized (lock()) { + log(Level.FINE, "run: {0}", this); + inJava(); + lock().notifyAll(); + } + } + + + protected String js(boolean[] finished) { + if (Boolean.TRUE.equals(done)) { + StringBuilder sb = new StringBuilder(); + encodeObject(result, false, sb, null); + finished[0] = true; + return sb.toString(); + } + return null; + } + + private final String toExec; + private String typeof; + + public Item(Item prev, String toExec) { + this.prev = prev; + this.toExec = toExec; + + this.method = null; + this.params = null; + this.thiz = null; + } + + protected String sj(boolean[] finished) { + finished[0] = false; + if (Boolean.TRUE.equals(done)) { + return null; + } + done = true; + return "javascript:" + toExec; + } + + protected final void result(String typeof, String result) { + if (this.method != null) { + throw new UnsupportedOperationException(); + } + this.typeof = typeof; + this.result = result; + log(Level.FINE, "result ({0}): {1} for {2}", typeof, result, toExec); + } + } // end of Item + + final void result(String typeof, String res) { + log(Level.FINE, "result@{0}: {1}", typeof, res); + synchronized (lock()) { + if ("OK".equals(typeof)) { + log(Level.FINE, "init: {0}", res); + this.msg = res; + lock().notifyAll(); + return; + } + call.result(typeof, res); + call = call.prev; + lock().notifyAll(); + } + } + + final String javacall( + String vmNumber, String fnName, String thizId, String encParams + ) throws Exception { + synchronized (lock()) { + Object vm = findObject(Integer.parseInt(vmNumber)); + assert vm != null; + final Object obj = thizId == null || "null".equals(thizId) + ? null : valueOf("java", thizId); + Method method = null; + for (Method m : vm.getClass().getMethods()) { + if (m.getName().equals(fnName)) { + method = m; + break; + } + } + assert method != null; + List params = new ArrayList(); + if (obj != null) { + params.add(obj); + } + params.addAll(Arrays.asList((Object[]) valueOf(encParams))); + Object[] converted = adaptParams(method, params); + boolean first = call == null; + log(Level.FINE, "jc: {0}@{1}args: {2} is first: {3}, now: {4}", new Object[]{method.getName(), vm, params, first, call}); + call = new Item(call, method, vm, converted); + if (first || synchronous) { + if (call != null) { + dispatch(call); + } + } else { + lock().notifyAll(); + } + return javaresult(); + } + } + + final String javaresult() throws IllegalStateException, InterruptedException { + synchronized (lock()) { + boolean[] finished = {false}; + for (;;) { + if (deferred != null) { + deferred.insert(0, "javascript:"); + String ret = deferred.toString(); + deferred = null; + return ret; + } + finished[0] = false; + String jsToExec = call.inJavaScript(finished); + log(Level.FINE, "jr: {0} jsToExec: {1} finished: {2}", new Object[]{call, jsToExec, finished[0]}); + if (jsToExec != null) { + if (finished[0]) { + call = call.prev; + } + return jsToExec; + } + lock().wait(); + } + } + } + + private StringBuilder deferred; + private Collection arguments = new LinkedList(); + + public final void loadScript(final Reader reader) throws Exception { + StringBuilder sb = new StringBuilder(); + char[] arr = new char[4092]; + for (;;) { + int len = reader.read(arr); + if (len == -1) { + break; + } + sb.append(arr, 0, len); + } + deferExec(sb); + } + + + final void deferExec(StringBuilder sb) { + synchronized (lock()) { + log(Level.FINE, "deferExec: {0} empty: {1}, call: {2}", new Object[]{sb, deferred == null, call}); + if (deferred == null) { + deferred = sb; + } else { + deferred.append(sb); + } + } + } + + @Texts({ + "flushExec=\n\nds(@1).toJava('r',null);\n" + }) + void flushImpl() { + synchronized (lock()) { + if (deferred != null) { + log(Level.FINE, "flush: {0}", deferred); + exec(Strings.flushExec(key).toString()); + } + } + } + + Object exec(String fn) { + Object ret; + boolean first; + synchronized (lock()) { + if (deferred != null) { + deferred.append(fn); + fn = deferred.toString(); + deferred = null; + log(Level.FINE, "Flushing {0}", fn); + } + + Item myCall; + boolean load; + if (call != null) { + call = myCall = new Item(call, fn); + lock().notifyAll(); + load = synchronous; + first = false; + } else { + call = myCall = new Item(null, null); + load = true; + first = true; + } + if (load) { + loadJS(fn); + } + for (;;) { + if (myCall.typeof != null) { + break; + } + try { + lock().wait(); + } catch (InterruptedException ex) { + log(Level.SEVERE, null, ex); + } + if (call != null) { + call.inJava(); + } + lock().notifyAll(); + } + ret = valueOf(myCall.typeof, (String) myCall.result); + } + if (first) { + arguments.clear(); + } + return ret; + } + + final boolean assertOK() { + synchronized (lock()) { + if (msg == null || msg.length() == 0) { + try { + lock().wait(10000); + } catch (InterruptedException ex) { + // OK, go on and check + } + } + return "OK".equals(msg) || "Initialized".equals(msg); + } + } + + private static Object[] adaptParams(Method toCall, List args) { + final Object[] arr = new Object[args.size()]; + final Class[] types = toCall.getParameterTypes(); + for (int i = 0; i < arr.length; i++) { + arr[i] = adaptType(types[i], args.get(i)); + } + return arr; + } + + private static Object adaptType(Class type, Object value) { + if (type.isPrimitive() && value instanceof Number) { + final Number n = (Number)value; + if (type == Byte.TYPE) return n.byteValue(); + if (type == Short.TYPE) return n.shortValue(); + if (type == Integer.TYPE) return n.intValue(); + if (type == Long.TYPE) return n.longValue(); + if (type == Float.TYPE) return n.floatValue(); + if (type == Double.TYPE) return n.doubleValue(); + if (type == Character.TYPE) return (char)n.intValue(); + } + return value; + } + + private static final class JSObject { + private final int index; + + public JSObject(int index) { + this.index = index; + } + + @Override + public int hashCode() { + return 37 * this.index; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final JSObject other = (JSObject) obj; + return this.index == other.index; + } + + @Texts({ + "jsObject=[jsobject-@1]" + }) + @Override + public String toString() { + return Strings.jsObject(index).toString(); + } + + } // end of JSObject + + static final AtomicInteger COUNTER = new AtomicInteger(0); + @Texts({ + "registerFn=ds(@2).rg(@1, function(", + "registerCode=) {\n@1\n});", + "v_vm=vm" + }) + private final class GFn extends Fn { + private final int id; + private final int[] vmId; + private final boolean[] keepAlive; + + public GFn(String code, String[] names, boolean[] ka) { + super(Generic.this); + this.id = COUNTER.getAndIncrement(); + this.keepAlive = ka; + + StringBuilder sb = new StringBuilder(1024); + sb.append(Strings.registerFn(id, key)); + String sep = ""; + boolean isVm = false; + for (String n : names) { + sb.append(sep).append(n); + sep = ","; + isVm = false; + if (Strings.v_vm().equals(n)) { + isVm = true; + } + } + sb.append(Strings.registerCode(code)); + this.vmId = isVm ? new int[] { -1 } : null; + deferExec(sb); + } + + @Override + public Object invoke(Object thiz, Object... args) throws Exception { + return invokeImpl(true, thiz, args); + } + + @Override + public void invokeLater(Object thiz, Object... args) throws Exception { + invokeImpl(false, thiz, args); + } + + @Texts({ + "invokeImplFn=ds(@3).fn(@1, @2, " + }) + private Object invokeImpl(boolean wait4js, Object thiz, Object... args) throws Exception { + if (vmId != null && vmId[0] < 0) { + vmId[0] = exportVm(args[args.length - 1]); + } + + StringBuilder sb = new StringBuilder(256); + sb.append(Strings.invokeImplFn(id, wait4js, key)); + encodeObject(thiz, false, sb, null); + for (int i = 0; i < args.length; i++) { + sb.append(", "); + boolean weak = keepAlive != null && !keepAlive[i]; + encodeObject(args[i], weak, sb, i == args.length - 1 ? vmId : null); + } + sb.append(");"); + + arguments.add(thiz); + arguments.add(args); + + if (wait4js) { + return exec(sb.toString()); + } else { + deferExec(sb); + return null; + } + } + } + + private static final class Exported implements Comparable { + private final int id; + private final Object obj; + private final boolean ref; + + Exported(int id, boolean ref, Object obj) { + this.id = id; + this.obj = ref ? createReferenceFor(obj) : obj; + this.ref = ref; + WeakHolder.clean(); + } + + protected Object get() { + if (ref) { + return ((Reference)obj).get(); + } else { + return obj; + } + } + + @Override + public int compareTo(Exported o) { + return id - o.id; + } + + private static Object createReferenceFor(Object obj) { + Reference ref = new WeakReference(obj); + if (obj instanceof Reference) { + Reference myRef = (Reference) obj; + if (obj.getClass().getName().equals("org.netbeans.html.ko4j.Knockout")) { + // workaround for #255677 + WeakHolder h = new WeakHolder(myRef.get(), obj); + h.register(); + } + } + return ref; + } + } + + private static final class WeakHolder extends PhantomReference { + private static final ReferenceQueue QUEUE = new ReferenceQueue(); + private static final Set active = new HashSet(); + private final Object knockout; + + public WeakHolder(Object referent, Object knockout) { + super(referent, QUEUE); + this.knockout = knockout; + } + + static void clean() { + for (;;) { + WeakHolder h = (WeakHolder) QUEUE.poll(); + if (h == null) { + break; + } + active.remove(h); + } + } + + void register() { + active.add(this); + } + } +} diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Level.java b/generic/src/main/java/com/dukescript/presenters/spi/Level.java new file mode 100644 index 00000000..ff962644 --- /dev/null +++ b/generic/src/main/java/com/dukescript/presenters/spi/Level.java @@ -0,0 +1,41 @@ +package com.dukescript.presenters.spi; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + +/** Logging levels similar to java.util.logging ones. */ +enum Level { + FINE, INFO, WARNING, SEVERE; + + public int intValue() { + switch (this) { + case SEVERE: return 1000; + case WARNING: return 900; + case INFO: return 800; + case FINE: return 500; + } + return 0; + } +} diff --git a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java new file mode 100644 index 00000000..329b7b1c --- /dev/null +++ b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java @@ -0,0 +1,60 @@ +package com.dukescript.presenters.spi; + +import java.io.Flushable; +import org.netbeans.html.boot.spi.Fn; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + +/** The prototypical presenter. An implementation of a {@link Fn.Presenter} based on + * top of textual protocol transferred between JVM and JavaScript engines. Use + * {@link ProtoPresenterBuilder#newBuilder()} to construct instance of this + * interface. + */ +public interface ProtoPresenter extends Fn.Presenter, Fn.KeepAlive, Flushable { + /** Dispatches callback from JavaScript back into appropriate + * Java implementation. User of {@link ProtoPresenterBuilder} is expected + * to register {@link ProtoPresenterBuilder#preparator} and setup a JavaScript + * call to this method. + * + * @param method the type of call to make + * @param a1 first argument + * @param a2 second argument + * @param a3 third argument + * @param a4 fourth argument + * @return returned string + * @throws Exception if something goes wrong + */ + String js2java(String method, String a1, String a2, String a3, String a4) throws Exception; + + /** Looks for additional data stored in the presenter. Data + * can be registered via {@link ProtoPresenterBuilder#register} method. + * + * @param the type of data to search for + * @param type exact type of the data + * @return found data or null + */ + T lookup(Class type); +} diff --git a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java new file mode 100644 index 00000000..6fb38353 --- /dev/null +++ b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java @@ -0,0 +1,294 @@ +package com.dukescript.presenters.spi; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; +import org.netbeans.html.boot.spi.Fn; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + +/** The prototypical presenter builder. Builds a {@link Fn.Presenter} based on + * top of textual protocol transferred between JVM and JavaScript engines. + */ +public final class ProtoPresenterBuilder { + private Evaluator loadScript; + private Executor executor; + private Preparator onReady; + private boolean sync; + private boolean eval; + private String type; + private String app; + private Displayer displayer; + private Logger logger; + private boolean implementExecutor; + private final List data = new ArrayList(); + + private ProtoPresenterBuilder() { + } + + /** Starts building new, customized instance of {@link ProtoPresenter}. + * @return new builder + */ + public static ProtoPresenterBuilder newBuilder() { + return new ProtoPresenterBuilder(); + } + + /** Interfaces for evaluation of JavaScript code. + * Registered via {@link #loadJavaScript} method. + */ + @FunctionalInterface + public interface Evaluator { + /** Evaluates (potentially asynchronously) given code in the JavaScript + * engine. + * @param code the code to evaluate + */ + public void eval(String code); + } + + /** + * Registers an implementation of {@link Evaluator}. + * @param loadScript the evaluator to use + * @param synchronous is the evaluator synchronous or asynchronous + * @return this builder + */ + public ProtoPresenterBuilder loadJavaScript(Evaluator loadScript, boolean synchronous) { + this.loadScript = loadScript; + this.sync = synchronous; + return this; + } + + /** + * Registers the executor to run all tasks in. + * + * @param executor the executor + * @param implementExecutor {@code true} if the presenter created + * by {@link #build()} method should also implement the {@link Executor} + * interface + * @return this builder + */ + public ProtoPresenterBuilder dispatcher(Executor executor, boolean implementExecutor) { + this.executor = executor; + this.implementExecutor = implementExecutor; + return this; + } + + /** Prepares the JavaScript environment. Defines a globally visible + * JavaScript function which takes five string arguments and + * then calls {@link ProtoPresenter#js2java(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) } + * method of the {@link #build() created} {@link ProtoPresenter}. + * Once the function is ready, it should notify the system + * of the name of the function by calling {@link OnPrepared#callbackIsPrepared(java.lang.String)}. + */ + @FunctionalInterface + public interface Preparator { + void prepare(OnPrepared onReady); + } + + /** Callback to make when {@link Preparator#prepare(OnPrepared)} is + * finished. + */ + public static abstract class OnPrepared { + OnPrepared() { + } + + /** Called when callback function prepared by {@link Preparator#prepare} + * is ready. + * + * @param callbackFunctionName the name of created JavaScript function + */ + public abstract void callbackIsPrepared(String callbackFunctionName); + } + + /** Registers instance of {@link Preparator}. + * + * @param onReady the instance to use + * @param evalJavaScript is the result of function registered by {@link OnPrepared#callbackIsPrepared(java.lang.String)} + * just a string (return {@code true}) or real JavaScript object (return {@code false})? + * @return this builder + */ + public ProtoPresenterBuilder preparator(Preparator onReady, boolean evalJavaScript) { + this.onReady = onReady; + this.eval = evalJavaScript; + return this; + } + + /** The type of the presenter (iOS, Android, etc.). + * @param type string to identify the presenter + * @return this builder + */ + public ProtoPresenterBuilder type(String type) { + this.type = type; + return this; + } + + /** The identification of the application. + * + * @param app string to identify the application + * @return this builder + */ + public ProtoPresenterBuilder app(String app) { + this.app = app; + return this; + } + + /** Interface to handle displaying of a URL. + * Register via {@link ProtoPresenterBuilder#displayer(org.netbeans.html.presenter.spi.ProtoPresenterBuilder.Displayer)}. + */ + @FunctionalInterface + public interface Displayer { + /** Display given URL and callback when the page is ready. + * + * @param url the page to display + * @param onLoad callback to make when the page is ready + */ + public void displayPage(URL url, Runnable onLoad); + } + + /** Registers new displayer. + * + * @param displayer the instance of displayer. + * @return this builder + */ + public ProtoPresenterBuilder displayer(Displayer displayer) { + this.displayer = displayer; + return this; + } + + /** Registers additional data with the {@link ProtoPresenter}.The data can be obtained by {@link ProtoPresenter#lookup}. + * @param data instance of some data + * @return this builder + */ + public ProtoPresenterBuilder register(Object data) { + this.data.add(data); + return this; + } + + /** Implementation of a logging interface from the {@link ProtoPresenter}. + */ + @FunctionalInterface + public interface Logger { + /** Log a message. Uses levels and message formats suitable for + * java.util.logging package. + * + * @param level value from 500-1000 + * @param msg message with optional curly braces parameters + * @param args the parameter values + */ + public void log(int level, String msg, Object... args); + } + + /** Registers instance of logger. + * + * @param logger instance of logger - may be {@code null} + * @return this builder + */ + public ProtoPresenterBuilder logger(Logger logger) { + this.logger = logger; + return this; + } + + /** Builds instance of presenter based on registered values. + * @return instance of presenter + */ + public ProtoPresenter build() { + if (implementExecutor) { + return new GenPresenterWithExecutor(this); + } + return new GenPresenter(this); + } + + private static final class GenPresenterWithExecutor extends GenPresenter implements Executor { + GenPresenterWithExecutor(ProtoPresenterBuilder b) { + super(b); + } + + @Override + public void execute(Runnable command) { + dispatch(command); + } + } + + private static class GenPresenter extends Generic implements ProtoPresenter { + private final Evaluator loadScript; + private final Executor executor; + private final Preparator onReady; + private final Displayer displayer; + private final Logger logger; + private final Object[] data; + + GenPresenter(ProtoPresenterBuilder b) { + super(b.sync, b.eval, b.type, b.app); + this.loadScript = b.loadScript; + this.executor = b.executor; + this.onReady = b.onReady; + this.displayer = b.displayer; + this.logger = b.logger; + this.data = b.data.toArray(); + } + + @Override + void log(Level level, String msg, Object... args) { + if (logger != null) { + logger.log(level.intValue(), msg, args); + } + } + + @Override + void callbackFn(ProtoPresenterBuilder.OnPrepared onReady) { + this.onReady.prepare(onReady); + } + + @Override + void loadJS(String js) { + loadScript.eval(js); + } + + @Override + void dispatch(Runnable r) { + executor.execute(r); + } + + @Override + public void displayPage(URL url, Runnable r) { + if (url == null && r == null) { + init(); + } else { + displayer.displayPage(url, r); + } + } + + @Override + public T lookup(Class type) { + for (Object o : data) { + if (type == o.getClass()) { + return type.cast(o); + } + } + return null; + } + } +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java b/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java new file mode 100644 index 00000000..f137ee93 --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java @@ -0,0 +1,71 @@ +package com.dukescript.presenters.spi; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.dukescript.presenters.spi.ProtoPresenterBuilder; +import com.dukescript.presenters.spi.Level; +import com.dukescript.presenters.spi.Generic; +import java.net.URL; +import static org.testng.Assert.*; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class ValueOfTest { + private Generic p; + @BeforeMethod public void initInstance() { + p = new Generic(true, true, "type", "app") { + @Override + void log(Level level, String msg, Object... args) { + } + + @Override + void callbackFn(ProtoPresenterBuilder.OnPrepared onReady) { + } + + @Override + void loadJS(String js) { + } + + @Override + void dispatch(Runnable r) { + } + + @Override + public void displayPage(URL url, Runnable r) { + } + }; + } + + + @Test public void parseSimpleArray() { + Object res = p.valueOf("array:1:8:number:6"); + assertTrue(res instanceof Object[], "It is an array: " + res); + Object[] arr = (Object[]) res; + assertEquals(arr.length, 1, "One array item"); + assertEquals(arr[0], 6.0, "Value is six"); + } +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java b/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java new file mode 100644 index 00000000..4be2ab53 --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java @@ -0,0 +1,71 @@ +package com.dukescript.presenters.spi.test; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.lang.reflect.Method; +import java.util.logging.Level; +import javax.script.ScriptException; +import static org.testng.Assert.assertEquals; +import org.testng.annotations.AfterClass; +import org.testng.annotations.Factory; + +public class CallbackTest { + @Factory public static Object[] deadlockTests() throws Exception { + return GenericTest.createTests(new CBP()); + } + + @AfterClass public static void countCallbacks() { + assertEquals(Counter.callbacks, Counter.calls, "Every call to loadJS is prefixed with a callback"); + } + + + private static final class CBP extends Testing { + + @Override + protected void loadJS(String js) { + dispatch(new Runnable () { + @Override + public void run() { + try { + Object res = eng.eval("if (this.counter) this.counter()"); + LOG.log(Level.FINE, "counter res: {0}", res); + } catch (ScriptException ex) { + LOG.log(Level.SEVERE, null, ex); + } + } + }); + super.loadJS(js); + } + + @Override void beforeTest(Class testClass) throws Exception { + Class cntr = testClass.getClassLoader().loadClass(Counter.class.getName()); + Method rc = cntr.getMethod("registerCounter"); + rc.invoke(null); + } + + } +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java b/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java new file mode 100644 index 00000000..ca136b02 --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java @@ -0,0 +1,121 @@ +package com.dukescript.presenters.spi.test; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import org.netbeans.html.boot.spi.Fn; +import org.testng.IHookCallBack; +import org.testng.IHookable; +import org.testng.ITest; +import org.testng.ITestResult; +import org.testng.annotations.Test; + +public final class Case implements ITest, IHookable, Runnable { + private final Fn.Presenter p; + private CountDownLatch finished; + private final Method m; + private Object result; + private Object inst; + private int cnt; + + Case(Fn.Presenter p, Method m) { + this.p = p; + this.m = m; + } + + @Override + public String getTestName() { + return m.getDeclaringClass().getSimpleName() + "." + m.getName(); + } + + @Test + public void executeTest() throws Exception { + for (;;) { + if (p instanceof Executor) { + finished = new CountDownLatch(1); + ((Executor)p).execute(this); + finished.await(); + } else { + run(); + } + if (result instanceof InterruptedException && cnt++ < 100) { + Thread.sleep(100); + result = null; + continue; + } + break; + } + if (result instanceof Exception) { + throw (Exception) result; + } else if (result instanceof Error) { + throw (Error) result; + } + } + + @Override + public void run() { + Closeable c = Fn.activate(p); + try { + if (p instanceof Testing) { + Testing tp = (Testing) p; + tp.beforeTest(m.getDeclaringClass()); + } + if (inst == null) { + inst = m.getDeclaringClass().newInstance(); + } + result = m.invoke(inst); + if (result == null) { + result = this; + } + } catch (InvocationTargetException ex) { + Throwable r = ex.getTargetException(); + result = r; + } catch (Exception ex) { + result = ex; + } finally { + try { + c.close(); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + if (finished != null) { + finished.countDown(); + } + } + } + + @Override + public void run(IHookCallBack ihcb, ITestResult itr) { + ihcb.runTestMethod(itr); + } + +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java b/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java new file mode 100644 index 00000000..1991a256 --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java @@ -0,0 +1,55 @@ +package com.dukescript.presenters.spi.test; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import net.java.html.js.JavaScriptBody; + +public final class Counter { + static int calls; + static int callbacks; + + static int count() { + return ++callbacks; + } + + public static final void registerCounter() { + if (rCounter()) { + callbacks = 0; + } + } + + @JavaScriptBody(args = {}, javacall = true, body + = "if (!this.counter) {\n" + + " this.counter = function() { return @com.dukescript.presenters.spi.test.Counter::count()(); };\n" + + " return true;\n" + + "} else {\n" + + " return false;\n" + + "}\n" + ) + private static native boolean rCounter(); + +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java b/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java new file mode 100644 index 00000000..871e7027 --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java @@ -0,0 +1,102 @@ +package com.dukescript.presenters.spi.test; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import net.java.html.boot.BrowserBuilder; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.json.tck.JavaScriptTCK; +import org.netbeans.html.json.tck.KOTest; +import com.dukescript.presenters.spi.ProtoPresenterBuilder; +import org.testng.annotations.Factory; + +public class GenericTest extends JavaScriptTCK { + private static Class browserClass; + + public GenericTest() { + } + + @Factory public static Object[] compatibilityTests() throws Exception { + return createTests(new Testing()); + } + + static Object[] createTests(Testing p) throws Exception { + Fn.Presenter presenter = p.presenter; + + final BrowserBuilder bb = BrowserBuilder.newBrowser(presenter).loadClass(GenericTest.class). + loadPage("empty.html"). + invoke("initialized"); + + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + bb.showAndWait(); + } + }); + + List res = new ArrayList(); + Class test = + loadClass().getClassLoader().loadClass(KOTest.class.getName()). + asSubclass(Annotation.class); + + Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null); + for (Class c : arr) { + for (Method m : c.getMethods()) { + if (m.getAnnotation(test) != null) { + res.add(new Case(presenter, m)); + } + } + } + return res.toArray(); + } + + public static Class[] tests() { + return testClasses(); + } + + static synchronized Class loadClass() throws InterruptedException { + while (browserClass == null) { + GenericTest.class.wait(); + } + return browserClass; + } + + public static synchronized void ready(Class browserCls) throws Exception { + browserClass = browserCls; + GenericTest.class.notifyAll(); + } + + public static void initialized() throws Exception { + Class classpathClass = ClassLoader.getSystemClassLoader().loadClass(GenericTest.class.getName()); + Method m = classpathClass.getMethod("ready", Class.class); + m.invoke(null, GenericTest.class); + } +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java b/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java new file mode 100644 index 00000000..cd5c174e --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java @@ -0,0 +1,37 @@ +package com.dukescript.presenters.spi.test; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import static com.dukescript.presenters.spi.test.GenericTest.createTests; +import org.testng.annotations.Factory; + +public class SynchronizedTest { + @Factory public static Object[] compatibilityTests() throws Exception { + return createTests(new Testing.Synchronized()); + } + +} diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java b/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java new file mode 100644 index 00000000..29812a65 --- /dev/null +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java @@ -0,0 +1,179 @@ +package com.dukescript.presenters.spi.test; + +/* + * #%L + * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.net.URL; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import com.dukescript.presenters.spi.ProtoPresenter; +import com.dukescript.presenters.spi.ProtoPresenterBuilder; + +class Testing { + static final Logger LOG = Logger.getLogger(Testing.class.getName()); + final Executor QUEUE; + final ScriptEngine eng; + final boolean sync; + final ProtoPresenter presenter; + + public Testing() { + this(false); + } + + protected Testing(boolean sync) { + this(sync, Executors.newSingleThreadExecutor()); + } + protected Testing(boolean sync, Executor queue) { + this.sync = sync; + this.QUEUE = queue; + this.presenter = ProtoPresenterBuilder.newBuilder() + .app("Testing") + .type("test") + .dispatcher(QUEUE, false) + .loadJavaScript(this::loadJS, sync) + .displayer(this::displayPage) + .preparator(this::callbackFn, true) + .logger(this::log) + .build(); + + ScriptEngineManager sem = new ScriptEngineManager(); + eng = sem.getEngineByMimeType("text/javascript"); + try { + eng.eval("function alert(m) { Packages.java.lang.System.out.println(m); };"); + } catch (ScriptException ex) { + throw new IllegalStateException(ex); + } + } + + protected void log(int priority, String msg, Object... args) { + Level level = findLevel(priority); + if (args.length == 1 && args[0] instanceof Throwable) { + LOG.log(level, msg, (Throwable)args[0]); + } else { + LOG.log(level, msg, args); + } + } + + private static Level findLevel(int priority) { + if (priority >= Level.SEVERE.intValue()) { + return Level.SEVERE; + } + if (priority >= Level.WARNING.intValue()) { + return Level.WARNING; + } + if (priority >= Level.INFO.intValue()) { + return Level.INFO; + } + return Level.FINE; + } + + public final class Clbk { + private Clbk() { + } + + public String pass(String method, String a1, String a2, String a3, String a4) throws Exception { + return presenter.js2java(method, a1, a2, a3, a4); + } + } + private final Clbk clbk = new Clbk(); + + protected void callbackFn(ProtoPresenterBuilder.OnPrepared ready) { + eng.getBindings(ScriptContext.ENGINE_SCOPE).put("jvm", clbk); + try { + eng.eval("(function(global) {\n" + + " var jvm = global.jvm;\n" + + " global.testingCB = function(m,a1,a2,a3,a4) {\n" + + " return jvm.pass(m,a1,a2,a3,a4);\n" + + " }\n" + + "})(this);\n" + ); + } catch (ScriptException ex) { + throw new IllegalStateException(ex); + } + eng.getBindings(ScriptContext.ENGINE_SCOPE).put("jvm", ""); + ready.callbackIsPrepared("testingCB"); + } + + protected void loadJS(final String js) { + QUEUE.execute(new Runnable() { + public void run() { + try { + Object res = eng.eval(js); + LOG.log(Level.FINE, "Result: {0}", res); + } catch (Throwable ex) { + LOG.log(Level.SEVERE, "Can't process " + js, ex); + } + } + }); + } + + public void displayPage(URL url, Runnable r) { + r.run(); + } + + public void dispatch(Runnable r) { + QUEUE.execute(r); + } + + void beforeTest(Class declaringClass) throws Exception { + } + + static class Synchronized extends Testing { + public Synchronized() { + super(true, (r) -> r.run()); + } + + @Override + protected void loadJS(final String js) { + try { + Object res = eng.eval(js); + LOG.log(Level.FINE, "Result: {0}", res); + } catch (Throwable ex) { + LOG.log(Level.SEVERE, "Can't process " + js, ex); + } + } + + @Override + public void displayPage(URL url, Runnable r) { + r.run(); + } + + @Override + public void dispatch(Runnable r) { + r.run(); + } + + @Override + void beforeTest(Class declaringClass) throws Exception { + } + } // end of Synchronized +} diff --git a/renderer/pom.xml b/renderer/pom.xml new file mode 100644 index 00000000..d2c22b40 --- /dev/null +++ b/renderer/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.dukescript.presenters + pom + 2.0-SNAPSHOT + + renderer + jar + Desktop Browser Renderer + + ${project.parent.basedir} + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + false + + + + attach-javadocs + + jar + + + + + + + + + org.testng + testng + ${testng.version} + test + + + net.java.dev.jna + jna + ${jna.version} + + + org.netbeans.html + net.java.html.boot + ${net.java.html.version} + + + \ No newline at end of file diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java b/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java new file mode 100644 index 00000000..e99ee697 --- /dev/null +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java @@ -0,0 +1,65 @@ +package com.dukescript.presenters.renderer; + +/* + * #%L + * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.sun.jna.Pointer; +import java.io.IOException; +import java.net.URI; +import java.util.logging.Level; + +final class AWT extends Show { + @Override + public void show(URI page) throws IOException { + try { + LOG.log(Level.FINE, "Trying Desktop.browse on {0} {2} by {1}", new Object[]{ + System.getProperty("java.vm.name"), + System.getProperty("java.vm.vendor"), + System.getProperty("java.vm.version"),}); + java.awt.Desktop.getDesktop().browse(page); + LOG.log(Level.FINE, "Desktop.browse successfully finished"); + System.in.read(); + } catch (UnsupportedOperationException ex) { + LOG.log(Level.FINE, "Desktop.browse not supported: {0}", ex.getMessage()); + throw new IOException(ex); + } + } + + @Override + public JSC jsc() { + return null; + } + + @Override + public Pointer jsContext() { + return null; + } + + @Override + public void execute(Runnable command) { + command.run(); + } +} diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java b/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java new file mode 100644 index 00000000..786af000 --- /dev/null +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java @@ -0,0 +1,483 @@ +package com.dukescript.presenters.renderer; + +/* + * #%L + * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.sun.jna.Callback; +import com.sun.jna.CallbackThreadInitializer; +import com.sun.jna.FromNativeContext; +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.NativeMapped; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; +import java.io.Closeable; +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import org.netbeans.html.boot.spi.Fn; + +final class Cocoa extends Show implements Callback { + private final Fn.Presenter presenter; + private final Runnable onPageLoad; + private final Runnable onContext; + private final JSC jsc; + + private static final Queue QUEUE = new ConcurrentLinkedQueue(); + private static Pointer NSApp; + private static Pointer appDelPtr; + private static Pointer doMainSelector; + private static Thread dispatchThread; + + private AppDidStart appDidStart; + private Ready ready; + private ContextCreated contextCreated; + private UIDelegate ui; + private DialogHandler[] dialogs; + private Pointer jsContext; + private String page; + private Pointer webView; + private Pointer mainWindow; + + Cocoa() { + this(null, null, null, false); + } + + Cocoa(Fn.Presenter p, Runnable onPageLoad, Runnable onContext, boolean hl) { + this.presenter = p; + this.onPageLoad = onPageLoad; + this.onContext = onContext; + this.jsc = (JSC) Native.loadLibrary("JavaScriptCore", JSC.class, Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true)); + } + + @Override + public JSC jsc() { + return jsc; + } + + @Override + public Pointer jsContext() { + return jsContext; + } + + @Override + public void show(URI page) { + this.page = page.toASCIIString(); + + ensureHttpAccess(page); + + Native.loadLibrary("WebKit", WebKit.class); + + appDidStart = new AppDidStart(); + contextCreated = new ContextCreated(); + ready = new Ready(); + ui = new UIDelegate(); + dialogs = new DialogHandler[3]; + dialogs[0] = new DialogHandler(0); + dialogs[1] = new DialogHandler(1); + dialogs[2] = new DialogHandler(2); + + if (appDelPtr == null) { + ObjC objC = ObjC.INSTANCE; + Pointer appDelClass = objC.objc_allocateClassPair(objC.objc_getClass("NSObject"), "AppDelegate", 0); + objC.class_addMethod(appDelClass, objC.sel_getUid("applicationDidFinishLaunching:"), appDidStart, "i@:@"); + doMainSelector = objC.sel_getUid("doMain"); + Native.setCallbackThreadInitializer(this, new CallbackThreadInitializer(false, false, "Cocoa Dispatch Thread")); + objC.class_addMethod(appDelClass, doMainSelector, this, "i@"); + objC.class_addMethod(appDelClass, objC.sel_getUid("webView:didCreateJavaScriptContext:forFrame:"), contextCreated, "v@:@:@"); + objC.class_addMethod(appDelClass, objC.sel_getUid("webView:didFinishLoadForFrame:"), ready, "v@:@"); + objC.class_addMethod(appDelClass, objC.sel_getUid("webView:createWebViewWithRequest:"), ui, "v@:@"); + objC.class_addMethod(appDelClass, objC.sel_getUid("webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:"), dialogs[0], "v@:@:@"); + objC.class_addMethod(appDelClass, objC.sel_getUid("webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:"), dialogs[1], "v@:@:@"); + objC.class_addMethod(appDelClass, objC.sel_getUid("webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:"), dialogs[2], "v@:@:@"); + objC.objc_registerClassPair(appDelClass); + + long appDelObj = send(objC.objc_getClass("AppDelegate"), "alloc"); + appDelPtr = new Pointer(appDelObj); + send(appDelPtr, "init"); + + send(appDelPtr, + "performSelectorOnMainThread:withObject:waitUntilDone:", + doMainSelector, null, 1 + ); + } else { + execute(new Runnable() { + @Override + public void run() { + appDidStart.callback(appDelPtr); + } + }); + } + } + + @Override + public void execute(Runnable command) { + QUEUE.add(command); + if (Thread.currentThread() == dispatchThread && Fn.activePresenter() == presenter) { + try { + process(); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "Cannot process " + command, ex); + } + } else { + send(appDelPtr, + "performSelectorOnMainThread:withObject:waitUntilDone:", + doMainSelector, null, 0 + ); + } + } + + private void process() throws Exception { + Closeable c = Fn.activate(presenter); + try { + for (;;) { + Runnable r = QUEUE.poll(); + if (r == null) { + break; + } + r.run(); + } + } finally { + c.close(); + } + } + + public interface ObjC extends Library { + + public static ObjC INSTANCE = (ObjC) Native.loadLibrary("objc.A", ObjC.class); + + public boolean class_addMethod(Pointer cls, Pointer name, Callback imp, String types); + + public String class_getName(Pointer cls); + + public String object_getClassName(Pointer cls); + + public Pointer class_copyMethodList(Class cls, IntByReference outCount); + + public Pointer objc_allocateClassPair(Pointer cls, String name, int additionalBytes); + + public Pointer objc_getClass(String name); + + public long objc_msgSend(Pointer theReceiver, Pointer theSelector, Object... arguments); + + public Rct objc_msgSend_stret(Pointer theReceiver, Pointer theSelector, Object... arguments); + + public void objc_registerClassPair(Pointer cls); + + public Pointer sel_getUid(String name); + } + + static long send(Pointer obj, String selector, Object... args) { + Pointer uid = ObjC.INSTANCE.sel_getUid(selector); + return ObjC.INSTANCE.objc_msgSend(obj, uid, args); + } + + public static interface WebKit extends Library { + } + + public void callback(Pointer self) throws Exception { + if (NSApp != null) { + process(); + return; + } + + ObjC objC = ObjC.INSTANCE; + long res = send(objC.objc_getClass("NSApplication"), "sharedApplication"); + if (res == 0) { + System.err.print("Failed to initialized NSApplication... terminating...\n"); + System.exit(1); + } + dispatchThread = Thread.currentThread(); + NSApp = new Pointer(res); + send(NSApp, "setActivationPolicy:", 0); + send(NSApp, "setDelegate:", self); + res = send(NSApp, "run"); + System.err.println("end res: " + res); + } + + public final class AppDidStart implements Callback { + AppDidStart() { + } + + public long callback(Pointer self) { + ObjC objC = ObjC.INSTANCE; + mainWindow = new Pointer(send(objC.objc_getClass("NSWindow"), "alloc")); + + Pointer screen = new Pointer(send(objC.objc_getClass("NSScreen"), "mainScreen")); + + Pointer uid = ObjC.INSTANCE.sel_getUid("frame"); + Rct size = ObjC.INSTANCE.objc_msgSend_stret(screen, uid); + + double height = size.height.doubleValue() * 0.9; + double width = size.width.doubleValue() * 0.9; + double x = size.width.doubleValue() * 0.05 + size.x.doubleValue(); + double y = size.height.doubleValue() * 0.05 + size.y.doubleValue(); + + Rct r = new Rct(x, y, width, height); + + int mode = 15; + int backingstoreBuffered = 2; + + send(mainWindow, + "initWithContentRect:styleMask:backing:defer:", + r, mode, backingstoreBuffered, false + ); + send(mainWindow, "setTitle:", nsString("Browser demo")); + Pointer webViewClass = objC.objc_getClass("WebView"); + long webViewId = send(webViewClass, "alloc"); + webView = new Pointer(webViewId); + send(webView, "init"); + + send(webView, "setFrameLoadDelegate:", self); + send(webView, "setUIDelegate:", self); + + Pointer frame = new Pointer(send(webView, "mainFrame")); + + Pointer urlClass = objC.objc_getClass("NSURL"); + Pointer url = new Pointer(send(urlClass, "URLWithString:", nsString(page))); + Pointer requestClass = objC.objc_getClass("NSURLRequest"); + Pointer request = new Pointer(send(requestClass, "alloc")); + send(request, "initWithURL:", url); + + send(mainWindow, "setContentView:", webView); + send(frame, "loadRequest:", request); + + send(mainWindow, "becomeFirstResponder"); + send(mainWindow, "makeKeyAndOrderFront:", NSApp); + return 1; + } + } + + static Pointer nsString(String bd) { + ObjC objC = ObjC.INSTANCE; + Pointer stringClass = objC.objc_getClass("NSString"); + Pointer browserDemo = new Pointer(send(stringClass, "stringWithCString:encoding:", bd, 4)); + return browserDemo; + } + + public final class ContextCreated implements Callback { + ContextCreated() { + } + + public void callback(Pointer webView, Pointer ctx, Pointer frame) { + frame = new Pointer(send(frame, "mainFrame")); + ctx = new Pointer(send(frame, "globalContext")); + + jsContext = ctx; + if (onContext != null) { + onContext.run(); + } + } + } + + public final class Ready implements Callback { + Ready() { + } + + public void callback(Pointer p1, Pointer frame) { + send(webView, "stringByEvaluatingJavaScriptFromString:", nsString("1 + 1")); + if (onPageLoad != null) { + onPageLoad.run(); + } + } + } + + public final class DialogHandler implements Callback { + private final int type; + + public DialogHandler(int type) { + this.type = type; + } + + public boolean alertOrConfirm(Pointer appDelegate, Pointer selector, Pointer webView, Pointer msg, Pointer frame) { + ObjC objC = ObjC.INSTANCE; +/* + System.err.println("webView: " + objC.object_getClassName(webView)); + System.err.println("frame: " + objC.object_getClassName(frame)); + System.err.println("msg: " + objC.object_getClassName(msg)); + + String text = new Pointer(send(msg, "UTF8String")).getString(0, "UTF-8"); + System.err.println("msg: " + text); +*/ + Pointer alert = new Pointer(send(objC.objc_getClass("NSAlert"), "alloc")); + send(alert, "init"); + send(alert, "setMessageText:", msg); + send(alert, "addButtonWithTitle:", nsString("OK")); + if (type == 1) { + send(alert, "addButtonWithTitle:", nsString("Cancel")); + } + + int res = ((int) send(alert, "runModal")) & 1; + + return res == 0; + } + } + + public final class UIDelegate implements Callback { + UIDelegate() { + } + + public Pointer callback(Pointer appDelegate) { + ObjC objC = ObjC.INSTANCE; + + Pointer uid = ObjC.INSTANCE.sel_getUid("frame"); + Rct size = ObjC.INSTANCE.objc_msgSend_stret(mainWindow, uid); + + double height = size.height.doubleValue() * 0.9; + double width = size.width.doubleValue() * 0.9; + double x = size.width.doubleValue() * 0.05 + size.x.doubleValue(); + double y = size.height.doubleValue() * 0.05 + size.y.doubleValue(); + + Pointer window = new Pointer(send(objC.objc_getClass("NSWindow"), "alloc")); + + Rct r = new Rct(x, y, width, height); + int mode = 15; + int backingstoreBuffered = 2; + + send(window, + "initWithContentRect:styleMask:backing:defer:", + r, mode, backingstoreBuffered, false + ); + send(window, "setTitle:", nsString("Browser demo")); + Pointer webViewClass = objC.objc_getClass("WebView"); + long webViewId = send(webViewClass, "alloc"); + Pointer webView = new Pointer(webViewId); + send(webView, "init"); + + send(window, "setContentView:", webView); + send(window, "makeKeyAndOrderFront:", (Object) null); + return webView; + } + } + + public static final class Rct extends Structure implements Structure.ByValue { + public Flt x; + public Flt y; + public Flt width; + public Flt height; + + public Rct() { + } + + public Rct(double x, double y, double width, double height) { + this.x = new Flt(x); + this.y = new Flt(y); + this.width = new Flt(width); + this.height = new Flt(height); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList("x", "y", "width", "height"); + } + } + + public static final class Flt extends Number implements NativeMapped { + + private static final boolean SMALL = Native.LONG_SIZE == 4; + private final double number; + + public Flt() { + this(0); + } + + public Flt(double d) { + number = d; + } + + @Override + public float floatValue() { + return (float) number; + } + + @Override + public double doubleValue() { + return number; + } + + @Override + public int intValue() { + return (int) number; + } + + @Override + public long longValue() { + return (long) number; + } + + @Override + public Object fromNative(Object o, FromNativeContext fromNativeContext) { + return new Flt(((Number) o).doubleValue()); + } + + @Override + public Object toNative() { + return SMALL ? floatValue() : number; + } + + @Override + public Class nativeType() { + return SMALL ? Float.class : Double.class; + } + + @Override + public String toString() { + return Double.toString(number); + } + } + + private static void ensureHttpAccess(URI page) { + if (!"http".equals(page.getScheme())) { + return; + } + ObjC objC = ObjC.INSTANCE; + + final Pointer nsBundle = objC.objc_getClass("NSBundle"); + final Pointer nsNumber = objC.objc_getClass("NSNumber"); + final Pointer nsDictionary = objC.objc_getClass("NSMutableDictionary"); + + + final Pointer mainBundle = new Pointer(send(nsBundle, "mainBundle")); + final Pointer info = new Pointer(send(mainBundle, "infoDictionary")); + + final Pointer nsAppTransportSecurity = nsString("NSAppTransportSecurity"); + final Pointer nsAllowArbitraryLoads = nsString("NSAllowsArbitraryLoads"); + final long nsTrue = send(nsNumber, "numberWithBool:", 1); + + final long rawDict = send(info, "objectForKey:", nsAppTransportSecurity); + final Pointer dict; + if (rawDict == 0) { + dict = new Pointer(send(nsDictionary, "dictionaryWithCapacity:", 1)); + send(info, "setValue:forKey:", dict, nsAppTransportSecurity); + send(dict, "setObject:forKey:", nsTrue, nsAllowArbitraryLoads); + } + } +} diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java b/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java new file mode 100644 index 00000000..6d10093a --- /dev/null +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java @@ -0,0 +1,450 @@ +package com.dukescript.presenters.renderer; + +/* + * #%L + * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.sun.jna.Callback; +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import org.netbeans.html.boot.spi.Fn; + +final class GTK extends Show implements InvokeLater { + private final Fn.Presenter presenter; + private final Runnable onPageLoad; + private final Runnable onContext; + private final boolean headless; + + private OnDestroy onDestroy; + private OnLoad onLoad; + private Pending pending; + private String page; + private Pointer jsContext; + private NewWebView newWebView; + + GTK() { + this(null, null, null, false); + } + + GTK(Fn.Presenter p, Runnable onPageLoad, Runnable onContext, boolean hl) { + this.onPageLoad = onPageLoad; + this.presenter = p; + this.headless = hl; + this.onContext = onContext; + } + + @Override + public JSC jsc() { + return INSTANCE.jsc; + } + + @Override + public Pointer jsContext() { + return jsContext; + } + + public interface GLib extends Library { + void g_idle_add(Callback r, Pointer p); + } + public interface G extends Library { + void g_signal_connect_data(Pointer obj, String signal, Callback callback, Pointer data, Void nul, int flags); + } + + public interface Gtk extends Library { + void gtk_init(int cnt, String[] args); + + Pointer gtk_window_new(int windowType); + Pointer gtk_scrolled_window_new(Pointer ignore, Pointer ignore2); + void gtk_window_get_position(Pointer window, IntByReference x, IntByReference y); + void gtk_window_get_size(Pointer window, IntByReference width, IntByReference height); + void gtk_window_set_default_size(Pointer window, int width, int height); + void gtk_window_set_title(Pointer window, String title); + void gtk_widget_show_all(Pointer window); + void gtk_window_set_gravity(Pointer window, int gravity); + void gtk_window_move(Pointer window, int x, int y); + void gtk_container_add(Pointer container, Pointer child); + void gtk_widget_grab_focus(Pointer window); + void gtk_widget_destroy(Pointer window); + void gtk_window_present(Pointer window); + void gtk_main(); + void gtk_main_quit(); + + void gtk_widget_set_size_request(Pointer window, int width, int height); + void gtk_window_set_position(Pointer window, int type); + Pointer gtk_button_new_with_label(String label); + void gtk_container_set_border_width(Pointer window, int size); + } + + public interface Gdk extends Library { + int gdk_screen_get_primary_monitor(Pointer screen); + Pointer gdk_screen_get_default(); + void gdk_screen_get_monitor_geometry(Pointer screen, int monitor, GRectangle geometry); + } + + public static class GRectangle extends Structure { + public int x, y; + public int width, height; + + @Override + protected List getFieldOrder() { + return Arrays.asList("x", "y", "width", "height"); + } + } + + public interface WebKit extends Library { + Pointer webkit_web_view_new(); + void webkit_web_view_load_uri(Pointer webView, String url); + Pointer webkit_web_page_frame_get_javascript_context_for_script_world(Pointer webFrame, Pointer world); + int webkit_web_view_get_load_status(Pointer webView); + Pointer webkit_web_view_get_main_frame(Pointer webView); + Pointer webkit_web_frame_get_global_context(Pointer webFrame); + String webkit_web_frame_get_title(Pointer webFrame); + } + + private static Libs INSTANCE; + + final Libs getInstance(boolean[] initialized) { + synchronized (GTK.class) { + if (INSTANCE == null) { + INSTANCE = new Libs(); + initialized[0] = true; + } + } + return INSTANCE; + } + + @Override + public void show(URI url) throws IOException { + this.page = url.toASCIIString(); + boolean[] justInitialized = {false}; + Gtk gtk; + try { + gtk = getInstance(justInitialized).gtk; + } catch (LinkageError e) { + throw new IOException(e); + } + if (justInitialized[0]) { + gtk.gtk_init(0, null); + run(); + gtk.gtk_main(); + } else { + INSTANCE.glib.g_idle_add(this, null); + } + } + + @Override + public void run() { + final Libs libs = getInstance(null); + final Gdk gdk = libs.gdk; + final Gtk gtk = libs.gtk; + final WebKit webKit = libs.webKit; + final G g = libs.g; + + final Pointer screen = gdk.gdk_screen_get_default(); + int primaryMonitor = gdk.gdk_screen_get_primary_monitor(screen); + GRectangle size = new GRectangle(); + gdk.gdk_screen_get_monitor_geometry(screen, primaryMonitor, size); + int height = (int) (size.height * 0.9); + int width = (int) (size.width * 0.9); + int x = (int) (size.width * 0.05) + size.x; + int y = (int) (size.height * 0.05) + size.y; + + final Pointer window = gtk.gtk_window_new(0); + gtk.gtk_window_set_default_size(window, width, height); + gtk.gtk_window_set_gravity(window, 5); + gtk.gtk_window_move(window, x, y); + + Pointer scroll = gtk.gtk_scrolled_window_new(null, null); + gtk.gtk_container_add(window, scroll); + + final Pointer webView = webKit.webkit_web_view_new(); + gtk.gtk_container_add(scroll, webView); + Pointer frame = webKit.webkit_web_view_get_main_frame(webView); + Pointer ctx = webKit.webkit_web_frame_get_global_context(frame); + this.jsContext = ctx; + if (onContext != null) { + onContext.run(); + } + onLoad = new OnLoad(webView, libs, window, onPageLoad); + g.g_signal_connect_data(webView, "notify::load-status", onLoad, null, null, 0); + + newWebView = new NewWebView(libs, headless); + g.g_signal_connect_data(webView, "create-web-view", newWebView, window, null, 0); + + webKit.webkit_web_view_load_uri(webView, page); + + gtk.gtk_widget_grab_focus(webView); + + onDestroy = new OnDestroy(); + g.g_signal_connect_data(window, "destroy", onDestroy, null, null, 0); + pending = new Pending(); + if (!headless) { + gtk.gtk_widget_show_all(window); + } + } + + private static class NewWebView implements Callback { + private final Libs libs; + private final boolean headless; + private final Collection onLoads = new HashSet(); + + NewWebView(Libs libs, boolean headless) { + this.libs = libs; + this.headless = headless; + } + + public Pointer createWebView(Pointer orig, Pointer frame, Pointer origWindow) { + IntByReference x = new IntByReference(0); + IntByReference y = new IntByReference(0); + IntByReference width = new IntByReference(0); + IntByReference height = new IntByReference(0); + + Gtk gtk = libs.gtk; + + gtk.gtk_window_get_position(origWindow, x, y); + gtk.gtk_window_get_size(origWindow, width, height); + + int tenthWidth = width.getValue() / 10; + int tenthHeight = height.getValue() / 10; + + final Pointer window = gtk.gtk_window_new(0); + gtk.gtk_window_set_default_size(window, width.getValue() - 2 * tenthWidth, height.getValue() - 2 * tenthHeight); + gtk.gtk_window_set_gravity(window, 5); + gtk.gtk_window_move(window, x.getValue() + tenthWidth, y.getValue() + tenthHeight); + + Pointer scroll = gtk.gtk_scrolled_window_new(null, null); + gtk.gtk_container_add(window, scroll); + + final Pointer webView = libs.webKit.webkit_web_view_new(); + gtk.gtk_container_add(scroll, webView); + + gtk.gtk_widget_grab_focus(webView); + + OnLoad onLoad = new OnLoad(webView, libs, window, null); + onLoads.add(onLoad); + libs.g.g_signal_connect_data(webView, "notify::load-status", onLoad, null, null, 0); + + if (!headless) { + gtk.gtk_widget_show_all(window); + gtk.gtk_window_present(window); + } + + return webView; + } + } + + private static class OnLoad implements Callback { + private final Libs libs; + private final Pointer webView; + private final Pointer window; + private final Runnable onPageLoad; + private Title title; + + public OnLoad(Pointer webView, Libs libs, Pointer window, Runnable onPageLoad) { + this.webView = webView; + this.window = window; + this.libs = libs; + this.onPageLoad = onPageLoad; + } + + public void loadStatus() { + int status = libs.webKit.webkit_web_view_get_load_status(webView); + if (status == 2) { + final Pointer frame = libs.webKit.webkit_web_view_get_main_frame(webView); + if (title == null) { + title = new Title(frame); + title.updateTitle(); + libs.g.g_signal_connect_data(frame, "notify::title", title, null, null, 0); + } + if (onPageLoad != null) { + onPageLoad.run(); + } + } + } + + private class Title implements Callback { + private final Pointer frame; + + public Title(Pointer frame) { + this.frame = frame; + } + + public void updateTitle() { + String title = libs.webKit.webkit_web_frame_get_title(frame); + if (title == null) { + title = "DukeScript Application"; + } + libs.gtk.gtk_window_set_title(window, title); + } + } + } + + private static class OnDestroy implements Callback { + public void signal() { + System.exit(0); + } + } + + @Override + public void execute(Runnable command) { + pending.queue.add(command); + if (Fn.activePresenter() == presenter) { + try { + pending.process(); + } catch (Exception ex) { + LOG.log(Level.SEVERE, "Cannot process " + command, ex); + } + } else { + GLib glib = getInstance(null).glib; + glib.g_idle_add(pending, null); + } + } + + + + private class Pending implements Callback { + final Queue queue = new ConcurrentLinkedQueue(); + + public void process() throws Exception { + Closeable c = Fn.activate(presenter); + try { + for (;;) { + Runnable r = queue.poll(); + if (r == null) { + break; + } + r.run(); + } + } finally { + c.close(); + } + } + } + + static final class Libs { + final Gtk gtk; + final JSC jsc; + final G g; + final GLib glib; + final Gdk gdk; + final WebKit webKit; + + Libs() { + List errors = new ArrayList(); + this.gtk = Libs.loadLibrary(Gtk.class, false, null); + this.jsc = Libs.loadLibrary(JSC.class, true, errors); + this.g = Libs.loadLibrary(G.class, false, errors); + this.glib = Libs.loadLibrary(GLib.class, false, errors); + this.gdk = Libs.loadLibrary(Gdk.class, false, errors); + this.webKit = Libs.loadLibrary(WebKit.class, false, errors); + + if (!errors.isEmpty()) { + throw linkageError(errors); + } + } + + static T loadLibrary(Class type, boolean allowObjects, Collection errors) { + String libName = System.getProperty("com.dukescript.presenters.renderer." + type.getSimpleName()); + if (libName == null) { + if (type == JSC.class) { + libName = "javascriptcoregtk-3.0"; + } else if (type == GTK.GLib.class) { + libName = "glib-2.0"; + } else if (type == GTK.G.class) { + libName = "gobject-2.0"; + } else if (type == GTK.Gdk.class) { + libName = "gtk-3"; + } else if (type == GTK.Gtk.class) { + libName = "gtk-3"; + } else if (type == GTK.WebKit.class) { + libName = "webkitgtk-3.0"; + } + } + try { + Object lib = Native.loadLibrary(libName, type, Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, allowObjects)); + return type.cast(lib); + } catch (LinkageError err) { + if (errors != null) { + errors.add(err); + return null; + } else { + throw err; + } + } + } + + private LinkageError linkageError(List errors) { + StringWriter sw = new StringWriter(); + String libraryPath = System.getProperty("java.library.path"); + sw.append("Java Library Path:"); + if (libraryPath != null) { + for (String pathElement : libraryPath.split(File.pathSeparator)) { + sw.append("\n Path ").append(pathElement); + File pathFile = new File(pathElement); + String[] libraries = pathFile.list(); + if (libraries != null) { + for (String lib : libraries) { + sw.append("\n ").append(lib); + } + } + } + sw.append("\n"); + } + PrintWriter pw = new PrintWriter(sw); + for (Throwable t : errors) { + t.printStackTrace(pw); + } + sw.append("\nStatus:"); + sw.append("\n jsc: " + jsc); + sw.append("\n g: " + g); + sw.append("\n glib: " + glib); + sw.append("\n gdk: " + gdk); + sw.append("\n webKit: " + webKit); + return new LinkageError(sw.toString()); + } + } +} + +interface InvokeLater extends Callback { + public void run(); +} diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java b/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java new file mode 100644 index 00000000..b7756e3d --- /dev/null +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java @@ -0,0 +1,293 @@ +package com.dukescript.presenters.renderer; + +/* + * #%L + * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.sun.jna.Callback; +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.PointerByReference; +import java.util.Arrays; +import java.util.List; + +/** Interface to JavaScriptCore native library. + */ +public interface JSC extends Library { + /** Data to create new JSC class definition. + */ + public static final class JSClassDefinition extends Structure { + + public int version; /* current (and only) version is 0 */ + public int attributes; + public String className = "javaClazz"; + public Pointer parentClass; + public Pointer staticValues; + public Pointer staticFunctions; + public Pointer initialize; + public Callback finalize; + public Pointer hasProperty; + public Pointer getProperty; + public Pointer setProperty; + public Pointer deleteProperty; + public Pointer getPropertyNames; + public Pointer callAsFunction; + public Pointer callAsConstructor; + public Pointer hasInstance; + public Pointer convertToType; + + public JSClassDefinition(Callback finalize) { + this.finalize = finalize; + } + + @Override + protected List getFieldOrder() { + return Arrays.asList( + "version", "attributes", "className", "parentClass", + "staticValues", "staticFunctions", "initialize", + "finalize", "hasProperty", "getProperty", "setProperty", + "deleteProperty", "getPropertyNames", "callAsFunction", + "callAsConstructor", "hasInstance", "convertToType" + ); + } + } + + Pointer JSClassCreate(JSClassDefinition def); + + /*! + @function + @abstract Tests whether a JavaScript value is an object with a given class in its class chain. + @param ctx The execution context to use. + @param value The JSValue to test. + @param jsClass The JSClass to test against. + @result true if value is an object and has jsClass in its class chain, otherwise false. + */ + int JSValueIsObjectOfClass(Pointer ctx, Pointer value, Pointer jsClass); + + Pointer JSContextGetGlobalObject(Pointer ctx); + + Pointer JSStringCreateWithUTF8CString(String s); + + Pointer JSStringRetain(Pointer string); + + Pointer JSStringRelease(Pointer string); + + int JSStringGetMaximumUTF8CStringSize(Pointer string); + + int JSStringGetUTF8CString(Pointer string, Memory mem, int bufferSize); + + boolean JSStringIsEqual(Pointer a, Pointer b); + + boolean JSStringIsEqualToUTF8CString(Pointer a, String b); + + /*! + @function JSEvaluateScript + @abstract Evaluates a string of JavaScript. + @param ctx The execution context to use. + @param script A JSString containing the script to evaluate. + @param thisObject The object to use as "this," or NULL to use the global object as "this." + @param sourceURL A JSString containing a URL for the script's source file. This is only used when reporting exceptions. Pass NULL if you do not care to include source file information in exceptions. + @param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The JSValue that results from evaluating script, or NULL if an exception is thrown. + */ + Pointer JSEvaluateScript(Pointer ctx, Pointer script, Pointer thisObject, Pointer sourceURL, int startingLineNumber, PointerByReference exception); + + /*! + @function + @abstract Sets a property on an object. + @param ctx The execution context to use. + @param object The JSObject whose property you want to set. + @param propertyName A JSString containing the property's name. + @param value A JSValue to use as the property's value. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @param attributes A logically ORed set of JSPropertyAttributes to give to the property. + */ + void JSObjectSetProperty(Pointer ctx, Pointer object, Pointer propertyName, Pointer value, int attributes, PointerByReference exception); + + /*! + @function + @abstract Gets a property from an object. + @param ctx The execution context to use. + @param object The JSObject whose property you want to get. + @param propertyName A JSString containing the property's name. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The property's value if object has the property, otherwise the undefined value. + */ + Pointer JSObjectGetProperty(Pointer ctx, Pointer object, Pointer propertyName, PointerByReference exception); + + /*! + @function + @abstract Gets a property from an object by numeric index. + @param ctx The execution context to use. + @param object The JSObject whose property you want to get. + @param propertyIndex An integer value that is the property's name. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The property's value if object has the property, otherwise the undefined value. + @discussion Calling JSObjectGetPropertyAtIndex is equivalent to calling JSObjectGetProperty with a string containing propertyIndex, but JSObjectGetPropertyAtIndex provides optimized access to numeric properties. + */ + Pointer JSObjectGetPropertyAtIndex(Pointer ctx, Pointer object, int propertyIndex, PointerByReference exception); + + /*! + @function + @abstract Convenience method for creating a JavaScript function with a given callback as its implementation. + @param ctx The execution context to use. + @param name A JSString containing the function's name. This will be used when converting the function to string. Pass NULL to create an anonymous function. + @param callAsFunction The JSObjectCallAsFunctionCallback to invoke when the function is called. + @result A JSObject that is a function. The object's prototype will be the default function prototype. + */ + Pointer JSObjectMakeFunctionWithCallback(Pointer ctx, Pointer name, Callback callAsFunction); + + /*! + @function + @abstract Creates a function with a given script as its body. + @param ctx The execution context to use. + @param name A JSString containing the function's name. This will be used when converting the function to string. Pass NULL to create an anonymous function. + @param parameterCount An integer count of the number of parameter names in parameterNames. + @param parameterNames A JSString array containing the names of the function's parameters. Pass NULL if parameterCount is 0. + @param body A JSString containing the script to use as the function's body. + @param sourceURL A JSString containing a URL for the script's source file. This is only used when reporting exceptions. Pass NULL if you do not care to include source file information in exceptions. + @param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions. The value is one-based, so the first line is line 1 and invalid values are clamped to 1. + @param exception A pointer to a JSValueRef in which to store a syntax error exception, if any. Pass NULL if you do not care to store a syntax error exception. + @result A JSObject that is a function, or NULL if either body or parameterNames contains a syntax error. The object's prototype will be the default function prototype. + @discussion Use this method when you want to execute a script repeatedly, to avoid the cost of re-parsing the script before each execution. + */ + Pointer JSObjectMakeFunction(Pointer ctx, Pointer name, int parameterCount, Pointer[] parameterNames, Pointer body, Pointer sourceURL, int startingLineNumber, PointerByReference exception); + + /*! + @function + @abstract Calls an object as a function. + @param ctx The execution context to use. + @param object The JSObject to call as a function. + @param thisObject The object to use as "this," or NULL to use the global object as "this." + @param argumentCount An integer count of the number of arguments in arguments. + @param arguments A JSValue array of arguments to pass to the function. Pass NULL if argumentCount is 0. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The JSValue that results from calling object as a function, or NULL if an exception is thrown or object is not a function. + */ + Pointer JSObjectCallAsFunction(Pointer ctx, Pointer object, Pointer thisObject, int argumentCount, Pointer[] arguments, PointerByReference exception); + + /*! + @function + @abstract Creates a JavaScript value of the null type. + @param ctx The execution context to use. + @result The unique null value. + */ + Pointer JSValueMakeNull(Pointer ctx); + + /*! + @function + @abstract Creates a JavaScript value of the boolean type. + @param ctx The execution context to use. + @param boolean The bool to assign to the newly created JSValue. + @result A JSValue of the boolean type, representing the value of boolean. + */ + Pointer JSValueMakeBoolean(Pointer ctx, boolean val); + + /*! + @function + @abstract Creates a JavaScript value of the number type. + @param ctx The execution context to use. + @param number The double to assign to the newly created JSValue. + @result A JSValue of the number type, representing the value of number. + */ + Pointer JSValueMakeNumber(Pointer ctx, double number); + + /*! + @function + @abstract Creates a JavaScript value of the string type. + @param ctx The execution context to use. + @param string The JSString to assign to the newly created JSValue. The + newly created JSValue retains string, and releases it upon garbage collection. + @result A JSValue of the string type, representing the value of string. + */ + Pointer JSValueMakeString(Pointer ctx, /*JSStringRef*/ Pointer string); + + /*! + @function + @abstract Creates a JavaScript Array object. + @param ctx The execution context to use. + @param argumentCount An integer count of the number of arguments in arguments. + @param arguments A JSValue array of data to populate the Array with. Pass NULL if argumentCount is 0. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSObject that is an Array. + @discussion The behavior of this function does not exactly match the behavior of the built-in Array constructor. Specifically, if one argument + is supplied, this function returns an array with one element. + */ + Pointer JSObjectMakeArray(Pointer ctx, int argumentCount, Pointer[] data, PointerByReference exception); + + /*! + @function + @abstract Creates a JavaScript object. + @param ctx The execution context to use. + @param jsClass The JSClass to assign to the object. Pass NULL to use the default object class. + @param data A void* to set as the object's private data. Pass NULL to specify no private data. + @result A JSObject with the given class and private data. + @discussion The default object class does not allocate storage for private data, so you must provide a non-NULL jsClass to JSObjectMake if you want your object to be able to store private data. + data is set on the created object before the intialize methods in its class chain are called. This enables the initialize methods to retrieve and manipulate data through JSObjectGetPrivate. + */ + Pointer JSObjectMake(Pointer ctx, Pointer jsClass, Object data); + + Object JSObjectGetPrivate(Pointer object); + + int JSValueGetType(Pointer ctx, Pointer value); + + /*! + @function + @abstract Converts a JavaScript value to number and returns the resulting number. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The numeric result of conversion, or NaN if an exception is thrown. + */ + double JSValueToNumber(Pointer ctx, Pointer value, PointerByReference exception); + + /*! + @function + @abstract Converts a JavaScript value to string and copies the result into a JavaScript string. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result A JSString with the result of conversion, or NULL if an exception is thrown. Ownership follows the Create Rule. + */ + Pointer JSValueToStringCopy(Pointer ctx, Pointer value, PointerByReference exception); + + /*! + @function + @abstract Converts a JavaScript value to object and returns the resulting object. + @param ctx The execution context to use. + @param value The JSValue to convert. + @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception. + @result The JSObject result of conversion, or NULL if an exception is thrown. + */ + Pointer JSValueToObject(Pointer ctx, Pointer value, PointerByReference exception); + + void JSValueProtect(Pointer ctx, Pointer value); + + void JSValueUnprotect(Pointer ctx, Pointer value); + +} diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java b/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java new file mode 100644 index 00000000..1ab9cb76 --- /dev/null +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java @@ -0,0 +1,116 @@ +package com.dukescript.presenters.renderer; + +/* + * #%L + * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.sun.jna.Pointer; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.html.boot.spi.Fn; + +/** Support for displaying browser. + */ +public abstract class Show { + static final Logger LOG = Logger.getLogger(Show.class.getName()); + + Show() { + } + + /** Shows a page in a browser. + * + * @param impl the name of implementation to use, can be null + * @param page the page URL + * @throws IOException if something goes wrong + */ + public static void show(String impl, URI page) throws IOException { + try { + Class c = Class.forName("com.dukescript.presenters.renderer." + impl); + Show show = (Show) c.newInstance(); + show.show(page); + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + if (impl == null) { + impl = "xdg-open"; + } + LOG.log(Level.INFO, "Trying command line execution of {0}", impl); + String[] cmdArr = { + impl, page.toString() + }; + LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); + final Process process = Runtime.getRuntime().exec(cmdArr); + try { + process.waitFor(); + } catch (InterruptedException ex1) { + throw (InterruptedIOException) new InterruptedIOException().initCause(ex1); + } + } + } + + /** Initializes native browser window. + * + * @param presenter the presenter that will be using the returned value + * @param onPageLoad callback when page finishes loading + * @param onContext callback when {@link #jsContext()} becomes available + * @param headless should the window appear on the monitor or not? + * useful for testing + * @return object to query and control the browser window + */ + public static Show open(Fn.Presenter presenter, Runnable onPageLoad, Runnable onContext, boolean headless) { + boolean isMac = System.getProperty("os.name").contains("Mac"); + return isMac ? + new Cocoa(presenter, onPageLoad, onContext, headless) : + new GTK(presenter, onPageLoad, onContext, headless); + } + + /** Loads a page into the browser window. + * + * @param page the URL to load + * @throws IOException if something goes wrong + */ + public abstract void show(URI page) throws IOException; + + /** Access to JavaScriptCore API of the browser window. + * @return JavaScriptCore instance or null if not supported + * for this browser + */ + public abstract JSC jsc(); + + /** Access to JavaScriptCore context. + * @return the context or null if not supported or not + * yet available + */ + public abstract Pointer jsContext(); + + /** Executes a runnable on "UI thread". + * @param command runnable to execute + */ + public abstract void execute(Runnable command); +} diff --git a/webkit/pom.xml b/webkit/pom.xml new file mode 100644 index 00000000..66355b78 --- /dev/null +++ b/webkit/pom.xml @@ -0,0 +1,155 @@ + + + 4.0.0 + + com.dukescript.presenters + pom + 2.0-SNAPSHOT + + webkit + WebKit Presenter + jar + + none + ${project.parent.basedir} + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + ${project.version} + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-javadoc-plugin + + false + com.dukescript.presenters + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + always + + + + org.netbeans.html + html4j-maven-plugin + ${net.java.html.version} + + + classes + + process-js-annotations + + + + test-classes + process-test-classes + + process-js-annotations + + + ${project.build.directory}/test-classes + + + + + + + + + org.testng + testng + ${testng.version} + test + + + net.java.dev.jna + jna + ${jna.version} + + + org.netbeans.html + net.java.html.boot + ${net.java.html.version} + jar + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + provided + + + org.netbeans.html + net.java.html.json.tck + ${net.java.html.version} + test + jar + + + + + org.glassfish.grizzly + grizzly-http-server + ${grizzly.version} + test + + + org.glassfish.grizzly + grizzly-websockets-server + ${grizzly.version} + test + jar + + + org.glassfish.grizzly + grizzly-http-servlet + ${grizzly.version} + test + + + javax.servlet + javax.servlet-api + 3.1.0 + test + + + org.netbeans.html + ko4j + ${net.java.html.version} + test + jar + + + com.dukescript.api + strings + ${project.version} + provided + + + ${project.groupId} + renderer + ${project.version} + + + \ No newline at end of file diff --git a/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java b/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java new file mode 100644 index 00000000..febcdd5e --- /dev/null +++ b/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java @@ -0,0 +1,74 @@ +package com.dukescript.presenters.webkit; + +/* + * #%L + * WebKit Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URL; +import java.nio.file.Files; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + + +final class UnJarResources { + static URL extract(URL url) throws IOException { + if (!"jar".equals(url.getProtocol())) { + return url; + } + JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); + JarFile jar = jarConnection.getJarFile(); + if (jar == null) { + return url; + } + File dir = File.createTempFile(jar.getName(), ".dir"); + dir.delete(); + dir.mkdirs(); + + Enumeration en = jar.entries(); + while (en.hasMoreElements()) { + JarEntry entry = en.nextElement(); + final String entryName = entry.getName(); + if (entryName.endsWith(".class") || entryName.endsWith("/")) { + continue; + } + File file = new File(dir, entryName.replace('/', File.separatorChar)); + file.getParentFile().mkdirs(); + try (InputStream is = jar.getInputStream(entry)) { + Files.copy(is, file.toPath()); + } + } + + File file = new File(dir, jarConnection.getEntryName().replace('/', File.separatorChar)); + if (file.exists()) { + return file.toURI().toURL(); + } else { + return url; + } + } +} diff --git a/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java b/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java new file mode 100644 index 00000000..d7924a43 --- /dev/null +++ b/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java @@ -0,0 +1,536 @@ +package com.dukescript.presenters.webkit; + +/* + * #%L + * WebKit Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import com.dukescript.presenters.renderer.JSC; +import com.dukescript.presenters.renderer.Show; +import com.sun.jna.Callback; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; +import java.io.Reader; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.html.boot.spi.Fn; +import org.openide.util.lookup.ServiceProvider; +import com.dukescript.api.strings.Texts; + +/** Displays using native WebKit component on Linux and Mac OS X. + * Requires necessary native libraries to be installed. Uses GTK3 on + * Linux which may not co-exist well with GTK2 (used by JDK's default AWT + * toolkit). + */ +@ServiceProvider(service = Fn.Presenter.class) +public final class WebKitPresenter implements Fn.Presenter, Fn.KeepAlive, Executor { + private static final Logger LOG = Logger.getLogger(WebKitPresenter.class.getName()); + private final Show shell; + private Runnable onPageLoad; + private OnFinalize onFinalize; + private Pointer ctx; + private Pointer javaClazz; + private final Map toJava = new HashMap<>(); + private Pointer arrayLength; + private Pointer valueTrue; + private Pointer valueFalse; + private String onPageApp; + + public WebKitPresenter() { + this(false); + } + + WebKitPresenter(boolean headless) { + shell = Show.open(this, new Runnable() { + @Override + public void run() { + onPageLoad.run(); + } + }, new Runnable() { + @Override + public void run() { + jsContext(shell.jsContext()); + } + }, headless); + } + + @Override + public Fn defineFn(String code, String... names) { + return defineFn(code, names, null); + } + @Override + public Fn defineFn(String code, String[] names, boolean[] keepAlive) { + JSC jsc = shell.jsc(); + Pointer[] jsNames = new Pointer[names.length]; + for (int i = 0; i < jsNames.length; i++) { + jsNames[i] = jsc.JSStringCreateWithUTF8CString(names[i]); + } + Pointer jsCode = jsc.JSStringCreateWithUTF8CString(code); + PointerByReference exc = new PointerByReference(); + Pointer fn = jsc.JSObjectMakeFunction(ctx, null, names.length, jsNames, jsCode, null, 1, exc); + if (fn == null) { + throw new IllegalStateException("Cannot initialize function: " + exc.getValue()); + } + + jsc.JSStringRelease(jsCode); + for (Pointer jsName : jsNames) { + jsc.JSStringRelease(jsName); + } + return new JSCFn(fn, keepAlive); + } + + @Override + public void displayPage(URL page, Runnable onPageLoad) { + this.onPageLoad = onPageLoad; + this.onPageApp = findCalleeClassName(); + try { + if ("jar".equals(page.getProtocol())) { + page = UnJarResources.extract(page); + } + + shell.show(page.toURI()); + } catch (Throwable t) { + LOG.log(Level.SEVERE, onPageApp, t); + } + } + + @Override + public void loadScript(Reader code) throws Exception { + StringBuilder sb = new StringBuilder(); + for (;;) { + int ch = code.read(); + if (ch == -1) { + break; + } + sb.append((char)ch); + } + Pointer script = shell.jsc().JSStringCreateWithUTF8CString(sb.toString()); + PointerByReference ex = new PointerByReference(); + shell.jsc().JSEvaluateScript(ctx, script, null, null, 1, ex); + shell.jsc().JSStringRelease(script); + if (ex.getValue() != null) { + throw new Exception(convertToString(shell.jsc(), ex.getValue())); + } + } + + Pointer[] convertFromJava(Object... args) throws Exception { + return convertFromJava(args, null); + } + Pointer[] convertFromJava(Object[] args, boolean[] keepAlive) throws Exception { + JSC jsc = shell.jsc(); + Pointer[] arr = new Pointer[args.length]; + for (int i = 0; i < arr.length; i++) { + Object v = args[i]; + if (v == null) { + v = jsc.JSValueMakeNull(ctx); + } else if (v instanceof Number) { + v = jsc.JSValueMakeNumber(ctx, ((Number)v).doubleValue()); + } else if (v instanceof Boolean) { + v = ((Boolean)v) ? valueTrue : valueFalse; + } else if (v instanceof String) { + Pointer str = jsc.JSStringCreateWithUTF8CString((String)v); + v = jsc.JSValueMakeString(ctx, str); + jsc.JSStringRelease(str); + } else if (v instanceof Enum) { + Pointer str = jsc.JSStringCreateWithUTF8CString(((Enum)v).name()); + v = jsc.JSValueMakeString(ctx, str); + jsc.JSStringRelease(str); + } else if (v instanceof Character) { + v = jsc.JSValueMakeNumber(ctx, (Character)v); + } else if (v instanceof JSObject) { + v = ((JSObject)v).value; + } else if (v instanceof int[]) { + int[] numbers = (int[])v; + Pointer[] content = new Pointer[numbers.length]; + for (int j = 0; j < content.length; j++) { + content[j] = jsc.JSValueMakeNumber(ctx, numbers[j]); + } + v = jsc.JSObjectMakeArray(ctx, content.length, content, null); + } else if (v instanceof double[]) { + double[] numbers = (double[])v; + Pointer[] content = new Pointer[numbers.length]; + for (int j = 0; j < content.length; j++) { + content[j] = jsc.JSValueMakeNumber(ctx, numbers[j]); + } + v = jsc.JSObjectMakeArray(ctx, content.length, content, null); + } else if (v instanceof Object[]) { + Pointer[] content = convertFromJava((Object[])v); + v = jsc.JSObjectMakeArray(ctx, content.length, content, null); + } else if (v.getClass().isArray()) { + int len = Array.getLength(v); + Object[] boxed = new Object[len]; + for (int j = 0; j < len; j++) { + boxed[j] = Array.get(v, j); + } + Pointer[] content = convertFromJava(boxed); + v = jsc.JSObjectMakeArray(ctx, content.length, content, null); + } else if (v.getClass().getSimpleName().equals("$JsCallbacks$")) { + Pointer vm = jsc.JSObjectMake(ctx, null, null); + for (Method method : v.getClass().getMethods()) { + if (method.getDeclaringClass() != v.getClass()) { + continue; + } + Pointer name = jsc.JSStringCreateWithUTF8CString(method.getName()); + FnCallback fnC = new FnCallback(v, method); + toJava.put(fnC, fnC); + Pointer fn = jsc.JSObjectMakeFunctionWithCallback(ctx, null, fnC); + jsc.JSObjectSetProperty(ctx, vm, name, fn, 0, null); + jsc.JSStringRelease(name); + } + v = vm; + } else { + Pointer p = jsc.JSObjectMake(ctx, javaClazz, null); + if (keepAlive == null || keepAlive[i]) { + toJava.put(p, v); + } else { + toJava.put(p, new WeakVal(v)); + } + protect(v, p); + v = p; + } + arr[i] = (Pointer) v; + } + return arr; + } + + final String convertToString(JSC jsc, Pointer value) { + int type = jsc.JSValueGetType(ctx, value); + if (type == 5) { + Pointer toStr = jsc.JSStringCreateWithUTF8CString("this.toString()"); + value = jsc.JSEvaluateScript(ctx, toStr, value, null, 0, null); + jsc.JSStringRelease(toStr); + } + Object ret = convertToJava(jsc, String.class, value); + return ret != null ? ret.toString() : ""; + } + + final Object convertToJava(JSC jsc, Class expectedType, Pointer value) throws IllegalStateException { + int type = jsc.JSValueGetType(ctx, value); + /* + typedef enum { + kJSTypeUndefined, + kJSTypeNull, + kJSTypeBoolean, + kJSTypeNumber, + kJSTypeString, + kJSTypeObject + } JSType; + */ + switch (type) { + case 0: + case 1: + return null; + case 2: { + double probability = jsc.JSValueToNumber(ctx, value, null); + if (expectedType == boolean.class) { + expectedType = Boolean.class; + } + return expectedType.cast(probability >= 0.5); + } + case 3: { + Double ret = jsc.JSValueToNumber(ctx, value, null); + if (expectedType.isInstance(ret) || expectedType == double.class) { + return ret; + } + if (expectedType == Integer.class || expectedType == int.class) { + return ret.intValue(); + } + if (expectedType == Float.class || expectedType == float.class) { + return ret.floatValue(); + } + if (expectedType == Long.class || expectedType == long.class) { + return ret.longValue(); + } + if (expectedType == Short.class || expectedType == short.class) { + return ret.shortValue(); + } + if (expectedType == Byte.class || expectedType == byte.class) { + return ret.byteValue(); + } + if (expectedType == Character.class || expectedType == char.class) { + return (char)ret.intValue(); + } + throw new ClassCastException("Cannot convert double to " + expectedType); + } + case 4: { + Pointer val = jsc.JSValueToStringCopy(ctx, value, null); + int max = jsc.JSStringGetMaximumUTF8CStringSize(val); + Memory mem = new Memory(max); + jsc.JSStringGetUTF8CString(val, mem, max); + return expectedType.cast(mem.getString(0)); + } + case 5: { + Object ret; + if (isJavaClazz(value)) { + ret = toJava.get(value); + if (ret instanceof WeakVal) { + ret = ((WeakVal)ret).get(); + } + } else { + PointerByReference ex = new PointerByReference(); + Pointer checkArray = jsc.JSObjectCallAsFunction(ctx, arrayLength, null, 1, new Pointer[] { value }, ex); + if (checkArray == null) { + throw new RuntimeException(convertToString(jsc, ex.getValue())); + } + int len = (int)jsc.JSValueToNumber(ctx, checkArray, null); + if (len >= 0) { + Object[] arr = new Object[len]; + for (int i = 0; i < len; i++) { + Pointer val = jsc.JSObjectGetPropertyAtIndex(ctx, value, i, null); + arr[i] = convertToJava(jsc, Object.class, val); + } + return arr; + } + ret = new JSObject(this, value); + } + return expectedType.cast(ret); + } + default: + throw new IllegalStateException("Uknown type: " + type); + } + } + + @Override + public void execute(Runnable command) { + shell.execute(command); + } + + final void jsContext(Pointer ctx) { + this.ctx = ctx; + + JSC jsc = shell.jsc(); + onFinalize = new WebKitPresenter.OnFinalize(); + javaClazz = jsc.JSClassCreate(new JSC.JSClassDefinition(onFinalize)); + + boolean testInstance = false; + assert testInstance = true; + if (testInstance) { + Pointer testObj = jsc.JSObjectMake(ctx, javaClazz, null); + assert isJavaClazz(testObj) : "Own classes has to be recognized"; + } + + { + Pointer jsGlobal = ctx; + Pointer arrArg = jsc.JSStringCreateWithUTF8CString("x"); + Pointer arrT = jsc.JSStringCreateWithUTF8CString("var res = x.constructor === Array ? x.length : -1; return res;"); + Pointer arrFn = jsc.JSObjectMakeFunction(jsGlobal, null, 1, new Pointer[]{arrArg}, arrT, null, 0, null); + arrayLength = arrFn; + jsc.JSValueProtect(ctx, arrFn); + assert !isJavaClazz(arrayLength) : "functions aren't Java classes"; + } + { + Pointer trueScr = jsc.JSStringCreateWithUTF8CString("true"); + valueTrue = jsc.JSEvaluateScript(ctx, trueScr, null, null, 1, null); + jsc.JSStringRelease(trueScr); + jsc.JSValueProtect(ctx, valueTrue); + int vT = jsc.JSValueGetType(ctx, valueTrue); + assert vT == 2; + assert !isJavaClazz(valueTrue) : "true isn't Java class"; + } + { + Pointer falseScr = jsc.JSStringCreateWithUTF8CString("false"); + valueFalse = jsc.JSEvaluateScript(ctx, falseScr, null, null, 1, null); + jsc.JSValueProtect(ctx, valueFalse); + jsc.JSStringRelease(falseScr); + int vF = jsc.JSValueGetType(ctx, valueFalse); + assert vF == 2; + assert !isJavaClazz(valueFalse) : "false isn't Java class"; + } + } + + private boolean isJavaClazz(Pointer obj) { + final int ret = shell.jsc().JSValueIsObjectOfClass(ctx, obj, javaClazz); + return ret == 1; + } + + + @Texts({ + "version=$version" + }) + final void onPageLoad() { + final String who = "WebKitPresenter:" + shell.getClass().getSimpleName(); + onPageLoad.run(); + } + + private static final class JSObject { + private final Pointer value; + + public JSObject(WebKitPresenter p, Pointer val) { + this.value = val; + p.protect(this, val); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object other) { + return other instanceof JSObject && value.equals(((JSObject)other).value); + } + } + + private static final ReferenceQueue QUEUE = new ReferenceQueue(); + private static final Set ALL = new HashSet<>(); + private void protect(Object obj, Pointer pointer) { + JSC jsc = shell.jsc(); + jsc.JSValueProtect(ctx, pointer); + ALL.add(new Protector(obj, pointer)); + cleanProtected(); + } + + private void cleanProtected() { + for (;;) { + Protector p = (Protector)QUEUE.poll(); + if (p == null) { + break; + } + ALL.remove(p); + p.unprotect(); + } + } + private final class Protector extends PhantomReference { + private final Pointer pointer; + + public Protector(Object referent, Pointer p) { + super(referent, QUEUE); + this.pointer = p; + } + + public void unprotect() { + JSC jsc = shell.jsc(); + jsc.JSValueUnprotect(ctx, pointer); + } + } + + private final class JSCFn extends Fn { + private final Pointer fn; + private final boolean[] keepAlive; + + public JSCFn(Pointer fn, boolean[] keepAlive) { + this.fn = fn; + this.keepAlive = keepAlive; + protect(this, fn); + } + + @Override + public Object invoke(Object thiz, Object... args) throws Exception { + cleanProtected(); + JSC jsc = shell.jsc(); + Pointer[] arr = convertFromJava(args, keepAlive); + Pointer jsThis = thiz == null ? null : convertFromJava(thiz)[0]; + PointerByReference exception = new PointerByReference(); + Pointer ret = jsc.JSObjectCallAsFunction(ctx, fn, jsThis, arr.length, arr, exception); + if (exception.getValue() != null) { + throw new Exception(convertToString(jsc, exception.getValue())); + } + + return convertToJava(jsc, Object.class, ret); + } + } + + + public final class FnCallback implements Callback { + private final Object vm; + private final Method method; + + public FnCallback(Object vm, Method method) { + this.vm = vm; + this.method = method; + } + + public Pointer call( + Pointer jsContextRef, Pointer jsFunction, Pointer thisObject, + int argumentCount, PointerByReference ref, Pointer exception + ) throws Exception { + JSC jsc = shell.jsc(); + int size = Native.getNativeSize(Pointer.class); + Object[] args = new Object[argumentCount]; + for (int i = 0, offset = 0; i < argumentCount; i++, offset += size) { + args[i] = convertToJava(jsc, method.getParameterTypes()[i], ref.getPointer().getPointer(offset)); + } + return convertFromJava(method.invoke(vm, args))[0]; + } + } + + private final class OnFinalize implements Callback { + public void callback(Pointer obj) { + java.util.Iterator> it = toJava.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (entry.getValue() == obj) { + it.remove(); + break; + } + } + } + } + + private static final class WeakVal extends WeakReference { + public WeakVal(Object referent) { + super(referent); + } + } + + private static String findCalleeClassName() { + StackTraceElement[] frames = new Exception().getStackTrace(); + for (StackTraceElement e : frames) { + String cn = e.getClassName(); + if (cn.startsWith("com.dukescript.presenters.")) { // NOI18N + continue; + } + if (cn.startsWith("org.netbeans.html.")) { // NOI18N + continue; + } + if (cn.startsWith("net.java.html.")) { // NOI18N + continue; + } + if (cn.startsWith("java.")) { // NOI18N + continue; + } + if (cn.startsWith("javafx.")) { // NOI18N + continue; + } + if (cn.startsWith("com.sun.")) { // NOI18N + continue; + } + return cn; + } + return "org.netbeans.html"; // NOI18N + } +} diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java b/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java new file mode 100644 index 00000000..95b374f4 --- /dev/null +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java @@ -0,0 +1,113 @@ +package com.dukescript.presenters.webkit; + +/* + * #%L + * WebKit Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Executor; +import org.netbeans.html.boot.spi.Fn; +import org.testng.IHookCallBack; +import org.testng.IHookable; +import org.testng.ITest; +import org.testng.ITestResult; +import org.testng.annotations.Test; + +public final class Case implements ITest, IHookable, Runnable { + private static final Timer T = new Timer("Interrupted Exception Handler"); + private final Fn.Presenter p; + private final Method m; + private Object result; + private Object inst; + + Case(Fn.Presenter p, Method m) { + this.p = p; + this.m = m; + } + + @Override + public String getTestName() { + return m.getName(); + } + + @Test + public synchronized void executeTest() throws Exception { + if (result == null) { + Executor exec = (Executor) p; + exec.execute(this); + wait(); + } + if (result instanceof Exception) { + throw (Exception)result; + } + if (result instanceof Error) { + throw (Error)result; + } + } + + @Override + public synchronized void run() { + boolean notify = true; + try { + if (inst == null) { + inst = m.getDeclaringClass().newInstance(); + } + result = m.invoke(inst); + if (result == null) { + result = this; + } + } catch (InvocationTargetException ex) { + Throwable r = ex.getTargetException(); + if (r instanceof InterruptedException) { + notify = false; + TimerTask tt = new TimerTask() { + @Override + public void run() { + ((Executor)p).execute(Case.this); + } + }; + T.schedule(tt, 100); + return; + } + result = r; + } catch (Exception ex) { + result = ex; + } finally { + if (notify) { + notifyAll(); + } + } + } + + @Override + public void run(IHookCallBack ihcb, ITestResult itr) { + ihcb.runTestMethod(itr); + } + +} diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java b/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java new file mode 100644 index 00000000..17de426a --- /dev/null +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java @@ -0,0 +1,246 @@ +package com.dukescript.presenters.webkit; + +/* + * #%L + * WebKit Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.glassfish.grizzly.PortRange; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.websockets.WebSocket; +import org.glassfish.grizzly.websockets.WebSocketAddOn; +import org.glassfish.grizzly.websockets.WebSocketApplication; +import org.glassfish.grizzly.websockets.WebSocketEngine; + +final class DynamicHTTP extends HttpHandler { + private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName()); + private static int resourcesCount; + private static List resources; + private static ServerConfiguration conf; + private static HttpServer server; + + private DynamicHTTP() { + } + + static URI initServer() throws Exception { + server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535)); + final WebSocketAddOn addon = new WebSocketAddOn(); + for (NetworkListener listener : server.getListeners()) { + listener.registerAddOn(addon); + } + resources = new ArrayList(); + + conf = server.getServerConfiguration(); + final DynamicHTTP dh = new DynamicHTTP(); + + conf.addHttpHandler(dh, "/"); + + server.start(); + + return pageURL("http", server, "/test.html"); + } + + @Override + public void service(Request request, Response response) throws Exception { + if ("/test.html".equals(request.getRequestURI())) { + response.setContentType("text/html"); + final InputStream is = new ByteArrayInputStream(( + "" + + "

GTK harness

" + + "" + + "" + ).getBytes("UTF-8")); + copyStream(is, response.getOutputStream(), null); + return; + } + if ("/dynamic".equals(request.getRequestURI())) { + String mimeType = request.getParameter("mimeType"); + List params = new ArrayList(); + boolean webSocket = false; + for (int i = 0;; i++) { + String p = request.getParameter("param" + i); + if (p == null) { + break; + } + if ("protocol:ws".equals(p)) { + webSocket = true; + continue; + } + params.add(p); + } + final String cnt = request.getParameter("content"); + String mangle = cnt.replace("%20", " ").replace("%0A", "\n"); + ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8")); + URI url; + final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()])); + if (webSocket) { + url = registerWebSocket(res); + } else { + url = registerResource(res); + } + response.getWriter().write(url.toString()); + response.getWriter().write("\n"); + return; + } + + for (Resource r : resources) { + if (r.httpPath.equals(request.getRequestURI())) { + response.setContentType(r.httpType); + r.httpContent.reset(); + String[] params = null; + if (r.parameters.length != 0) { + params = new String[r.parameters.length]; + for (int i = 0; i < r.parameters.length; i++) { + params[i] = request.getParameter(r.parameters[i]); + if (params[i] == null) { + if ("http.method".equals(r.parameters[i])) { + params[i] = request.getMethod().toString(); + } else if ("http.requestBody".equals(r.parameters[i])) { + Reader rdr = request.getReader(); + StringBuilder sb = new StringBuilder(); + for (;;) { + int ch = rdr.read(); + if (ch == -1) { + break; + } + sb.append((char) ch); + } + params[i] = sb.toString(); + } else if (r.parameters[i].startsWith("http.header.")) { + params[i] = request.getHeader(r.parameters[i].substring(12)); + } + } + if (params[i] == null) { + params[i] = "null"; + } + } + } + + copyStream(r.httpContent, response.getOutputStream(), null, params); + } + } + } + + private URI registerWebSocket(Resource r) { + WebSocketEngine.getEngine().register("", r.httpPath, new WS(r)); + return pageURL("ws", server, r.httpPath); + } + + private URI registerResource(Resource r) { + if (!resources.contains(r)) { + resources.add(r); + conf.addHttpHandler(this, r.httpPath); + } + return pageURL("http", server, r.httpPath); + } + + private static URI pageURL(String proto, HttpServer server, final String page) { + NetworkListener listener = server.getListeners().iterator().next(); + int port = listener.getPort(); + try { + return new URI(proto + "://localhost:" + port + page); + } catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } + + static final class Resource { + + final InputStream httpContent; + final String httpType; + final String httpPath; + final String[] parameters; + + Resource(InputStream httpContent, String httpType, String httpPath, + String[] parameters) { + httpContent.mark(Integer.MAX_VALUE); + this.httpContent = httpContent; + this.httpType = httpType; + this.httpPath = httpPath; + this.parameters = parameters; + } + } + + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + if (ch == '$' && params.length > 0) { + int cnt = is.read() - '0'; + if (baseURL != null && cnt == 'U' - '0') { + os.write(baseURL.getBytes("UTF-8")); + } else { + if (cnt >= 0 && cnt < params.length) { + os.write(params[cnt].getBytes("UTF-8")); + } else { + os.write('$'); + os.write(cnt + '0'); + } + } + } else { + os.write(ch); + } + } + } + + private static class WS extends WebSocketApplication { + private final Resource r; + + private WS(Resource r) { + this.r = r; + } + + @Override + public void onMessage(WebSocket socket, String text) { + try { + r.httpContent.reset(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copyStream(r.httpContent, out, null, text); + String s = new String(out.toByteArray(), "UTF-8"); + socket.send(s); + } catch (IOException ex) { + LOG.log(Level.WARNING, "Error processing message " + text, ex); + } + } + } +} diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java new file mode 100644 index 00000000..62cbbba6 --- /dev/null +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java @@ -0,0 +1,107 @@ +package com.dukescript.presenters.webkit; + +/* + * #%L + * WebKit Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import net.java.html.BrwsrCtx; +import net.java.html.boot.BrowserBuilder; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.json.tck.JavaScriptTCK; +import org.netbeans.html.json.tck.KOTest; +import org.testng.Assert; +import static org.testng.Assert.assertNotSame; +import static org.testng.Assert.assertSame; +import org.testng.annotations.Factory; + +public class GtkJavaScriptTest extends JavaScriptTCK { + private static Class browserClass; + private static Fn.Presenter browserPresenter; + + public GtkJavaScriptTest() { + } + + @Factory public static Object[] compatibilityTests() throws Exception { + final BrowserBuilder bb = BrowserBuilder.newBrowser(new WebKitPresenter(true)).loadClass(GtkJavaScriptTest.class). + loadPage("empty.html"). + invoke("initialized"); + + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + bb.showAndWait(); + } + }); + + List res = new ArrayList<>(); + Class test = + loadClass().getClassLoader().loadClass(KOTest.class.getName()). + asSubclass(Annotation.class); + + Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null); + for (Class c : arr) { + for (Method m : c.getMethods()) { + if (m.getAnnotation(test) != null) { + res.add(new Case(browserPresenter, m)); + } + } + } + return res.toArray(); + } + + static synchronized Class loadClass() throws InterruptedException { + while (browserClass == null) { + GtkJavaScriptTest.class.wait(); + } + return browserClass; + } + + public static synchronized void ready(Class browserCls) throws Exception { + browserClass = browserCls; + browserPresenter = Fn.activePresenter(); + GtkJavaScriptTest.class.notifyAll(); + } + + public static void initialized() throws Exception { + BrwsrCtx b1 = BrwsrCtx.findDefault(GtkJavaScriptTest.class); + assertNotSame(b1, BrwsrCtx.EMPTY, "Browser context is not empty"); + BrwsrCtx b2 = BrwsrCtx.findDefault(GtkJavaScriptTest.class); + assertSame(b1, b2, "Browser context remains stable"); + Assert.assertSame(GtkJavaScriptTest.class.getClassLoader(), + ClassLoader.getSystemClassLoader(), + "No special classloaders" + ); + GtkJavaScriptTest.ready(GtkJavaScriptTest.class); + } + + public static Class[] tests() { + return testClasses(); + } +} diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java new file mode 100644 index 00000000..5be97e72 --- /dev/null +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java @@ -0,0 +1,214 @@ +package com.dukescript.presenters.webkit; + +/* + * #%L + * WebKit Presenter - a library from the "DukeScript Presenters" project. + * + * Dukehoff GmbH designates this particular file as subject to the "Classpath" + * exception as provided in the README.md file that accompanies this code. + * %% + * Copyright (C) 2015 - 2019 Dukehoff GmbH + * %% + * 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, either version 3 of the + * License, or (at your option) any later version. + * + * 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. If not, see + * . + * #L% + */ + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import net.java.html.BrwsrCtx; +import net.java.html.boot.BrowserBuilder; +import net.java.html.js.JavaScriptBody; +import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.json.spi.Technology; +import org.netbeans.html.json.spi.Transfer; +import org.netbeans.html.json.spi.WSTransfer; +import org.netbeans.html.json.tck.KOTest; +import org.netbeans.html.json.tck.KnockoutTCK; +import org.netbeans.html.ko4j.KO4J; +import org.openide.util.lookup.ServiceProvider; +import org.testng.Assert; +import static org.testng.Assert.*; +import org.testng.annotations.Factory; + +@ServiceProvider(service = KnockoutTCK.class) +public final class GtkKnockoutTest extends KnockoutTCK { + private static Class browserClass; + private static Fn.Presenter browserContext; + + public GtkKnockoutTest() { + } + + @Factory public static Object[] compatibilityTests() throws Exception { + Class[] arr = testClasses(); + for (int i = 0; i < arr.length; i++) { + assertEquals(arr[i].getClassLoader(), + GtkKnockoutTest.class.getClassLoader(), + "All classes loaded by the same classloader" + ); + } + + URI uri = DynamicHTTP.initServer(); + + final BrowserBuilder bb = BrowserBuilder.newBrowser( + new WebKitPresenter(true) + ).loadClass(GtkKnockoutTest.class). + loadPage(uri.toString()). + invoke("initialized"); + + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + bb.showAndWait(); + } + }); + + ClassLoader l = getClassLoader(); + List res = new ArrayList<>(); + for (Class oldC : arr) { + Class c = Class.forName(oldC.getName(), true, l); + seekKOTests(c, res); + } + return res.toArray(); + } + + private static void seekKOTests(Class c, List res) throws SecurityException, ClassNotFoundException { + Class koTest = + c.getClassLoader().loadClass(KOTest.class.getName()). + asSubclass(Annotation.class); + for (Method m : c.getMethods()) { + if (m.getAnnotation(koTest) != null) { + res.add(new Case(browserContext, m)); + } + } + } + + static synchronized ClassLoader getClassLoader() throws InterruptedException { + while (browserClass == null) { + GtkKnockoutTest.class.wait(); + } + return browserClass.getClassLoader(); + } + + public static synchronized void initialized(Class browserCls) throws Exception { + browserClass = browserCls; + browserContext = Fn.activePresenter(); + GtkKnockoutTest.class.notifyAll(); + } + + public static void initialized() throws Exception { + Assert.assertSame(GtkKnockoutTest.class.getClassLoader(), + ClassLoader.getSystemClassLoader(), + "No special classloaders" + ); + GtkKnockoutTest.initialized(GtkKnockoutTest.class); + browserContext = Fn.activePresenter(); + } + + @Override + public BrwsrCtx createContext() { + KO4J ko4j = new KO4J(); + Contexts.Builder cb = Contexts.newBuilder(). + register(Technology.class, ko4j.knockout(), 10). + register(Transfer.class, ko4j.transfer(), 10); + if (ko4j.websockets() != null) { + cb.register(WSTransfer.class, ko4j.websockets(), 10); + } + cb.register(Executor.class, (Executor)browserContext, 10); + cb.register(Fn.Presenter.class, browserContext, 10); + BrwsrCtx ctx = cb.build(); + return ctx; + } + + @Override + public Object createJSON(Map values) { + Object json = createJSON(); + for (Map.Entry entry : values.entrySet()) { + setProperty(json, entry.getKey(), entry.getValue()); + } + return json; + } + + @JavaScriptBody(args = {}, body = "return new Object();") + private static native Object createJSON(); + @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;") + private static native void setProperty(Object json, String key, Object value); + + @Override + @JavaScriptBody(args = { "s", "args" }, body = "" + + "var f = new Function(s); " + + "return f.apply(null, args);" + ) + public native Object executeScript(String script, Object[] arguments); + + @JavaScriptBody(args = { }, body = + "var h;" + + "if (!!window && !!window.location && !!window.location.href)\n" + + " h = window.location.href;\n" + + "else " + + " h = null;" + + "return h;\n" + ) + private static native String findBaseURL(); + + @Override + public URI prepareURL(String content, String mimeType, String[] parameters) { + try { + final URL baseURL = new URL(findBaseURL()); + StringBuilder sb = new StringBuilder(); + sb.append("/dynamic?mimeType=").append(mimeType); + for (int i = 0; i < parameters.length; i++) { + sb.append("¶m").append(i).append("=").append(parameters[i]); + } + String mangle = content.replace("\n", "%0a") + .replace("\"", "\\\"").replace(" ", "%20"); + sb.append("&content=").append(mangle); + + URL query = new URL(baseURL, sb.toString()); + URLConnection c = query.openConnection(); + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream())); + URI connectTo = new URI(br.readLine()); + return connectTo; + } catch (IOException ex) { + throw new IllegalStateException(ex); + } catch (URISyntaxException ex) { + throw new IllegalStateException(ex); + } + } + + @Override + public boolean canFailWebSocketTest() { + try { + Class.forName("java.util.function.Function"); + return false; + } catch (ClassNotFoundException ex) { + // running on JDK7, FX WebView WebSocket impl does not work + return true; + } + } +} diff --git a/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html b/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html new file mode 100644 index 00000000..408a59ba --- /dev/null +++ b/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html @@ -0,0 +1,35 @@ + + + + + Empty + + + + +
Empty
+ + From 0aaf14736901706e0782f2570328985e18ef5d57 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sat, 28 Sep 2019 13:14:40 +0200 Subject: [PATCH 02/16] Adding new modules to the build --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index ac35b02b..1d031d2f 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,11 @@ --> boot-agent-test xhr4j + + browser + renderer + generic + webkit From e27b03d7f3b0e5ecee29eba1fa28acae62c3d79a Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sat, 28 Sep 2019 13:29:24 +0200 Subject: [PATCH 03/16] Fixing pom.xml files to let the projects compile --- browser/pom.xml | 30 ++++++++++++++++++++++++------ generic/pom.xml | 29 ++++++++++++++++++++++++----- pom.xml | 5 +++++ renderer/pom.xml | 26 ++++++++++++++++++++++---- webkit/pom.xml | 32 +++++++++++++++++++++++++------- 5 files changed, 100 insertions(+), 22 deletions(-) diff --git a/browser/pom.xml b/browser/pom.xml index cbb2bc77..390363bc 100644 --- a/browser/pom.xml +++ b/browser/pom.xml @@ -1,11 +1,30 @@ + 4.0.0 browser - com.dukescript.presenters jar - com.dukescript.presenters + org.netbeans.html pom 2.0-SNAPSHOT @@ -53,7 +72,7 @@ org.netbeans.html html4j-maven-plugin - ${net.java.html.version} + ${project.version} process-test-classes @@ -72,7 +91,6 @@ org.testng testng - ${testng.version} test @@ -83,7 +101,7 @@ org.netbeans.html ko4j - ${net.java.html.version} + ${project.version} test @@ -95,7 +113,7 @@ org.netbeans.html net.java.html.json.tck - ${net.java.html.version} + ${project.version} test diff --git a/generic/pom.xml b/generic/pom.xml index cc907726..66ef4923 100644 --- a/generic/pom.xml +++ b/generic/pom.xml @@ -1,8 +1,28 @@ + 4.0.0 - com.dukescript.presenters + org.netbeans.html pom 2.0-SNAPSHOT @@ -20,12 +40,11 @@ org.netbeans.html net.java.html.boot jar - ${net.java.html.version} + ${project.version} org.testng testng - ${testng.version} test @@ -33,7 +52,7 @@ net.java.html.json.tck test jar - ${net.java.html.version} + ${project.version} com.dukescript.api @@ -82,7 +101,7 @@ org.netbeans.html html4j-maven-plugin - ${net.java.html.version} + ${project.version} test-classes diff --git a/pom.xml b/pom.xml index 1d031d2f..c4b0d8a6 100644 --- a/pom.xml +++ b/pom.xml @@ -335,6 +335,11 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. + + net.java.dev.jna + jna + 5.3.1 + org.openjfx javafx-web diff --git a/renderer/pom.xml b/renderer/pom.xml index d2c22b40..4d4cb473 100644 --- a/renderer/pom.xml +++ b/renderer/pom.xml @@ -1,8 +1,28 @@ + 4.0.0 - com.dukescript.presenters + org.netbeans.html pom 2.0-SNAPSHOT @@ -35,18 +55,16 @@ org.testng testng - ${testng.version} test net.java.dev.jna jna - ${jna.version} org.netbeans.html net.java.html.boot - ${net.java.html.version} + ${project.version} \ No newline at end of file diff --git a/webkit/pom.xml b/webkit/pom.xml index 66355b78..8eab28cc 100644 --- a/webkit/pom.xml +++ b/webkit/pom.xml @@ -1,8 +1,28 @@ + 4.0.0 - com.dukescript.presenters + org.netbeans.html pom 2.0-SNAPSHOT @@ -53,7 +73,7 @@ org.netbeans.html html4j-maven-plugin - ${net.java.html.version} + ${project.version} classes @@ -79,18 +99,16 @@ org.testng testng - ${testng.version} test net.java.dev.jna jna - ${jna.version} org.netbeans.html net.java.html.boot - ${net.java.html.version} + ${project.version} jar @@ -102,7 +120,7 @@ org.netbeans.html net.java.html.json.tck - ${net.java.html.version} + ${project.version} test jar @@ -136,7 +154,7 @@ org.netbeans.html ko4j - ${net.java.html.version} + ${project.version} test jar From 1b79a744586c13ea040b5566c6656907843101ce Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sun, 29 Sep 2019 08:04:46 +0200 Subject: [PATCH 04/16] Cleanup versions of the dependencies --- browser/pom.xml | 4 +--- generic/pom.xml | 2 -- pom.xml | 7 +++++++ webkit/pom.xml | 5 ----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/browser/pom.xml b/browser/pom.xml index 390363bc..44f6b0cb 100644 --- a/browser/pom.xml +++ b/browser/pom.xml @@ -72,7 +72,6 @@ org.netbeans.html html4j-maven-plugin - ${project.version} process-test-classes @@ -107,7 +106,6 @@ org.netbeans.api org-openide-util-lookup - ${netbeans.version} provided @@ -123,7 +121,7 @@ 2.3.19 - com.dukescript.presenters + ${project.groupId} generic ${project.version} diff --git a/generic/pom.xml b/generic/pom.xml index 66ef4923..84a80937 100644 --- a/generic/pom.xml +++ b/generic/pom.xml @@ -57,8 +57,6 @@ com.dukescript.api strings - ${project.version} - provided diff --git a/pom.xml b/pom.xml index c4b0d8a6..73142dd5 100644 --- a/pom.xml +++ b/pom.xml @@ -356,6 +356,13 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. android-json 0.0.20131108.vaadin1 + + com.dukescript.api + strings + 1.6.2 + jar + provided + org.ow2.asm asm diff --git a/webkit/pom.xml b/webkit/pom.xml index 8eab28cc..0f5baca0 100644 --- a/webkit/pom.xml +++ b/webkit/pom.xml @@ -73,7 +73,6 @@ org.netbeans.html html4j-maven-plugin - ${project.version} classes @@ -114,7 +113,6 @@ org.netbeans.api org-openide-util-lookup - ${netbeans.version} provided @@ -148,7 +146,6 @@ javax.servlet javax.servlet-api - 3.1.0 test @@ -161,8 +158,6 @@ com.dukescript.api strings - ${project.version} - provided ${project.groupId} From f13ebe78f76266ee9a350ed8df567ff1bc6df3f3 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sun, 29 Sep 2019 08:46:18 +0200 Subject: [PATCH 05/16] Adjusting to new TCK by hiding proprietary properties --- .../java/com/dukescript/presenters/spi/Generic.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java b/generic/src/main/java/com/dukescript/presenters/spi/Generic.java index 4327b728..097c576e 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java +++ b/generic/src/main/java/com/dukescript/presenters/spi/Generic.java @@ -88,10 +88,14 @@ final Object lock() { "init=(function(global) {" + "\n var fncns = new Array();" + "\n var js2j = new Array();" - + "\n function jobject(id,value) { this.id = id; this.v = value; return this; };" - + "\n jobject.prototype['native'] = true;" - + "\n jobject.prototype.valueOf = function() { return this.v ? this.v : '[jobject ' + this.id + ']'; };" - + "\n jobject.prototype.toString = jobject.prototype.valueOf;" + + "\n function jobject(id,value) {" + + "\n Object.defineProperty(this, 'id', { value : id });" + + "\n Object.defineProperty(this, 'v', { value : value });" + + "\n return this;" + + "\n };" + + "\n Object.defineProperty(jobject.prototype, 'native', { value : true });" + + "\n Object.defineProperty(jobject.prototype, 'valueOf', { value : function() { return this.v ? this.v : '[jobject ' + this.id + ']'; } });" + + "\n Object.defineProperty(jobject.prototype, 'toString', { value : jobject.prototype.valueOf });" + "\n var toVM = global['@2'];" + "\n delete global['@2'];" + "\n if (typeof toVM !== 'function') {" From a78ac508fafb0c1ba47b62e803cdb01da846d989 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sun, 29 Sep 2019 11:29:44 +0200 Subject: [PATCH 06/16] Relicensing to Apache license --- .../com/dukescript/presenters/Browser.java | 44 +++++++---------- .../dukescript/presenters/BrowserTest.java | 44 +++++++---------- .../dukescript/presenters/DynamicHTTP.java | 44 +++++++---------- .../com/dukescript/presenters/KOScript.java | 44 +++++++---------- .../dukescript/presenters/KoBrowserTest.java | 44 +++++++---------- .../com/dukescript/presenters/ServerTest.java | 39 +++++++-------- .../com/dukescript/presenters/empty.html | 42 ++++++++--------- .../com/dukescript/presenters/server.html | 42 ++++++++--------- .../dukescript/presenters/spi/Generic.java | 43 +++++++---------- .../com/dukescript/presenters/spi/Level.java | 39 +++++++-------- .../presenters/spi/ProtoPresenter.java | 43 +++++++---------- .../presenters/spi/ProtoPresenterBuilder.java | 43 +++++++---------- .../presenters/spi/ValueOfTest.java | 47 +++++++------------ .../presenters/spi/test/CallbackTest.java | 44 +++++++---------- .../dukescript/presenters/spi/test/Case.java | 44 +++++++---------- .../presenters/spi/test/Counter.java | 44 +++++++---------- .../presenters/spi/test/GenericTest.java | 45 +++++++----------- .../presenters/spi/test/SynchronizedTest.java | 44 +++++++---------- .../presenters/spi/test/Testing.java | 44 +++++++---------- .../html/js/tests/JavaScriptBodyTest.java | 4 -- pom.xml | 1 - .../dukescript/presenters/renderer/AWT.java | 44 +++++++---------- .../dukescript/presenters/renderer/Cocoa.java | 44 +++++++---------- .../dukescript/presenters/renderer/GTK.java | 44 +++++++---------- .../dukescript/presenters/renderer/JSC.java | 44 +++++++---------- .../dukescript/presenters/renderer/Show.java | 44 +++++++---------- .../presenters/webkit/UnJarResources.java | 43 +++++++---------- .../presenters/webkit/WebKitPresenter.java | 44 +++++++---------- .../dukescript/presenters/webkit/Case.java | 45 +++++++----------- .../presenters/webkit/DynamicHTTP.java | 44 +++++++---------- .../presenters/webkit/GtkJavaScriptTest.java | 44 +++++++---------- .../presenters/webkit/GtkKnockoutTest.java | 44 +++++++---------- .../dukescript/presenters/webkit/empty.html | 42 ++++++++--------- 33 files changed, 557 insertions(+), 797 deletions(-) diff --git a/browser/src/main/java/com/dukescript/presenters/Browser.java b/browser/src/main/java/com/dukescript/presenters/Browser.java index 11a7a414..bc3c16b7 100644 --- a/browser/src/main/java/com/dukescript/presenters/Browser.java +++ b/browser/src/main/java/com/dukescript/presenters/Browser.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters; - -/* - * #%L - * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters; import com.dukescript.presenters.renderer.Show; import java.io.Closeable; diff --git a/browser/src/test/java/com/dukescript/presenters/BrowserTest.java b/browser/src/test/java/com/dukescript/presenters/BrowserTest.java index b965b987..d4d23ef4 100644 --- a/browser/src/test/java/com/dukescript/presenters/BrowserTest.java +++ b/browser/src/test/java/com/dukescript/presenters/BrowserTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters; - -/* - * #%L - * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters; import java.lang.annotation.Annotation; import java.lang.reflect.Method; diff --git a/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java b/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java index fc3e02b6..fe62bdb4 100644 --- a/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java +++ b/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters; - -/* - * #%L - * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/browser/src/test/java/com/dukescript/presenters/KOScript.java b/browser/src/test/java/com/dukescript/presenters/KOScript.java index a719a609..b6b724c5 100644 --- a/browser/src/test/java/com/dukescript/presenters/KOScript.java +++ b/browser/src/test/java/com/dukescript/presenters/KOScript.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters; - -/* - * #%L - * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters; import java.io.Closeable; import java.io.IOException; diff --git a/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java b/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java index 0386221a..7bcf1236 100644 --- a/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java +++ b/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters; - -/* - * #%L - * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters; import java.io.BufferedReader; import java.io.IOException; diff --git a/browser/src/test/java/com/dukescript/presenters/ServerTest.java b/browser/src/test/java/com/dukescript/presenters/ServerTest.java index a73b1873..417501fe 100644 --- a/browser/src/test/java/com/dukescript/presenters/ServerTest.java +++ b/browser/src/test/java/com/dukescript/presenters/ServerTest.java @@ -1,29 +1,22 @@ -package com.dukescript.presenters; - -/* - * #%L - * DukeScript Presenter for any Browser - a library from the "DukeScript Presenters" project. +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. If not, see - * . - * #L% + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ +package com.dukescript.presenters; import com.dukescript.presenters.renderer.Show; import java.io.IOException; diff --git a/browser/src/test/resources/com/dukescript/presenters/empty.html b/browser/src/test/resources/com/dukescript/presenters/empty.html index 3bb2554b..45eb2c1a 100644 --- a/browser/src/test/resources/com/dukescript/presenters/empty.html +++ b/browser/src/test/resources/com/dukescript/presenters/empty.html @@ -1,27 +1,23 @@ + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> diff --git a/browser/src/test/resources/com/dukescript/presenters/server.html b/browser/src/test/resources/com/dukescript/presenters/server.html index 9f089e64..39a2603a 100644 --- a/browser/src/test/resources/com/dukescript/presenters/server.html +++ b/browser/src/test/resources/com/dukescript/presenters/server.html @@ -1,27 +1,23 @@ + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java b/generic/src/main/java/com/dukescript/presenters/spi/Generic.java index 097c576e..e61d189a 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java +++ b/generic/src/main/java/com/dukescript/presenters/spi/Generic.java @@ -1,29 +1,22 @@ -package com.dukescript.presenters.spi; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ +package com.dukescript.presenters.spi; import java.io.Flushable; import java.io.IOException; diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Level.java b/generic/src/main/java/com/dukescript/presenters/spi/Level.java index ff962644..1781fbfb 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/Level.java +++ b/generic/src/main/java/com/dukescript/presenters/spi/Level.java @@ -1,29 +1,22 @@ -package com.dukescript.presenters.spi; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. + * http://www.apache.org/licenses/LICENSE-2.0 * - * 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. If not, see - * . - * #L% + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ +package com.dukescript.presenters.spi; /** Logging levels similar to java.util.logging ones. */ enum Level { diff --git a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java index 329b7b1c..8094620f 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java +++ b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java @@ -1,33 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package com.dukescript.presenters.spi; import java.io.Flushable; import org.netbeans.html.boot.spi.Fn; -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% - */ - /** The prototypical presenter. An implementation of a {@link Fn.Presenter} based on * top of textual protocol transferred between JVM and JavaScript engines. Use * {@link ProtoPresenterBuilder#newBuilder()} to construct instance of this diff --git a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java index 6fb38353..90f891c1 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java +++ b/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java @@ -1,3 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package com.dukescript.presenters.spi; import java.net.URL; @@ -6,31 +24,6 @@ import java.util.concurrent.Executor; import org.netbeans.html.boot.spi.Fn; -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% - */ - /** The prototypical presenter builder. Builds a {@link Fn.Presenter} based on * top of textual protocol transferred between JVM and JavaScript engines. */ diff --git a/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java b/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java index f137ee93..ef914f00 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java @@ -1,34 +1,23 @@ -package com.dukescript.presenters.spi; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ +package com.dukescript.presenters.spi; - -import com.dukescript.presenters.spi.ProtoPresenterBuilder; -import com.dukescript.presenters.spi.Level; -import com.dukescript.presenters.spi.Generic; import java.net.URL; import static org.testng.Assert.*; import org.testng.annotations.BeforeMethod; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java b/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java index 4be2ab53..b9d368a2 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.spi.test; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.spi.test; import java.lang.reflect.Method; import java.util.logging.Level; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java b/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java index ca136b02..032ecca6 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.spi.test; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.spi.test; import java.io.Closeable; import java.io.IOException; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java b/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java index 1991a256..4146b545 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.spi.test; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.spi.test; import net.java.html.js.JavaScriptBody; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java b/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java index 871e7027..b7afb242 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.spi.test; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.spi.test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -35,7 +27,6 @@ import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.json.tck.JavaScriptTCK; import org.netbeans.html.json.tck.KOTest; -import com.dukescript.presenters.spi.ProtoPresenterBuilder; import org.testng.annotations.Factory; public class GenericTest extends JavaScriptTCK { diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java b/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java index cd5c174e..9714bacc 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.spi.test; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.spi.test; import static com.dukescript.presenters.spi.test.GenericTest.createTests; import org.testng.annotations.Factory; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java b/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java index 29812a65..db9b5999 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java +++ b/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.spi.test; - -/* - * #%L - * DukeScript Generic Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.spi.test; import java.net.URL; import java.util.concurrent.Executor; diff --git a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java index 86f15f48..b3c3f215 100644 --- a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java +++ b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java @@ -19,10 +19,6 @@ package net.java.html.js.tests; import java.io.StringReader; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.concurrent.Callable; import net.java.html.json.Models; import org.netbeans.html.boot.spi.Fn; diff --git a/pom.xml b/pom.xml index 73142dd5..e18f49d4 100644 --- a/pom.xml +++ b/pom.xml @@ -186,7 +186,6 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. org.apache.rat apache-rat-plugin - 0.12 prepare-package diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java b/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java index e99ee697..403e4e8e 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.renderer; - -/* - * #%L - * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.renderer; import com.sun.jna.Pointer; import java.io.IOException; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java b/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java index 786af000..0e5156b0 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.renderer; - -/* - * #%L - * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.renderer; import com.sun.jna.Callback; import com.sun.jna.CallbackThreadInitializer; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java b/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java index 6d10093a..c9e3a510 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.renderer; - -/* - * #%L - * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.renderer; import com.sun.jna.Callback; import com.sun.jna.Library; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java b/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java index b7756e3d..484c853b 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.renderer; - -/* - * #%L - * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.renderer; import com.sun.jna.Callback; import com.sun.jna.Library; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java b/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java index 1ab9cb76..9d43091d 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java +++ b/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.renderer; - -/* - * #%L - * Desktop Browser Renderer - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.renderer; import com.sun.jna.Pointer; import java.io.IOException; diff --git a/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java b/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java index febcdd5e..59bf56b9 100644 --- a/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java +++ b/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java @@ -1,29 +1,22 @@ -package com.dukescript.presenters.webkit; - -/* - * #%L - * WebKit Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ +package com.dukescript.presenters.webkit; import java.io.File; import java.io.IOException; diff --git a/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java b/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java index d7924a43..c87ecb23 100644 --- a/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java +++ b/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.webkit; - -/* - * #%L - * WebKit Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.webkit; import com.dukescript.presenters.renderer.JSC; import com.dukescript.presenters.renderer.Show; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java b/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java index 95b374f4..a90e9a09 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java @@ -1,31 +1,22 @@ -package com.dukescript.presenters.webkit; - -/* - * #%L - * WebKit Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - - +package com.dukescript.presenters.webkit; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java b/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java index 17de426a..d9fbd0d0 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.webkit; - -/* - * #%L - * WebKit Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.webkit; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java index 62cbbba6..b7fc7dd9 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.webkit; - -/* - * #%L - * WebKit Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.webkit; import java.lang.annotation.Annotation; import java.lang.reflect.Method; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java index 5be97e72..4dbe0d29 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java +++ b/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java @@ -1,30 +1,22 @@ -package com.dukescript.presenters.webkit; - -/* - * #%L - * WebKit Presenter - a library from the "DukeScript Presenters" project. - * - * Dukehoff GmbH designates this particular file as subject to the "Classpath" - * exception as provided in the README.md file that accompanies this code. - * %% - * Copyright (C) 2015 - 2019 Dukehoff GmbH - * %% - * 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, either version 3 of the - * License, or (at your option) any later version. - * - * 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. If not, see - * . - * #L% +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ - +package com.dukescript.presenters.webkit; import java.io.BufferedReader; import java.io.IOException; diff --git a/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html b/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html index 408a59ba..013a440e 100644 --- a/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html +++ b/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html @@ -1,27 +1,23 @@ + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> From acfb95018d7f8c16ec80d64a6c107ee3d42ac615 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Mon, 30 Sep 2019 07:02:13 +0200 Subject: [PATCH 07/16] Moving to org.netbeans.html.presenters namespace --- .../netbeans/html/presenters/browser}/Browser.java | 9 ++++----- .../netbeans/html/presenters/browser}/BrowserTest.java | 2 +- .../netbeans/html/presenters/browser}/DynamicHTTP.java | 2 +- .../netbeans/html/presenters/browser}/KOScript.java | 2 +- .../netbeans/html/presenters/browser}/KoBrowserTest.java | 3 ++- .../netbeans/html/presenters/browser}/ServerTest.java | 5 +++-- .../netbeans/html/presenters/browser}/empty.html | 0 .../netbeans/html/presenters/browser}/server.html | 0 generic/pom.xml | 3 +-- .../netbeans/html}/presenters/spi/Generic.java | 4 ++-- .../netbeans/html}/presenters/spi/Level.java | 2 +- .../netbeans/html}/presenters/spi/ProtoPresenter.java | 2 +- .../html}/presenters/spi/ProtoPresenterBuilder.java | 2 +- .../netbeans/html}/presenters/spi/ValueOfTest.java | 5 ++++- .../netbeans/html}/presenters/spi/test/CallbackTest.java | 2 +- .../netbeans/html}/presenters/spi/test/Case.java | 2 +- .../netbeans/html}/presenters/spi/test/Counter.java | 4 ++-- .../netbeans/html}/presenters/spi/test/GenericTest.java | 2 +- .../html}/presenters/spi/test/SynchronizedTest.java | 4 ++-- .../netbeans/html}/presenters/spi/test/Testing.java | 6 +++--- .../netbeans/html/presenters/render}/AWT.java | 2 +- .../netbeans/html/presenters/render}/Cocoa.java | 2 +- .../netbeans/html/presenters/render}/GTK.java | 2 +- .../netbeans/html/presenters/render}/JSC.java | 2 +- .../netbeans/html/presenters/render}/Show.java | 2 +- webkit/pom.xml | 2 +- .../netbeans/html}/presenters/webkit/UnJarResources.java | 2 +- .../html}/presenters/webkit/WebKitPresenter.java | 6 +++--- .../netbeans/html}/presenters/webkit/Case.java | 2 +- .../netbeans/html}/presenters/webkit/DynamicHTTP.java | 2 +- .../html}/presenters/webkit/GtkJavaScriptTest.java | 3 ++- .../html}/presenters/webkit/GtkKnockoutTest.java | 3 ++- .../netbeans/html}/presenters/webkit/empty.html | 0 33 files changed, 48 insertions(+), 43 deletions(-) rename browser/src/main/java/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/Browser.java (99%) rename browser/src/test/java/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/BrowserTest.java (98%) rename browser/src/test/java/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/DynamicHTTP.java (99%) rename browser/src/test/java/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/KOScript.java (98%) rename browser/src/test/java/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/KoBrowserTest.java (98%) rename browser/src/test/java/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/ServerTest.java (96%) rename browser/src/test/resources/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/empty.html (100%) rename browser/src/test/resources/{com/dukescript/presenters => org/netbeans/html/presenters/browser}/server.html (100%) rename generic/src/main/java/{com/dukescript => org/netbeans/html}/presenters/spi/Generic.java (99%) rename generic/src/main/java/{com/dukescript => org/netbeans/html}/presenters/spi/Level.java (96%) rename generic/src/main/java/{com/dukescript => org/netbeans/html}/presenters/spi/ProtoPresenter.java (98%) rename generic/src/main/java/{com/dukescript => org/netbeans/html}/presenters/spi/ProtoPresenterBuilder.java (99%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/ValueOfTest.java (90%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/test/CallbackTest.java (97%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/test/Case.java (98%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/test/Counter.java (89%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/test/GenericTest.java (98%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/test/SynchronizedTest.java (89%) rename generic/src/test/java/{com/dukescript => org/netbeans/html}/presenters/spi/test/Testing.java (96%) rename renderer/src/main/java/{com/dukescript/presenters/renderer => org/netbeans/html/presenters/render}/AWT.java (97%) rename renderer/src/main/java/{com/dukescript/presenters/renderer => org/netbeans/html/presenters/render}/Cocoa.java (99%) rename renderer/src/main/java/{com/dukescript/presenters/renderer => org/netbeans/html/presenters/render}/GTK.java (99%) rename renderer/src/main/java/{com/dukescript/presenters/renderer => org/netbeans/html/presenters/render}/JSC.java (99%) rename renderer/src/main/java/{com/dukescript/presenters/renderer => org/netbeans/html/presenters/render}/Show.java (98%) rename webkit/src/main/java/{com/dukescript => org/netbeans/html}/presenters/webkit/UnJarResources.java (98%) rename webkit/src/main/java/{com/dukescript => org/netbeans/html}/presenters/webkit/WebKitPresenter.java (99%) rename webkit/src/test/java/{com/dukescript => org/netbeans/html}/presenters/webkit/Case.java (98%) rename webkit/src/test/java/{com/dukescript => org/netbeans/html}/presenters/webkit/DynamicHTTP.java (99%) rename webkit/src/test/java/{com/dukescript => org/netbeans/html}/presenters/webkit/GtkJavaScriptTest.java (97%) rename webkit/src/test/java/{com/dukescript => org/netbeans/html}/presenters/webkit/GtkKnockoutTest.java (98%) rename webkit/src/test/resources/{com/dukescript => org/netbeans/html}/presenters/webkit/empty.html (100%) diff --git a/browser/src/main/java/com/dukescript/presenters/Browser.java b/browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java similarity index 99% rename from browser/src/main/java/com/dukescript/presenters/Browser.java rename to browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java index bc3c16b7..6270b32e 100644 --- a/browser/src/main/java/com/dukescript/presenters/Browser.java +++ b/browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters; +package org.netbeans.html.presenters.browser; -import com.dukescript.presenters.renderer.Show; +import org.netbeans.html.presenters.render.Show; import java.io.Closeable; import java.io.FileNotFoundException; import java.io.Flushable; @@ -26,7 +26,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.PrintStream; import java.io.Reader; import java.io.Writer; import java.net.URI; @@ -54,8 +53,8 @@ import org.glassfish.grizzly.http.util.HttpStatus; import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.boot.spi.Fn.Presenter; -import com.dukescript.presenters.spi.ProtoPresenter; -import com.dukescript.presenters.spi.ProtoPresenterBuilder; +import org.netbeans.html.presenters.spi.ProtoPresenter; +import org.netbeans.html.presenters.spi.ProtoPresenterBuilder; import org.openide.util.lookup.ServiceProvider; /** Browser based {@link Presenter}. It starts local server and diff --git a/browser/src/test/java/com/dukescript/presenters/BrowserTest.java b/browser/src/test/java/org/netbeans/html/presenters/browser/BrowserTest.java similarity index 98% rename from browser/src/test/java/com/dukescript/presenters/BrowserTest.java rename to browser/src/test/java/org/netbeans/html/presenters/browser/BrowserTest.java index d4d23ef4..6ca28b08 100644 --- a/browser/src/test/java/com/dukescript/presenters/BrowserTest.java +++ b/browser/src/test/java/org/netbeans/html/presenters/browser/BrowserTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters; +package org.netbeans.html.presenters.browser; import java.lang.annotation.Annotation; import java.lang.reflect.Method; diff --git a/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java b/browser/src/test/java/org/netbeans/html/presenters/browser/DynamicHTTP.java similarity index 99% rename from browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java rename to browser/src/test/java/org/netbeans/html/presenters/browser/DynamicHTTP.java index fe62bdb4..0ebbed5c 100644 --- a/browser/src/test/java/com/dukescript/presenters/DynamicHTTP.java +++ b/browser/src/test/java/org/netbeans/html/presenters/browser/DynamicHTTP.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters; +package org.netbeans.html.presenters.browser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/browser/src/test/java/com/dukescript/presenters/KOScript.java b/browser/src/test/java/org/netbeans/html/presenters/browser/KOScript.java similarity index 98% rename from browser/src/test/java/com/dukescript/presenters/KOScript.java rename to browser/src/test/java/org/netbeans/html/presenters/browser/KOScript.java index b6b724c5..937454d1 100644 --- a/browser/src/test/java/com/dukescript/presenters/KOScript.java +++ b/browser/src/test/java/org/netbeans/html/presenters/browser/KOScript.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters; +package org.netbeans.html.presenters.browser; import java.io.Closeable; import java.io.IOException; diff --git a/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java b/browser/src/test/java/org/netbeans/html/presenters/browser/KoBrowserTest.java similarity index 98% rename from browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java rename to browser/src/test/java/org/netbeans/html/presenters/browser/KoBrowserTest.java index 7bcf1236..241aeee9 100644 --- a/browser/src/test/java/com/dukescript/presenters/KoBrowserTest.java +++ b/browser/src/test/java/org/netbeans/html/presenters/browser/KoBrowserTest.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters; +package org.netbeans.html.presenters.browser; +import org.netbeans.html.presenters.browser.Browser; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; diff --git a/browser/src/test/java/com/dukescript/presenters/ServerTest.java b/browser/src/test/java/org/netbeans/html/presenters/browser/ServerTest.java similarity index 96% rename from browser/src/test/java/com/dukescript/presenters/ServerTest.java rename to browser/src/test/java/org/netbeans/html/presenters/browser/ServerTest.java index 417501fe..a127f3c6 100644 --- a/browser/src/test/java/com/dukescript/presenters/ServerTest.java +++ b/browser/src/test/java/org/netbeans/html/presenters/browser/ServerTest.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters; +package org.netbeans.html.presenters.browser; -import com.dukescript.presenters.renderer.Show; +import org.netbeans.html.presenters.browser.Browser; +import org.netbeans.html.presenters.render.Show; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; diff --git a/browser/src/test/resources/com/dukescript/presenters/empty.html b/browser/src/test/resources/org/netbeans/html/presenters/browser/empty.html similarity index 100% rename from browser/src/test/resources/com/dukescript/presenters/empty.html rename to browser/src/test/resources/org/netbeans/html/presenters/browser/empty.html diff --git a/browser/src/test/resources/com/dukescript/presenters/server.html b/browser/src/test/resources/org/netbeans/html/presenters/browser/server.html similarity index 100% rename from browser/src/test/resources/com/dukescript/presenters/server.html rename to browser/src/test/resources/org/netbeans/html/presenters/browser/server.html diff --git a/generic/pom.xml b/generic/pom.xml index 84a80937..53642951 100644 --- a/generic/pom.xml +++ b/generic/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-javadoc-plugin - com.dukescript.presenters.spi + org.netbeans.html.presenters.spi @@ -99,7 +99,6 @@ org.netbeans.html html4j-maven-plugin - ${project.version} test-classes diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java b/generic/src/main/java/org/netbeans/html/presenters/spi/Generic.java similarity index 99% rename from generic/src/main/java/com/dukescript/presenters/spi/Generic.java rename to generic/src/main/java/org/netbeans/html/presenters/spi/Generic.java index e61d189a..d96dafe9 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/Generic.java +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/Generic.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi; +package org.netbeans.html.presenters.spi; +import com.dukescript.api.strings.Texts; import java.io.Flushable; import java.io.IOException; import java.io.Reader; @@ -41,7 +42,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.netbeans.html.boot.spi.Fn; -import com.dukescript.api.strings.Texts; abstract class Generic implements Fn.Presenter, Fn.KeepAlive, Flushable { private String msg; diff --git a/generic/src/main/java/com/dukescript/presenters/spi/Level.java b/generic/src/main/java/org/netbeans/html/presenters/spi/Level.java similarity index 96% rename from generic/src/main/java/com/dukescript/presenters/spi/Level.java rename to generic/src/main/java/org/netbeans/html/presenters/spi/Level.java index 1781fbfb..f84c0e98 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/Level.java +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/Level.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi; +package org.netbeans.html.presenters.spi; /** Logging levels similar to java.util.logging ones. */ enum Level { diff --git a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java similarity index 98% rename from generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java rename to generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java index 8094620f..92c3bd74 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenter.java +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi; +package org.netbeans.html.presenters.spi; import java.io.Flushable; import org.netbeans.html.boot.spi.Fn; diff --git a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java similarity index 99% rename from generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java rename to generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java index 90f891c1..dc589043 100644 --- a/generic/src/main/java/com/dukescript/presenters/spi/ProtoPresenterBuilder.java +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi; +package org.netbeans.html.presenters.spi; import java.net.URL; import java.util.ArrayList; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java b/generic/src/test/java/org/netbeans/html/presenters/spi/ValueOfTest.java similarity index 90% rename from generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/ValueOfTest.java index ef914f00..8f0338c4 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/ValueOfTest.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/ValueOfTest.java @@ -16,8 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi; +package org.netbeans.html.presenters.spi; +import org.netbeans.html.presenters.spi.Level; +import org.netbeans.html.presenters.spi.Generic; +import org.netbeans.html.presenters.spi.ProtoPresenterBuilder; import java.net.URL; import static org.testng.Assert.*; import org.testng.annotations.BeforeMethod; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java b/generic/src/test/java/org/netbeans/html/presenters/spi/test/CallbackTest.java similarity index 97% rename from generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/test/CallbackTest.java index b9d368a2..cdf03418 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/CallbackTest.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/test/CallbackTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi.test; +package org.netbeans.html.presenters.spi.test; import java.lang.reflect.Method; import java.util.logging.Level; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java b/generic/src/test/java/org/netbeans/html/presenters/spi/test/Case.java similarity index 98% rename from generic/src/test/java/com/dukescript/presenters/spi/test/Case.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/test/Case.java index 032ecca6..c3bc60d0 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/Case.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/test/Case.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi.test; +package org.netbeans.html.presenters.spi.test; import java.io.Closeable; import java.io.IOException; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java b/generic/src/test/java/org/netbeans/html/presenters/spi/test/Counter.java similarity index 89% rename from generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/test/Counter.java index 4146b545..75a4a973 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/Counter.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/test/Counter.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi.test; +package org.netbeans.html.presenters.spi.test; import net.java.html.js.JavaScriptBody; @@ -36,7 +36,7 @@ public static final void registerCounter() { @JavaScriptBody(args = {}, javacall = true, body = "if (!this.counter) {\n" - + " this.counter = function() { return @com.dukescript.presenters.spi.test.Counter::count()(); };\n" + + " this.counter = function() { return @org.netbeans.html.presenters.spi.test.Counter::count()(); };\n" + " return true;\n" + "} else {\n" + " return false;\n" diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java b/generic/src/test/java/org/netbeans/html/presenters/spi/test/GenericTest.java similarity index 98% rename from generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/test/GenericTest.java index b7afb242..180b5389 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/GenericTest.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/test/GenericTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi.test; +package org.netbeans.html.presenters.spi.test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java b/generic/src/test/java/org/netbeans/html/presenters/spi/test/SynchronizedTest.java similarity index 89% rename from generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/test/SynchronizedTest.java index 9714bacc..fe7b3cb3 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/SynchronizedTest.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/test/SynchronizedTest.java @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi.test; +package org.netbeans.html.presenters.spi.test; -import static com.dukescript.presenters.spi.test.GenericTest.createTests; +import static org.netbeans.html.presenters.spi.test.GenericTest.createTests; import org.testng.annotations.Factory; public class SynchronizedTest { diff --git a/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java b/generic/src/test/java/org/netbeans/html/presenters/spi/test/Testing.java similarity index 96% rename from generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java rename to generic/src/test/java/org/netbeans/html/presenters/spi/test/Testing.java index db9b5999..887b4353 100644 --- a/generic/src/test/java/com/dukescript/presenters/spi/test/Testing.java +++ b/generic/src/test/java/org/netbeans/html/presenters/spi/test/Testing.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.spi.test; +package org.netbeans.html.presenters.spi.test; import java.net.URL; import java.util.concurrent.Executor; @@ -27,8 +27,8 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; -import com.dukescript.presenters.spi.ProtoPresenter; -import com.dukescript.presenters.spi.ProtoPresenterBuilder; +import org.netbeans.html.presenters.spi.ProtoPresenter; +import org.netbeans.html.presenters.spi.ProtoPresenterBuilder; class Testing { static final Logger LOG = Logger.getLogger(Testing.class.getName()); diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java b/renderer/src/main/java/org/netbeans/html/presenters/render/AWT.java similarity index 97% rename from renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java rename to renderer/src/main/java/org/netbeans/html/presenters/render/AWT.java index 403e4e8e..630cd3c3 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/AWT.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/AWT.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.renderer; +package org.netbeans.html.presenters.render; import com.sun.jna.Pointer; import java.io.IOException; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java b/renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java similarity index 99% rename from renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java rename to renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java index 0e5156b0..57db64ce 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/Cocoa.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.renderer; +package org.netbeans.html.presenters.render; import com.sun.jna.Callback; import com.sun.jna.CallbackThreadInitializer; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java b/renderer/src/main/java/org/netbeans/html/presenters/render/GTK.java similarity index 99% rename from renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java rename to renderer/src/main/java/org/netbeans/html/presenters/render/GTK.java index c9e3a510..21df1065 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/GTK.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/GTK.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.renderer; +package org.netbeans.html.presenters.render; import com.sun.jna.Callback; import com.sun.jna.Library; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java b/renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java similarity index 99% rename from renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java rename to renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java index 484c853b..d13f1069 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/JSC.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.renderer; +package org.netbeans.html.presenters.render; import com.sun.jna.Callback; import com.sun.jna.Library; diff --git a/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java b/renderer/src/main/java/org/netbeans/html/presenters/render/Show.java similarity index 98% rename from renderer/src/main/java/com/dukescript/presenters/renderer/Show.java rename to renderer/src/main/java/org/netbeans/html/presenters/render/Show.java index 9d43091d..47d510c4 100644 --- a/renderer/src/main/java/com/dukescript/presenters/renderer/Show.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/Show.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.renderer; +package org.netbeans.html.presenters.render; import com.sun.jna.Pointer; import java.io.IOException; diff --git a/webkit/pom.xml b/webkit/pom.xml index 0f5baca0..834f99de 100644 --- a/webkit/pom.xml +++ b/webkit/pom.xml @@ -51,7 +51,7 @@ maven-javadoc-plugin false - com.dukescript.presenters + org.netbeans.html.presenters.webkit diff --git a/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java b/webkit/src/main/java/org/netbeans/html/presenters/webkit/UnJarResources.java similarity index 98% rename from webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java rename to webkit/src/main/java/org/netbeans/html/presenters/webkit/UnJarResources.java index 59bf56b9..fb001139 100644 --- a/webkit/src/main/java/com/dukescript/presenters/webkit/UnJarResources.java +++ b/webkit/src/main/java/org/netbeans/html/presenters/webkit/UnJarResources.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.webkit; +package org.netbeans.html.presenters.webkit; import java.io.File; import java.io.IOException; diff --git a/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java b/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java similarity index 99% rename from webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java rename to webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java index c87ecb23..050bb00e 100644 --- a/webkit/src/main/java/com/dukescript/presenters/webkit/WebKitPresenter.java +++ b/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.webkit; +package org.netbeans.html.presenters.webkit; -import com.dukescript.presenters.renderer.JSC; -import com.dukescript.presenters.renderer.Show; +import org.netbeans.html.presenters.render.JSC; +import org.netbeans.html.presenters.render.Show; import com.sun.jna.Callback; import com.sun.jna.Memory; import com.sun.jna.Native; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/Case.java similarity index 98% rename from webkit/src/test/java/com/dukescript/presenters/webkit/Case.java rename to webkit/src/test/java/org/netbeans/html/presenters/webkit/Case.java index a90e9a09..705e0208 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/Case.java +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/Case.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.webkit; +package org.netbeans.html.presenters.webkit; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/DynamicHTTP.java similarity index 99% rename from webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java rename to webkit/src/test/java/org/netbeans/html/presenters/webkit/DynamicHTTP.java index d9fbd0d0..e69c3212 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/DynamicHTTP.java +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/DynamicHTTP.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.webkit; +package org.netbeans.html.presenters.webkit; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java similarity index 97% rename from webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java rename to webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java index b7fc7dd9..a81520af 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkJavaScriptTest.java +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.webkit; +package org.netbeans.html.presenters.webkit; +import org.netbeans.html.presenters.webkit.WebKitPresenter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; diff --git a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java similarity index 98% rename from webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java rename to webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java index 4dbe0d29..a2929f95 100644 --- a/webkit/src/test/java/com/dukescript/presenters/webkit/GtkKnockoutTest.java +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package com.dukescript.presenters.webkit; +package org.netbeans.html.presenters.webkit; +import org.netbeans.html.presenters.webkit.WebKitPresenter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; diff --git a/webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html b/webkit/src/test/resources/org/netbeans/html/presenters/webkit/empty.html similarity index 100% rename from webkit/src/test/resources/com/dukescript/presenters/webkit/empty.html rename to webkit/src/test/resources/org/netbeans/html/presenters/webkit/empty.html From 3946a8f96c13b9ec2e7e05bbb229b7bb76886826 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Mon, 30 Sep 2019 07:23:46 +0200 Subject: [PATCH 08/16] Polishing the Javadoc a bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e18f49d4..870f718f 100644 --- a/pom.xml +++ b/pom.xml @@ -107,7 +107,7 @@ ${publicPackages} false -org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context.impl:org.netbeans.html.equinox.*:org.netbeans.html.geo.impl:org.netbeans.html.json.impl:org.netbeans.html.sound.impl:org.netbeans.html.ko.*:org.netbeans.html.ko4j:org.netbeans.html.mojo:org.netbeans.html.wstyrus:net.java.html.js.tests:net.java.html.json.tests:org.netbeans.html.xhr4j +org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context.impl:org.netbeans.html.equinox.*:org.netbeans.html.geo.impl:org.netbeans.html.json.impl:org.netbeans.html.sound.impl:org.netbeans.html.ko.*:org.netbeans.html.ko4j:org.netbeans.html.mojo:org.netbeans.html.wstyrus:net.java.html.js.tests:net.java.html.json.tests:org.netbeans.html.xhr4j:java.lang @@ -144,7 +144,7 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. org.apidesign.javadoc codesnippet-doclet - 0.23 + 0.31 -snippetpath boot-fx/src/test From f86b10b32bb690a60898b2131398c7390998d0d3 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Mon, 30 Sep 2019 19:32:31 +0200 Subject: [PATCH 09/16] Improving Javadoc --- .../html/presenters/browser/Browser.java | 56 +++++++++++-------- .../html/presenters/browser/package-info.java | 24 ++++++++ .../html/presenters/spi/ProtoPresenter.java | 3 +- .../presenters/spi/ProtoPresenterBuilder.java | 8 +-- .../html/presenters/spi/package-info.java | 24 ++++++++ .../java/net/java/html/json/Property.java | 3 +- .../netbeans/html/json/spi/Technology.java | 2 +- pom.xml | 7 +++ .../html/presenters/render/package-info.java | 24 ++++++++ .../presenters/webkit/WebKitPresenter.java | 15 ++++- .../html/presenters/webkit/package-info.java | 24 ++++++++ .../presenters/webkit/GtkJavaScriptTest.java | 23 ++++---- 12 files changed, 170 insertions(+), 43 deletions(-) create mode 100644 browser/src/main/java/org/netbeans/html/presenters/browser/package-info.java create mode 100644 generic/src/main/java/org/netbeans/html/presenters/spi/package-info.java create mode 100644 renderer/src/main/java/org/netbeans/html/presenters/render/package-info.java create mode 100644 webkit/src/main/java/org/netbeans/html/presenters/webkit/package-info.java diff --git a/browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java b/browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java index 6270b32e..748625c6 100644 --- a/browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java +++ b/browser/src/main/java/org/netbeans/html/presenters/browser/Browser.java @@ -58,30 +58,13 @@ import org.openide.util.lookup.ServiceProvider; /** Browser based {@link Presenter}. It starts local server and - * launches browser that connects to it. The actual browser to - * be launched can be influenced by value of - * com.dukescript.presenters.browser property. - * It can have following values: - *
    - *
  • GTK - use Gtk WebKit implementation. Requires presence of - * appropriate native libraries
  • - *
  • AWT - use {@link java.awt.Desktop#browse(java.net.URI)} to - * launch a browser
  • - *
  • NONE - just launches the server, useful together with - * com.dukescript.presenters.browserPort property that - * can specify a fixed port to open the server at - *
  • - *
  • any other value is interpreted as a command which is then - * launched on a command line with one parameter - the URL to connect to
  • - *
- * If the property is not specified the system tries GTK mode first, - * followed by AWT and then tries to execute xdg-open - * (default LINUX command to launch a browser from a shell script). + * launches browser that connects to it. Use {@link Browser.Config} to + * configure the actual browser to be started. *

* To use this presenter specify following dependency: *

  * <dependency>
- *   <groupId>com.dukescript.presenters</groupId>
+ *   <groupId>org.netbeans.html.browser</groupId>
  *   <artifactId>browser</artifactId>
  *   <version>1.x</version>
  * </dependency>
@@ -97,11 +80,37 @@ public final class Browser implements Fn.Presenter, Fn.KeepAlive, Flushable,
     private Runnable onPageLoad;
     private Command current;
     private final Config config;
-    
+
+    /** Default constructor. Reads configuration from properties. The actual browser to
+     * be launched can be influenced by value of
+     * com.dukescript.presenters.browser property.
+     * It can have following values:
+     * 
    + *
  • GTK - use Gtk WebKit implementation. Requires presence of + * appropriate native libraries
  • + *
  • AWT - use {@link java.awt.Desktop#browse(java.net.URI)} to + * launch a browser
  • + *
  • NONE - just launches the server, useful together with + * com.dukescript.presenters.browserPort property that + * can specify a fixed port to open the server at + *
  • + *
  • any other value is interpreted as a command which is then + * launched on a command line with one parameter - the URL to connect to
  • + *
+ * If the property is not specified the system tries GTK mode first, + * followed by AWT and then tries to execute xdg-open + * (default LINUX command to launch a browser from a shell script). + * @throws Exception + */ public Browser() throws Exception { this(new Config()); } + /** + * Browser configured by provided config. + * + * @param config the configuration + */ public Browser(Config config) { this(findCalleeClassName(), config); } @@ -229,13 +238,16 @@ public final void displayPage(URL page, Runnable onPageLoad) { /** Parameters to configure {@link Browser}. * Create an instance and pass it - * to {@link Browser#Browser(com.dukescript.presenters.Browser.Config) } + * to {@link Browser#Browser(org.netbeans.html.presenters.browser.Browser.Config) } * constructor. */ public final static class Config { String browser; Integer port; + /** + * Default constructor. + */ public Config() { } diff --git a/browser/src/main/java/org/netbeans/html/presenters/browser/package-info.java b/browser/src/main/java/org/netbeans/html/presenters/browser/package-info.java new file mode 100644 index 00000000..188c86d4 --- /dev/null +++ b/browser/src/main/java/org/netbeans/html/presenters/browser/package-info.java @@ -0,0 +1,24 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Generic {@link org.netbeans.html.presenters.browser.Browser} presenter + * and its {@link org.netbeans.html.presenters.browser.Browser.Config configuration}. + */ +package org.netbeans.html.presenters.browser; diff --git a/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java index 92c3bd74..3203050e 100644 --- a/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenter.java @@ -20,8 +20,9 @@ import java.io.Flushable; import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.boot.spi.Fn.Presenter; -/** The prototypical presenter. An implementation of a {@link Fn.Presenter} based on +/** The prototypical presenter. An implementation of a {@link Presenter} based on * top of textual protocol transferred between JVM and JavaScript engines. Use * {@link ProtoPresenterBuilder#newBuilder()} to construct instance of this * interface. diff --git a/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java index dc589043..c510b868 100644 --- a/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/ProtoPresenterBuilder.java @@ -22,9 +22,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; -import org.netbeans.html.boot.spi.Fn; +import org.netbeans.html.boot.spi.Fn.Presenter; -/** The prototypical presenter builder. Builds a {@link Fn.Presenter} based on +/** The prototypical presenter builder. Builds a {@link Presenter} based on * top of textual protocol transferred between JVM and JavaScript engines. */ public final class ProtoPresenterBuilder { @@ -101,7 +101,7 @@ public interface Preparator { void prepare(OnPrepared onReady); } - /** Callback to make when {@link Preparator#prepare(OnPrepared)} is + /** Callback to make when {@link Preparator#prepare(org.netbeans.html.presenters.spi.ProtoPresenterBuilder.OnPrepared)} is * finished. */ public static abstract class OnPrepared { @@ -149,7 +149,7 @@ public ProtoPresenterBuilder app(String app) { } /** Interface to handle displaying of a URL. - * Register via {@link ProtoPresenterBuilder#displayer(org.netbeans.html.presenter.spi.ProtoPresenterBuilder.Displayer)}. + * Register via {@link ProtoPresenterBuilder#displayer(org.netbeans.html.presenters.spi.ProtoPresenterBuilder.Displayer)}. */ @FunctionalInterface public interface Displayer { diff --git a/generic/src/main/java/org/netbeans/html/presenters/spi/package-info.java b/generic/src/main/java/org/netbeans/html/presenters/spi/package-info.java new file mode 100644 index 00000000..29570792 --- /dev/null +++ b/generic/src/main/java/org/netbeans/html/presenters/spi/package-info.java @@ -0,0 +1,24 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * The {@link org.netbeans.html.presenters.spi.ProtoPresenter prototypical presenter} + * based on textual protocol between JavaScript VM and Java VM. + */ +package org.netbeans.html.presenters.spi; diff --git a/json/src/main/java/net/java/html/json/Property.java b/json/src/main/java/net/java/html/json/Property.java index 860154f1..2f8a346d 100644 --- a/json/src/main/java/net/java/html/json/Property.java +++ b/json/src/main/java/net/java/html/json/Property.java @@ -23,6 +23,7 @@ import java.lang.annotation.Target; import java.util.List; import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; import org.netbeans.html.json.spi.Technology; /** Represents a property in a class defined with {@link Model} annotation. @@ -63,7 +64,7 @@ * * Technologies may decide to represent such non-mutable * property in more effective way - for - * example Knockout Java Bindings technology (with {@link Contexts.Id id} "ko4j") + * example Knockout Java Bindings technology (with {@link Id id} "ko4j") * uses plain JavaScript value (number, string, array, boolean) rather * than classical observable. * diff --git a/json/src/main/java/org/netbeans/html/json/spi/Technology.java b/json/src/main/java/org/netbeans/html/json/spi/Technology.java index b138dbe1..7b7ed2f1 100644 --- a/json/src/main/java/org/netbeans/html/json/spi/Technology.java +++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java @@ -34,7 +34,7 @@ * implementation called ko4j. * * @param technology internal type that keeps internal data for each - * instance of {@linkplains Model model class}. + * instance of {@linkplain Model model class}. * * @author Jaroslav Tulach */ diff --git a/pom.xml b/pom.xml index 870f718f..28abe215 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,10 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. Service Provider APIs (not commonly interesting) org.netbeans.html.* + + Build Your Own Presenter! + org.netbeans.html.presenters.* + http://testng.org/javadocs/ @@ -150,9 +154,12 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. -snippetpath boot-fx/src/test -snippetpath boot-script/src/test -snippetpath json/src/test + -snippetpath webkit/src/test ${javadoc.allowjs} -hiddingannotation java.lang.Deprecated + true + true diff --git a/renderer/src/main/java/org/netbeans/html/presenters/render/package-info.java b/renderer/src/main/java/org/netbeans/html/presenters/render/package-info.java new file mode 100644 index 00000000..61b9222f --- /dev/null +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/package-info.java @@ -0,0 +1,24 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * {@link org.netbeans.html.presenters.render.Show Show} a browser + * component. + */ +package org.netbeans.html.presenters.render; diff --git a/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java b/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java index 050bb00e..fccc12de 100644 --- a/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java +++ b/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java @@ -42,6 +42,7 @@ import org.netbeans.html.boot.spi.Fn; import org.openide.util.lookup.ServiceProvider; import com.dukescript.api.strings.Texts; +import net.java.html.boot.BrowserBuilder; /** Displays using native WebKit component on Linux and Mac OS X. * Requires necessary native libraries to be installed. Uses GTK3 on @@ -62,11 +63,21 @@ public final class WebKitPresenter implements Fn.Presenter, Fn.KeepAlive, Execut private Pointer valueFalse; private String onPageApp; + /** Default constructor. Rather than dealing with this class directly, + * consider using it via {@link BrowserBuilder} API. + */ public WebKitPresenter() { this(false); } - - WebKitPresenter(boolean headless) { + + /** Visible or invisible presenter. This constructor allows one to + * launch the presenter in headless mode. + * + * {@codesnippet org.netbeans.html.presenters.webkit.GtkJavaScriptTest} + * + * @param headless {@code true} if the presenter shall run headless + */ + public WebKitPresenter(boolean headless) { shell = Show.open(this, new Runnable() { @Override public void run() { diff --git a/webkit/src/main/java/org/netbeans/html/presenters/webkit/package-info.java b/webkit/src/main/java/org/netbeans/html/presenters/webkit/package-info.java new file mode 100644 index 00000000..f40ef676 --- /dev/null +++ b/webkit/src/main/java/org/netbeans/html/presenters/webkit/package-info.java @@ -0,0 +1,24 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Native {@link org.netbeans.html.presenters.webkit.WebKitPresenter} for + * Linux and Mac OS X. + */ +package org.netbeans.html.presenters.webkit; diff --git a/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java index a81520af..777d0af6 100644 --- a/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkJavaScriptTest.java @@ -18,7 +18,6 @@ */ package org.netbeans.html.presenters.webkit; -import org.netbeans.html.presenters.webkit.WebKitPresenter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; @@ -42,16 +41,16 @@ public GtkJavaScriptTest() { } @Factory public static Object[] compatibilityTests() throws Exception { - final BrowserBuilder bb = BrowserBuilder.newBrowser(new WebKitPresenter(true)).loadClass(GtkJavaScriptTest.class). - loadPage("empty.html"). - invoke("initialized"); + Runnable onPageLoaded = GtkJavaScriptTest::initialized; - Executors.newSingleThreadExecutor().submit(new Runnable() { - @Override - public void run() { - bb.showAndWait(); - } - }); + // BEGIN: org.netbeans.html.presenters.webkit.GtkJavaScriptTest + final WebKitPresenter headlessPresenter = new WebKitPresenter(true); + final BrowserBuilder bb = BrowserBuilder.newBrowser(headlessPresenter). + loadFinished(onPageLoaded). + loadPage("empty.html"); + // END: org.netbeans.html.presenters.webkit.GtkJavaScriptTest + + Executors.newSingleThreadExecutor().submit(bb::showAndWait); List res = new ArrayList<>(); Class test = @@ -76,13 +75,13 @@ static synchronized Class loadClass() throws InterruptedException { return browserClass; } - public static synchronized void ready(Class browserCls) throws Exception { + public static synchronized void ready(Class browserCls) { browserClass = browserCls; browserPresenter = Fn.activePresenter(); GtkJavaScriptTest.class.notifyAll(); } - public static void initialized() throws Exception { + public static void initialized() { BrwsrCtx b1 = BrwsrCtx.findDefault(GtkJavaScriptTest.class); assertNotSame(b1, BrwsrCtx.EMPTY, "Browser context is not empty"); BrwsrCtx b2 = BrwsrCtx.findDefault(GtkJavaScriptTest.class); From dfb6f4477bd954cd8949bd5ccbcd5e8f61dd570f Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Mon, 30 Sep 2019 20:13:12 +0200 Subject: [PATCH 10/16] Packaging the JARs as OSGi bundles --- browser/pom.xml | 8 +++++++- generic/pom.xml | 9 ++++++++- pom.xml | 12 +++++++----- renderer/pom.xml | 15 ++++++++++++++- webkit/pom.xml | 8 +++++++- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/browser/pom.xml b/browser/pom.xml index 44f6b0cb..f053eabd 100644 --- a/browser/pom.xml +++ b/browser/pom.xml @@ -22,7 +22,7 @@ 4.0.0 browser - jar + bundle org.netbeans.html pom @@ -31,9 +31,15 @@ DukeScript Presenter for any Browser ${project.parent.basedir} + org.netbeans.html.presenters.browser + + + org.apache.felix + maven-bundle-plugin + org.apache.maven.plugins maven-compiler-plugin diff --git a/generic/pom.xml b/generic/pom.xml index 53642951..b59de107 100644 --- a/generic/pom.xml +++ b/generic/pom.xml @@ -29,10 +29,13 @@ generic DukeScript Generic Presenter - jar + bundle 2.0-SNAPSHOT ${project.parent.basedir} + org.netbeans.html.presenters.spi + + @@ -61,6 +64,10 @@ + + org.apache.felix + maven-bundle-plugin + org.apache.maven.plugins maven-compiler-plugin diff --git a/pom.xml b/pom.xml index 28abe215..d0e0f2b4 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.apache.netbeans netbeans-parent - 1 + 2 UTF-8 @@ -40,6 +40,8 @@ ${project.artifactId} none --allow-script-in-comments + ${publicPackages} + META-INF.services.* json @@ -296,7 +298,7 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. true - ${publicPackages},META-INF.services.*;-noimport:=true;-split-package:=first + ${publicPackages},${publicMetaInf};-noimport:=true;-split-package:=first ${bundleSymbolicName} @@ -512,7 +514,7 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. org.netbeans.tools sigtest-maven-plugin - 1.0 + 1.2 @@ -522,8 +524,8 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. - ${publicPackages} - 1.6 + ${sigtestPackages} + 1.6.1 diff --git a/renderer/pom.xml b/renderer/pom.xml index 4d4cb473..cb534b8f 100644 --- a/renderer/pom.xml +++ b/renderer/pom.xml @@ -27,13 +27,26 @@ 2.0-SNAPSHOT renderer - jar + bundle Desktop Browser Renderer ${project.parent.basedir} + org.netbeans.html.presenters.render + + + + org.apache.felix + maven-bundle-plugin + + + dependent + osgi.extender;resolution:=optional;cardinality:=multiple;resolution:=optional + + + org.apache.maven.plugins maven-javadoc-plugin diff --git a/webkit/pom.xml b/webkit/pom.xml index 834f99de..83fcb33e 100644 --- a/webkit/pom.xml +++ b/webkit/pom.xml @@ -28,13 +28,19 @@ webkit WebKit Presenter - jar + bundle none ${project.parent.basedir} + org.netbeans.html.presenters.webkit + + + org.apache.felix + maven-bundle-plugin + org.apache.maven.plugins maven-compiler-plugin From c206ac0fd867887838ce58699e6c4ecd56b919ac Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Mon, 30 Sep 2019 20:34:56 +0200 Subject: [PATCH 11/16] Select implementation from the correct package and inherit I/O --- .../main/java/org/netbeans/html/presenters/render/Show.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/renderer/src/main/java/org/netbeans/html/presenters/render/Show.java b/renderer/src/main/java/org/netbeans/html/presenters/render/Show.java index 47d510c4..149aba13 100644 --- a/renderer/src/main/java/org/netbeans/html/presenters/render/Show.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/Show.java @@ -43,7 +43,7 @@ public abstract class Show { */ public static void show(String impl, URI page) throws IOException { try { - Class c = Class.forName("com.dukescript.presenters.renderer." + impl); + Class c = Class.forName(Show.class.getPackage().getName() + '.' + impl); Show show = (Show) c.newInstance(); show.show(page); } catch (IOException ex) { @@ -57,7 +57,7 @@ public static void show(String impl, URI page) throws IOException { impl, page.toString() }; LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); - final Process process = Runtime.getRuntime().exec(cmdArr); + final Process process = new ProcessBuilder().inheritIO().command(cmdArr).start(); try { process.waitFor(); } catch (InterruptedException ex1) { From e289231f7df877a15203c52301993c9fcb3d83df Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Tue, 1 Oct 2019 05:04:08 +0200 Subject: [PATCH 12/16] Newer javadoc requires additionalOptions --- pom.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index d0e0f2b4..3da5ebc7 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.1.1 true ${publicPackages} @@ -152,15 +152,17 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. codesnippet-doclet 0.31 - + -snippetpath boot-fx/src/test -snippetpath boot-script/src/test -snippetpath json/src/test -snippetpath webkit/src/test ${javadoc.allowjs} -hiddingannotation java.lang.Deprecated - + + true From 0d251015d781c1f932a4d9944709e9da48b02601 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Tue, 1 Oct 2019 05:09:22 +0200 Subject: [PATCH 13/16] Presenter is null when used from BrowserTest --- .../java/org/netbeans/html/presenters/render/Cocoa.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java b/renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java index 57db64ce..297daf58 100644 --- a/renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/Cocoa.java @@ -148,7 +148,7 @@ public void execute(Runnable command) { } private void process() throws Exception { - Closeable c = Fn.activate(presenter); + Closeable c = presenter == null ? null : Fn.activate(presenter); try { for (;;) { Runnable r = QUEUE.poll(); @@ -158,7 +158,9 @@ private void process() throws Exception { r.run(); } } finally { - c.close(); + if (c != null) { + c.close(); + } } } From 542c9e935e200c4db35a924157cc345b0dbb263f Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Tue, 1 Oct 2019 05:40:28 +0200 Subject: [PATCH 14/16] Print out thread dump after 60s of inactivity --- .../presenters/webkit/GtkKnockoutTest.java | 2 +- .../html/presenters/webkit/Timeout.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 webkit/src/test/java/org/netbeans/html/presenters/webkit/Timeout.java diff --git a/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java index a2929f95..9650e8d0 100644 --- a/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/GtkKnockoutTest.java @@ -18,7 +18,6 @@ */ package org.netbeans.html.presenters.webkit; -import org.netbeans.html.presenters.webkit.WebKitPresenter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -53,6 +52,7 @@ public final class GtkKnockoutTest extends KnockoutTCK { private static Class browserClass; private static Fn.Presenter browserContext; + private static final Timeout WATCHER = new Timeout(60000); public GtkKnockoutTest() { } diff --git a/webkit/src/test/java/org/netbeans/html/presenters/webkit/Timeout.java b/webkit/src/test/java/org/netbeans/html/presenters/webkit/Timeout.java new file mode 100644 index 00000000..b69a062c --- /dev/null +++ b/webkit/src/test/java/org/netbeans/html/presenters/webkit/Timeout.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.html.presenters.webkit; + +import java.io.PrintStream; +import java.util.Map.Entry; + +final class Timeout implements Runnable { + private final long timeout; + private final Thread thread; + + Timeout(long timeout) { + this.timeout = timeout; + this.thread = new Thread(this, "Timeout Watcher"); + this.thread.setDaemon(true); + this.thread.start(); + } + + @Override + public void run() { + try { + Thread.sleep(timeout); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + System.err.println("Timeout in " + timeout + " ms"); + dumpThreads(System.err); + } + + private static void dumpThreads(PrintStream ps) { + for (Entry e : Thread.getAllStackTraces().entrySet()) { + ps.println("Thread " + e.getKey().getName() + ":"); + for (StackTraceElement t : e.getValue()) { + ps.println(" " + t); + } + } + ps.println(); + } +} From 2d6140d6abf18af0919eb5ca4b7998135135cc0c Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Tue, 1 Oct 2019 20:00:31 +0200 Subject: [PATCH 15/16] Use sigtestPackages to verify the compatibility and publicPackages to generate new .sigfile --- pom.xml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3da5ebc7..e9c8f4b9 100644 --- a/pom.xml +++ b/pom.xml @@ -519,14 +519,27 @@ org.netbeans.html.boot.impl:org.netbeans.html.boot.fx:org.netbeans.html.context. 1.2 + check - generate check + integration-test + + ${sigtestPackages} + + + + generate + + generate + + package + + ${publicPackages} + - ${sigtestPackages} 1.6.1 From cdde5d7ac7f9274d7b90f9787be49ad8018e0d10 Mon Sep 17 00:00:00 2001 From: Eppleton IT Date: Sat, 12 Oct 2019 19:57:18 +0200 Subject: [PATCH 16/16] Check the type of JSValue is object before calling JSValueIsObjectOfClass --- .../java/org/netbeans/html/presenters/render/JSC.java | 2 +- .../netbeans/html/presenters/webkit/WebKitPresenter.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java b/renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java index d13f1069..e9752d9c 100644 --- a/renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java +++ b/renderer/src/main/java/org/netbeans/html/presenters/render/JSC.java @@ -78,7 +78,7 @@ protected List getFieldOrder() { @param jsClass The JSClass to test against. @result true if value is an object and has jsClass in its class chain, otherwise false. */ - int JSValueIsObjectOfClass(Pointer ctx, Pointer value, Pointer jsClass); + boolean JSValueIsObjectOfClass(Pointer ctx, Pointer value, Pointer jsClass); Pointer JSContextGetGlobalObject(Pointer ctx); diff --git a/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java b/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java index fccc12de..7adf6e25 100644 --- a/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java +++ b/webkit/src/main/java/org/netbeans/html/presenters/webkit/WebKitPresenter.java @@ -376,8 +376,12 @@ final void jsContext(Pointer ctx) { } private boolean isJavaClazz(Pointer obj) { - final int ret = shell.jsc().JSValueIsObjectOfClass(ctx, obj, javaClazz); - return ret == 1; + final JSC jsc = shell.jsc(); + int type = jsc.JSValueGetType(ctx, obj); + if (type != 5) { + return false; + } + return jsc.JSValueIsObjectOfClass(ctx, obj, javaClazz); }