Letting BrwsrCtx invoke runnables correctly and reschedule them into the appropriate thread if necessary. Caching JavaScript objects in the hope to allow asynchronous execution of the JavaScript snippets. BrwsrCtxExec
authorJaroslav Tulach <jaroslav.tulach@netbeans.org>
Tue, 04 Mar 2014 17:09:22 +0100
branchBrwsrCtxExec
changeset 5747958c9c205d9
parent 572 3a4fbc4a0998
child 575 3e6c5f24b12d
Letting BrwsrCtx invoke runnables correctly and reschedule them into the appropriate thread if necessary. Caching JavaScript objects in the hope to allow asynchronous execution of the JavaScript snippets.
boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
boot/src/main/java/net/java/html/boot/BrowserBuilder.java
context/src/main/java/net/java/html/BrwsrCtx.java
context/src/main/java/org/apidesign/html/context/spi/Contexts.java
context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java
json/src/main/java/org/apidesign/html/json/spi/JSONCall.java
json/src/main/java/org/apidesign/html/json/spi/Proto.java
json/src/main/java/org/netbeans/html/json/impl/JSON.java
ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java
ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java
     1.1 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java	Tue Mar 04 12:28:27 2014 +0100
     1.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java	Tue Mar 04 17:09:22 2014 +0100
     1.3 @@ -43,6 +43,8 @@
     1.4  package org.netbeans.html.boot.fx;
     1.5  
     1.6  import java.io.BufferedReader;
     1.7 +import java.io.Closeable;
     1.8 +import java.io.IOException;
     1.9  import java.io.Reader;
    1.10  import java.net.URL;
    1.11  import java.util.ArrayList;
    1.12 @@ -234,11 +236,35 @@
    1.13          }
    1.14      }
    1.15      
    1.16 -    @Override public void execute(Runnable r) {
    1.17 +    @Override public void execute(final Runnable r) {
    1.18          if (Platform.isFxApplicationThread()) {
    1.19 -            r.run();
    1.20 +            Closeable c = Fn.activate(this);
    1.21 +            try {
    1.22 +                r.run();
    1.23 +            } finally {
    1.24 +                try {
    1.25 +                    c.close();
    1.26 +                } catch (IOException ex) {
    1.27 +                    // ignore
    1.28 +                }
    1.29 +            }                
    1.30          } else {
    1.31 -            Platform.runLater(r);
    1.32 +            class Wrap implements Runnable {
    1.33 +                @Override
    1.34 +                public void run() {
    1.35 +                    Closeable c = Fn.activate(AbstractFXPresenter.this);
    1.36 +                    try {
    1.37 +                        r.run();
    1.38 +                    } finally {
    1.39 +                        try {
    1.40 +                            c.close();
    1.41 +                        } catch (IOException ex) {
    1.42 +                            // ignore
    1.43 +                        }
    1.44 +                    }                
    1.45 +                }
    1.46 +            }
    1.47 +            Platform.runLater(new Wrap());
    1.48          }
    1.49      }
    1.50  
     2.1 --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java	Tue Mar 04 12:28:27 2014 +0100
     2.2 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java	Tue Mar 04 17:09:22 2014 +0100
     2.3 @@ -51,11 +51,13 @@
     2.4  import java.util.Collection;
     2.5  import java.util.Enumeration;
     2.6  import java.util.ServiceLoader;
     2.7 +import java.util.concurrent.Executor;
     2.8  import java.util.logging.Level;
     2.9  import java.util.logging.Logger;
    2.10  import net.java.html.BrwsrCtx;
    2.11  import net.java.html.js.JavaScriptBody;
    2.12  import org.apidesign.html.boot.spi.Fn;
    2.13 +import org.apidesign.html.context.spi.Contexts;
    2.14  import org.netbeans.html.boot.impl.FindResources;
    2.15  import org.netbeans.html.boot.impl.FnContext;
    2.16  import org.netbeans.html.boot.impl.FnUtils;
    2.17 @@ -246,7 +248,15 @@
    2.18                          if (browserClass != null) {
    2.19                              browserClass[0] = newClazz;
    2.20                          }
    2.21 -                        BrwsrCtx c = BrwsrCtx.findDefault(newClazz);
    2.22 +                        Contexts.Builder cb = Contexts.newBuilder();
    2.23 +                        if (!Contexts.fillInByProviders(newClazz, cb)) {
    2.24 +                            LOG.log(Level.WARNING, "Using empty technology for {0}", newClazz);
    2.25 +                        }
    2.26 +                        if (currentP instanceof Executor) {
    2.27 +                            cb.register(Executor.class, (Executor)currentP, 1000);
    2.28 +                        }
    2.29 +                        cb.register(Fn.Presenter.class, currentP, 1000);
    2.30 +                        BrwsrCtx c = cb.build();
    2.31                          c.execute(this);
    2.32                          return;
    2.33                      }
     3.1 --- a/context/src/main/java/net/java/html/BrwsrCtx.java	Tue Mar 04 12:28:27 2014 +0100
     3.2 +++ b/context/src/main/java/net/java/html/BrwsrCtx.java	Tue Mar 04 17:09:22 2014 +0100
     3.3 @@ -42,7 +42,7 @@
     3.4   */
     3.5  package net.java.html;
     3.6  
     3.7 -import java.util.ServiceLoader;
     3.8 +import java.util.concurrent.Executor;
     3.9  import java.util.logging.Logger;
    3.10  import org.netbeans.html.context.impl.CtxAccssr;
    3.11  import org.netbeans.html.context.impl.CtxImpl;
    3.12 @@ -88,7 +88,8 @@
    3.13      
    3.14      /** Seeks for the default context that is associated with the requesting
    3.15       * class. If no suitable context is found, a warning message is
    3.16 -     * printed and {@link #EMPTY} context is returned.
    3.17 +     * printed and {@link #EMPTY} context is returned. One can enter 
    3.18 +     * a context by calling {@link #execute(java.lang.Runnable)}.
    3.19       * 
    3.20       * @param requestor the class that makes the request
    3.21       * @return appropriate context for the request
    3.22 @@ -100,38 +101,7 @@
    3.23          }
    3.24          
    3.25          org.apidesign.html.context.spi.Contexts.Builder cb = Contexts.newBuilder();
    3.26 -        boolean found = false;
    3.27 -        
    3.28 -        ClassLoader l;
    3.29 -        try {
    3.30 -            l = requestor.getClassLoader();
    3.31 -        } catch (SecurityException ex) {
    3.32 -            l = null;
    3.33 -        }
    3.34 -        
    3.35 -        for (org.apidesign.html.context.spi.Contexts.Provider cp : ServiceLoader.load(
    3.36 -            org.apidesign.html.context.spi.Contexts.Provider.class, l
    3.37 -        )) {
    3.38 -            cp.fillContext(cb, requestor);
    3.39 -            found = true;
    3.40 -        }
    3.41 -        try {
    3.42 -            for (org.apidesign.html.context.spi.Contexts.Provider cp : ServiceLoader.load(org.apidesign.html.context.spi.Contexts.Provider.class, org.apidesign.html.context.spi.Contexts.Provider.class.getClassLoader())) {
    3.43 -                cp.fillContext(cb, requestor);
    3.44 -                found = true;
    3.45 -            }
    3.46 -        } catch (SecurityException ex) {
    3.47 -            if (!found) {
    3.48 -                throw ex;
    3.49 -            }
    3.50 -            // if we have some data from regular provides, go on
    3.51 -        }
    3.52 -        if (!found) {
    3.53 -            for (org.apidesign.html.context.spi.Contexts.Provider cp : ServiceLoader.load(org.apidesign.html.context.spi.Contexts.Provider.class)) {
    3.54 -                cp.fillContext(cb, requestor);
    3.55 -                found = true;
    3.56 -            }
    3.57 -        }
    3.58 +        boolean found = Contexts.fillInByProviders(requestor, cb);
    3.59          if (!found) {
    3.60              LOG.warning("No browser context found. Returning empty technology!");
    3.61              return EMPTY;
    3.62 @@ -140,21 +110,37 @@
    3.63      }
    3.64  
    3.65      /** Runs provided code in the context of this {@link BrwsrCtx}.
    3.66 -     * While the <code>exec</code> is running, the {@link #findDefault(java.lang.Class)}
    3.67 -     * method returns <code>this</code>. The provided code is executed
    3.68 -     * synchronously on the same thread; 
    3.69 -     * the call returns when <code>exec.run()</code> is over.
    3.70 +     * If there is an {@link Executor} {@link Contexts#find(net.java.html.BrwsrCtx, java.lang.Class)  registered in the context}
    3.71 +     * it is used to perform the given code. While the code <code>exec</code>
    3.72 +     * is running the value of {@link #findDefault(java.lang.Class)} returns
    3.73 +     * <code>this</code>. If the executor supports a single thread execution
    3.74 +     * policy, it may execute the runnable later (in such case this method
    3.75 +     * returns immediately). If the call to this method is done on the right
    3.76 +     * thread, the runnable should be executed synchronously.
    3.77       * 
    3.78       * @param exec the code to execute
    3.79       * @since 0.7.6
    3.80       */
    3.81 -    public final void execute(Runnable exec) {
    3.82 -        BrwsrCtx prev = CURRENT.get();
    3.83 -        try {
    3.84 -            CURRENT.set(this);
    3.85 -            exec.run();
    3.86 -        } finally {
    3.87 -            CURRENT.set(prev);
    3.88 +    public final void execute(final Runnable exec) {
    3.89 +        class Wrap implements Runnable {
    3.90 +            @Override
    3.91 +            public void run() {
    3.92 +                BrwsrCtx prev = CURRENT.get();
    3.93 +                try {
    3.94 +                    CURRENT.set(BrwsrCtx.this);
    3.95 +                    exec.run();
    3.96 +                } finally {
    3.97 +                    CURRENT.set(prev);
    3.98 +                }
    3.99 +            }
   3.100 +        }
   3.101 +        Wrap w = new Wrap();
   3.102 +        Executor runIn = Contexts.find(this, Executor.class);
   3.103 +        if (runIn == null) {
   3.104 +            w.run();
   3.105 +        } else {
   3.106 +            runIn.execute(w);
   3.107          }
   3.108      }
   3.109  }
   3.110 +
     4.1 --- a/context/src/main/java/org/apidesign/html/context/spi/Contexts.java	Tue Mar 04 12:28:27 2014 +0100
     4.2 +++ b/context/src/main/java/org/apidesign/html/context/spi/Contexts.java	Tue Mar 04 17:09:22 2014 +0100
     4.3 @@ -42,6 +42,7 @@
     4.4   */
     4.5  package org.apidesign.html.context.spi;
     4.6  
     4.7 +import java.util.ServiceLoader;
     4.8  import net.java.html.BrwsrCtx;
     4.9  import org.netbeans.html.context.impl.CtxImpl;
    4.10  
    4.11 @@ -77,6 +78,47 @@
    4.12          return CtxImpl.find(context, technology);
    4.13      }
    4.14  
    4.15 +    /** Seeks {@link ServiceLoader} for all registered instances of
    4.16 +     * {@link Provider} and asks them to {@link Provider#fillContext(org.apidesign.html.context.spi.Contexts.Builder, java.lang.Class) fill
    4.17 +     * the builder}.
    4.18 +     * 
    4.19 +     * @param requestor the application class for which to find the context
    4.20 +     * @param cb the context builder to register technologies into
    4.21 +     * @return <code>true</code>, if some instances of the provider were
    4.22 +     *    found, <code>false</code> otherwise
    4.23 +     * @since 0.7.6
    4.24 +     */
    4.25 +    public static boolean fillInByProviders(Class<?> requestor, Contexts.Builder cb) {
    4.26 +        boolean found = false;
    4.27 +        ClassLoader l;
    4.28 +        try {
    4.29 +            l = requestor.getClassLoader();
    4.30 +        } catch (SecurityException ex) {
    4.31 +            l = null;
    4.32 +        }
    4.33 +        for (Provider cp : ServiceLoader.load(Provider.class, l)) {
    4.34 +            cp.fillContext(cb, requestor);
    4.35 +            found = true;
    4.36 +        }
    4.37 +        try {
    4.38 +            for (Provider cp : ServiceLoader.load(Provider.class, Provider.class.getClassLoader())) {
    4.39 +                cp.fillContext(cb, requestor);
    4.40 +                found = true;
    4.41 +            }
    4.42 +        } catch (SecurityException ex) {
    4.43 +            if (!found) {
    4.44 +                throw ex;
    4.45 +            }
    4.46 +        }
    4.47 +        if (!found) {
    4.48 +            for (Provider cp : ServiceLoader.load(Provider.class)) {
    4.49 +                cp.fillContext(cb, requestor);
    4.50 +                found = true;
    4.51 +            }
    4.52 +        }
    4.53 +        return found;
    4.54 +    }
    4.55 +
    4.56      /** Implementors of various HTML technologies should
    4.57       * register their implementation via {@link java.util.ServiceProvider} so
    4.58       * {@link java.util.ServiceProvider} can find them, when their JARs are included
    4.59 @@ -144,4 +186,18 @@
    4.60              return impl.build();
    4.61          }
    4.62      }
    4.63 +    
    4.64 +    /** Injects a {@link BrwsrCtx} into a technology registered by
    4.65 +     * {@link Builder#register(java.lang.Class, java.lang.Object, int)} when
    4.66 +     * appropriate context is created.
    4.67 +     * 
    4.68 +     * @since 0.7.6
    4.69 +     */
    4.70 +    public interface CtxAware {
    4.71 +        /** Injects associated context into an technology.
    4.72 +         * 
    4.73 +         * @param ctx the context this technology is registed in
    4.74 +         */
    4.75 +        public void injectCtx(BrwsrCtx ctx);
    4.76 +    }
    4.77  }
     5.1 --- a/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java	Tue Mar 04 12:28:27 2014 +0100
     5.2 +++ b/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java	Tue Mar 04 17:09:22 2014 +0100
     5.3 @@ -46,6 +46,7 @@
     5.4  import java.util.Collections;
     5.5  import java.util.List;
     5.6  import net.java.html.BrwsrCtx;
     5.7 +import org.apidesign.html.context.spi.Contexts;
     5.8  
     5.9  /** Implementation detail. Holds list of technologies for particular
    5.10   * {@link BrwsrCtx}.
    5.11 @@ -75,8 +76,16 @@
    5.12  
    5.13      public BrwsrCtx build() {
    5.14          Collections.sort(techs);
    5.15 -        CtxImpl impl = new CtxImpl(Collections.unmodifiableList(techs));
    5.16 -        return CtxAccssr.getDefault().newContext(impl);
    5.17 +        final List<Bind<?>> arr = Collections.unmodifiableList(techs);
    5.18 +        CtxImpl impl = new CtxImpl(arr);
    5.19 +        BrwsrCtx ctx = CtxAccssr.getDefault().newContext(impl);
    5.20 +        for (Bind<?> b : arr) {
    5.21 +            if (b.impl instanceof Contexts.CtxAware) {
    5.22 +                Contexts.CtxAware aw = (Contexts.CtxAware)b.impl;
    5.23 +                aw.injectCtx(ctx);
    5.24 +            }
    5.25 +        }
    5.26 +        return ctx;
    5.27      }
    5.28  
    5.29      public <Tech> void register(Class<Tech> type, Tech impl, int priority) {
     6.1 --- a/json/src/main/java/org/apidesign/html/json/spi/JSONCall.java	Tue Mar 04 12:28:27 2014 +0100
     6.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/JSONCall.java	Tue Mar 04 17:09:22 2014 +0100
     6.3 @@ -45,6 +45,7 @@
     6.4  import java.io.IOException;
     6.5  import java.io.OutputStream;
     6.6  import net.java.html.BrwsrCtx;
     6.7 +import org.apidesign.html.context.spi.Contexts;
     6.8  import org.netbeans.html.json.impl.JSON;
     6.9  import org.netbeans.html.json.impl.RcvrJSON;
    6.10  
    6.11 @@ -123,7 +124,7 @@
    6.12      }
    6.13      
    6.14      private void dispatch(final RcvrJSON.MsgEvnt ev) {
    6.15 -        JSON.runInBrowser(ctx, new Runnable() {
    6.16 +        ctx.execute(new Runnable() {
    6.17              @Override
    6.18              public void run() {
    6.19                  ev.dispatch(whenDone);
     7.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Tue Mar 04 12:28:27 2014 +0100
     7.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Tue Mar 04 17:09:22 2014 +0100
     7.3 @@ -154,7 +154,7 @@
     7.4       * @param run the action to execute
     7.5       */
     7.6      public void runInBrowser(Runnable run) {
     7.7 -        JSON.runInBrowser(context, run);
     7.8 +        context.execute(run);
     7.9      }
    7.10  
    7.11      /** Initializes the provided collection with a content of the <code>array</code>.
     8.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Tue Mar 04 12:28:27 2014 +0100
     8.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Tue Mar 04 17:09:22 2014 +0100
     8.3 @@ -80,16 +80,6 @@
     8.4          return t == null ? EmptyTech.EMPTY : t;
     8.5      }
     8.6      
     8.7 -    public static void runInBrowser(final BrwsrCtx c, final Runnable runnable) {
     8.8 -        class Wrap implements Runnable {
     8.9 -            @Override
    8.10 -            public void run() {
    8.11 -                c.execute(runnable);
    8.12 -            }
    8.13 -        }
    8.14 -        findTechnology(c).runSafe(new Wrap());
    8.15 -    }
    8.16 -    
    8.17      public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
    8.18          Transfer t = findTransfer(c);
    8.19          t.extract(value, props, values);
     9.1 --- a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java	Tue Mar 04 12:28:27 2014 +0100
     9.2 +++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java	Tue Mar 04 17:09:22 2014 +0100
     9.3 @@ -53,6 +53,7 @@
     9.4  import java.net.URLConnection;
     9.5  import java.util.Map;
     9.6  import java.util.concurrent.Callable;
     9.7 +import java.util.concurrent.Executor;
     9.8  import java.util.concurrent.Executors;
     9.9  import net.java.html.BrwsrCtx;
    9.10  import net.java.html.boot.BrowserBuilder;
    9.11 @@ -143,7 +144,8 @@
    9.12              Object fx = cnstr.newInstance(browserContext);
    9.13              Contexts.Builder cb = Contexts.newBuilder().
    9.14                  register(Technology.class, (Technology)fx, 10).
    9.15 -                register(Transfer.class, (Transfer)fx, 10);
    9.16 +                register(Transfer.class, (Transfer)fx, 10).
    9.17 +                register(Executor.class, (Executor)browserContext, 10);
    9.18  //        if (fx.areWebSocketsSupported()) {
    9.19  //            cb.register(WSTransfer.class, fx, 10);
    9.20  //        }
    10.1 --- a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java	Tue Mar 04 12:28:27 2014 +0100
    10.2 +++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java	Tue Mar 04 17:09:22 2014 +0100
    10.3 @@ -54,6 +54,7 @@
    10.4  import java.util.ArrayList;
    10.5  import java.util.List;
    10.6  import java.util.Map;
    10.7 +import java.util.concurrent.Executor;
    10.8  import java.util.concurrent.Executors;
    10.9  import net.java.html.BrwsrCtx;
   10.10  import net.java.html.boot.BrowserBuilder;
   10.11 @@ -154,7 +155,10 @@
   10.12          Contexts.Builder cb = Contexts.newBuilder().
   10.13              register(Technology.class, ko.knockout(), 10).
   10.14              register(Transfer.class, tc, 10).
   10.15 -            register(WSTransfer.class, tc, 10);
   10.16 +            register(WSTransfer.class, tc, 10).
   10.17 +            register(Executor.class, (Executor)browserContext, 10).
   10.18 +            register(Fn.Presenter.class, (Fn.Presenter)browserContext, 10);
   10.19 +                
   10.20          return cb.build();
   10.21      }
   10.22  
    11.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java	Tue Mar 04 12:28:27 2014 +0100
    11.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java	Tue Mar 04 17:09:22 2014 +0100
    11.3 @@ -51,8 +51,10 @@
    11.4  import java.net.URL;
    11.5  import java.util.concurrent.Executor;
    11.6  import java.util.logging.Logger;
    11.7 +import net.java.html.BrwsrCtx;
    11.8  import net.java.html.js.JavaScriptBody;
    11.9  import org.apidesign.html.boot.spi.Fn;
   11.10 +import org.apidesign.html.context.spi.Contexts;
   11.11  import org.apidesign.html.json.spi.FunctionBinding;
   11.12  import org.apidesign.html.json.spi.JSONCall;
   11.13  import org.apidesign.html.json.spi.PropertyBinding;
   11.14 @@ -69,13 +71,19 @@
   11.15   */
   11.16  final class FXContext
   11.17  implements Technology.BatchInit<Object>, Technology.ValueMutated<Object>,
   11.18 -Transfer, WSTransfer<LoadWS> {
   11.19 +Transfer, WSTransfer<LoadWS>, Contexts.CtxAware {
   11.20      static final Logger LOG = Logger.getLogger(FXContext.class.getName());
   11.21      private static Boolean javaScriptEnabled;
   11.22 -    private final Fn.Presenter browserContext;
   11.23 +    private BrwsrCtx browserContext;
   11.24 +    private Object[] jsObjects;
   11.25 +    private int jsIndex;
   11.26  
   11.27      public FXContext(Fn.Presenter browserContext) {
   11.28 -        this.browserContext = browserContext;
   11.29 +        Contexts.Builder cb = Contexts.newBuilder();
   11.30 +        if (browserContext instanceof Executor) {
   11.31 +            cb.register(Executor.class, (Executor)browserContext, 1000);
   11.32 +        }
   11.33 +        this.browserContext = cb.build();
   11.34      }
   11.35      
   11.36      @JavaScriptBody(args = {}, body = "if (window) return true; else return false;")
   11.37 @@ -125,13 +133,24 @@
   11.38          for (int i = 0; i < funcNames.length; i++) {
   11.39              funcNames[i] = funcArr[i].getFunctionName();
   11.40          }
   11.41 -        Object ret = Knockout.wrapModel(model, 
   11.42 +        Object ret = getJSObject();
   11.43 +        Knockout.wrapModel(ret, model, 
   11.44              propNames, propReadOnly, propValues, propArr,
   11.45              funcNames, funcArr
   11.46          );
   11.47          return ret;
   11.48      }
   11.49      
   11.50 +    private Object getJSObject() {
   11.51 +        int len = 64;
   11.52 +        if (jsObjects != null && jsIndex < (len = jsObjects.length)) {
   11.53 +            return jsObjects[jsIndex++];
   11.54 +        }
   11.55 +        jsObjects = Knockout.allocJS(len * 2);
   11.56 +        jsIndex = 1;
   11.57 +        return jsObjects[0];
   11.58 +    }
   11.59 +    
   11.60      @Override
   11.61      public Object wrapModel(Object model) {
   11.62          throw new UnsupportedOperationException();
   11.63 @@ -213,26 +232,7 @@
   11.64  
   11.65      @Override
   11.66      public void runSafe(final Runnable r) {
   11.67 -        class Wrap implements Runnable {
   11.68 -            @Override public void run() {
   11.69 -                Closeable c = Fn.activate(browserContext);
   11.70 -                try {
   11.71 -                    r.run();
   11.72 -                } finally {
   11.73 -                    try {
   11.74 -                        c.close();
   11.75 -                    } catch (IOException ex) {
   11.76 -                        // cannot be thrown
   11.77 -                    }
   11.78 -                }
   11.79 -            }
   11.80 -        }
   11.81 -        Wrap w = new Wrap();
   11.82 -        if (browserContext instanceof Executor) {
   11.83 -            ((Executor)browserContext).execute(w);
   11.84 -        } else {
   11.85 -            w.run();
   11.86 -        }
   11.87 +        browserContext.execute(r);
   11.88      }
   11.89  
   11.90      @Override
   11.91 @@ -250,6 +250,11 @@
   11.92          socket.close();
   11.93      }
   11.94  
   11.95 +    @Override
   11.96 +    public void injectCtx(BrwsrCtx ctx) {
   11.97 +        browserContext = ctx;
   11.98 +    }
   11.99 +
  11.100      private static final class TrueFn extends Fn implements Fn.Presenter {
  11.101          @Override
  11.102          public Object invoke(Object thiz, Object... args) throws Exception {
    12.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Tue Mar 04 12:28:27 2014 +0100
    12.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Tue Mar 04 17:09:22 2014 +0100
    12.3 @@ -78,12 +78,18 @@
    12.4      @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);\n")
    12.5      native static void applyBindings(Object bindings);
    12.6      
    12.7 +    @JavaScriptBody(args = { "cnt" }, body = 
    12.8 +        "var arr = new Array(cnt);\n" +
    12.9 +        "for (var i = 0; i < cnt; i++) arr[i] = new Object();\n" +
   12.10 +        "return arr;\n"
   12.11 +    )
   12.12 +    native static Object[] allocJS(int cnt);
   12.13 +    
   12.14      @JavaScriptBody(
   12.15          javacall = true,
   12.16 -        args = {"model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
   12.17 -        body
   12.18 -        = "var ret = {};\n"
   12.19 -        + "ret['ko-fx.model'] = model;\n"
   12.20 +        args = {"ret", "model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
   12.21 +        body =
   12.22 +          "ret['ko-fx.model'] = model;\n"
   12.23          + "function koComputed(name, readOnly, value, prop) {\n"
   12.24          + "  function realGetter() {\n"
   12.25          + "    try {\n"
   12.26 @@ -126,10 +132,9 @@
   12.27          + "for (var i = 0; i < funcNames.length; i++) {\n"
   12.28          + "  koExpose(funcNames[i], funcArr[i]);\n"
   12.29          + "}\n"
   12.30 -        + "return ret;\n"
   12.31          )
   12.32 -    static native Object wrapModel(
   12.33 -        Object model,
   12.34 +    static native void wrapModel(
   12.35 +        Object ret, Object model,
   12.36          String[] propNames, boolean[] propReadOnly, Object propValues, PropertyBinding[] propArr,
   12.37          String[] funcNames, FunctionBinding[] funcArr
   12.38      );
    13.1 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java	Tue Mar 04 12:28:27 2014 +0100
    13.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java	Tue Mar 04 17:09:22 2014 +0100
    13.3 @@ -54,6 +54,7 @@
    13.4  import java.util.ArrayList;
    13.5  import java.util.List;
    13.6  import java.util.Map;
    13.7 +import java.util.concurrent.Executor;
    13.8  import java.util.concurrent.Executors;
    13.9  import net.java.html.BrwsrCtx;
   13.10  import net.java.html.boot.BrowserBuilder;
   13.11 @@ -160,7 +161,10 @@
   13.12          if (fx.areWebSocketsSupported()) {
   13.13              cb.register(WSTransfer.class, fx, 10);
   13.14          }
   13.15 -        return cb.build();
   13.16 +        cb.register(Executor.class, (Executor)browserContext, 10);
   13.17 +        cb.register(Fn.Presenter.class, browserContext, 10);
   13.18 +        BrwsrCtx ctx = cb.build();
   13.19 +        return ctx;
   13.20      }
   13.21  
   13.22      @Override