on 978645f help sign in
.travis.yml+24−40
since splitsnapshots
unifiedsplit
@@ -1,47 +1,31 @@
1−language: nix
1+sudo: required
2−nix: 2.2.2
2+services:
3+ - docker
3
4
4−jobs:
5+cache:
5− include:
6+ directories:
6− - stage: Build
7+ - .cabal
7− env: OS=linux GHCVER=ghc865
8+ - .ghc
8− - stage: Build
9− env: OS=linux GHCVER=ghc881
10− - env: OS=macos GHCVER=ghc865
11− os: osx
12− # Fix OSX, see https://travis-ci.community/t/cannot-use-nix-support-on-osx/2927
13− language: generic
14− before_install:
15− - curl https://nixos.org/nix/install | sh
16− - source /Users/travis/.nix-profile/etc/profile.d/nix.sh
17− - env: OS=macos GHCVER=ghc881
18− os: osx
19− # Fix OSX, see https://travis-ci.community/t/cannot-use-nix-support-on-osx/2927
20− language: generic
21− before_install:
22− - curl https://nixos.org/nix/install | sh
23− - source /Users/travis/.nix-profile/etc/profile.d/nix.sh
24
9
25−install:
10+env:
26−- sudo mkdir -p /etc/nix
11+# - CABALVER=1.24 GHCVER=7.10.3 OSVER=xenial
27−- echo "substituters = https://cache.nixos.org https://hydra.iohk.io" | sudo tee -a /etc/nix/nix.conf > /dev/null
12+ - CABALVER=1.24 GHCVER=8.0.1 OSVER=xenial
28−- echo "trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" | sudo tee -a /etc/nix/nix.conf > /dev/null
13+ - CABALVER=1.24 GHCVER=8.0.2 OSVER=xenial
14+ - CABALVER=1.24 GHCVER=7.10.3 OSVER=trusty
15+ - CABALVER=1.24 GHCVER=8.0.1 OSVER=trusty
16+ - CABALVER=1.24 GHCVER=8.0.2 OSVER=trusty
29
17
30−script:
18+before_install:
31−- nix-build --argstr haskellCompiler $GHCVER -A hsPkgs.jsaddle.components.library
19+ - docker build -t ghc -f travis/$OSVER.Dockerfile travis
32−- nix-build --argstr haskellCompiler $GHCVER -A hsPkgs.jsaddle-warp.components.library
33−- nix-build --argstr haskellCompiler $GHCVER -A hsPkgs.jsaddle-clib.components.library
34−- |
35− if [ "$OS" == "macos" ]; then
36− nix-build --argstr haskellCompiler $GHCVER -A hsPkgs.jsaddle-wkwebview.components.library
37− fi
38−# TODO try to find how to get working webkitgtk with nix on macOS
39−- |
40− if [ "$OS" != "macos" ]; then
41− nix-build --argstr haskellCompiler $GHCVER -A hsPkgs.jsaddle-webkit2gtk.components.library
42− fi
43
20
21+script:
22+ - docker run -v `pwd`:/build ghc
23+ sh -x -c "
24+ apt-get install -y cabal-install-$CABALVER ghc-$GHCVER &&
25+ export PATH=\$HOME/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:/opt/alex/3.1.7/bin:/opt/happy/1.19.5/bin:\$PATH &&
26+ ./travis/build.sh"
44notifications:
27notifications:
45 irc:
28 irc:
46 channels: "irc.freenode.net#ghcjs"
29 channels: "irc.freenode.net#ghcjs"
30+ skip_join: true
47− email: true
31+ email: true
README.md+14−34
@@ -13,8 +13,7 @@ import Control.Monad.IO.Class (MonadIO(..))
13import Control.Concurrent.MVar (takeMVar, putMVar, newEmptyMVar)
13import Control.Concurrent.MVar (takeMVar, putMVar, newEmptyMVar)
14import Control.Lens ((^.))
14import Control.Lens ((^.))
15import Language.Javascript.JSaddle
15import Language.Javascript.JSaddle
16− (jsg, js, js1, jss, fun, valToNumber, syncPoint)
16+ (run, jsg, js, js1, jss, fun, valToNumber, syncPoint)
17−import Language.Javascript.JSaddle.Warp (run)
18
17
19main = run 3709 $ do
18main = run 3709 $ do
20 doc <- jsg "document"
19 doc <- jsg "document"
@@ -80,7 +79,7 @@ main = run 3708 $ do
80 appendChild newParagraph text
79 appendChild newParagraph text
81 appendChild body (Just newParagraph)
80 appendChild body (Just newParagraph)
82 return ()
81 return ()
83−
82+
84 -- Make an exit button
83 -- Make an exit button
85 exitMVar <- liftIO newEmptyMVar
84 exitMVar <- liftIO newEmptyMVar
86 Just exit <- createElement doc (Just "span")
85 Just exit <- createElement doc (Just "span")
@@ -103,31 +102,15 @@ When compiled with GHC this code will run a Warp web server you can connect to a
103
102
104## How does it work on GHC
103## How does it work on GHC
105
104
106−There are a number of different JSaddle runners to choose from
105+As of version 0.5 it runs a small warp server that provides [index.html](https://github.com/ghcjs/jsaddle/blob/master/data/index.html) and
107−
106+[jsaddle.js](https://github.com/ghcjs/jsaddle/blob/master/data/jsaddle.js). The [jsaddle.js](https://github.com/ghcjs/jsaddle/blob/master/data/jsaddle.js) code connects to the server using WebSockets and processes JSON commands from the server and sends the result back over the same WebSocket.
108−* [jsaddle-warp](https://hackage.haskell.org/package/jsaddle-warp) - runs JSaddle in a warp server with a web browser connected to it.
109−* [jsaddle-webkit2gtk](https://hackage.haskell.org/package/jsaddle-webkit2gtk) - runs JSaddle in a WebKitGTK window.
110−* [jsaddle-wkwebview](https://hackage.haskell.org/package/jsaddle-wkwebview) - runs JSaddle in a WKWebView on iOS or macOS.
111−* [jsaddle-clib](https://hackage.haskell.org/package/jsaddle-clib) - C interface used to run JSaddle on Android using JNI.
112−
113−In all of these cases a web control or browser of some sort is used. An
114−[HTML file](./jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs#L24)
115−and a [small JavaScript command interpreter](./jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs#L340)
116−are loaded into it. Then the native GHC compiled JSaddle code runs. In order to interact with the browser
117−it sends commands to the JavaScript command interpreter. The mechanism for sending the commands differs
118−for the different runners, but they are always combined into batches and encoded in JSON.
119
107
120−Haskell callbacks from JavaScript are supported by sending JSON back from the command interpreter. These can be synchronous (blocking the
108+### Why use WebSockets?
121−JavaScript thread until the Haskell callback completes) or asynchronous.
122
109
123−### Why use a JavaScript command interpreter?
124−
125−Older versions of JSaddle relied on the WebKit1 interface to WebKitGTK and JavaScriptCore that is only supported in older versions of
126−WebKitGTK. With the newer WebKit2 interface this level of access is only available to WebKit Extensions.
110+Older versions relied on WebKitGTK and JavaScriptCore, but in WekKit2 the interface JSaddle used is only available to WebKit Extensions
111+so most of the benefits of this interface are lost (extensions are not in the same process as Gtk and RPC would have been needed).
127−The general advice for people migrating from WebKit1 to WebKit2 seems to be to use JavaScript.
112+The general advice for people migrating from WebKit1 to WebKit2 seems to be to use JavaScript. There is an interface in WebKit2
128−
113+to send JavaScript code from the host application, but it does not seem to provide any advantages over the WebSockets approach.
129−As a bonus we have been able to support a number of different platforms with the JavaScript command interpreter and it should
130−be easy to support more in the future.
131
114
132### JSVal
115### JSVal
133
116
@@ -137,8 +120,8 @@ The first five keys `[0..4]` are
137these values. Both the Haskell code and `jsaddle.js` can allocate new `JSVal` keys. The Haskell code allocates negative
120these values. Both the Haskell code and `jsaddle.js` can allocate new `JSVal` keys. The Haskell code allocates negative
138keys and `jsaddle.js` allocates positive keys to avoid clashing.
121keys and `jsaddle.js` allocates positive keys to avoid clashing.
139
122
140−The haskell code will not know the value to go with the key, but it will include it in commands to the server
123+The haskell code will not know the value of the to go with the key, but it will include it in commands to the server
141−that are sort of "hey call this function and put the result in the map with this key"). This allows for the
124+that are sort of "hey call this function and and put the result in the map with this key"). This allows for the
142lazy execution of the JSaddle code.
125lazy execution of the JSaddle code.
143
126
144A finalizer is added on the Haskell code and an asynchronous `FreeJSVal` command is automatically sent to `jsaddle.js`
127A finalizer is added on the Haskell code and an asynchronous `FreeJSVal` command is automatically sent to `jsaddle.js`
@@ -146,7 +129,7 @@ when the JSVal is no longer reachable. The `jsaddle.js` code then deletes the k
146
129
147### JSStrings
130### JSStrings
148
131
149−These are haskell `Text` type and so reside on the native side of the WebSocket. If you want to avoid transferring large string values over the WebSocket use `toJSVal` to convert it to a `JSVal`.
132+These are haskell `Text` type and so reside on the native side of the WebSocket. If you want to make avoid transferring large string values over the WebSocket use `toJSVal` to conevt it to a `JSVal`.
150
133
151### Lazy Execution
134### Lazy Execution
152
135
@@ -178,7 +161,7 @@ force all the pending asynchronous commands to be executed.
178It uses a handful of JS FFI calls to execute JavaScript functions indirectly. This indirection will be small compared to the overhead of the WebSockets approach (used when JSaddle is compiled with GHC), but it will be significant compared to hand crafted JS FFI calls.
161It uses a handful of JS FFI calls to execute JavaScript functions indirectly. This indirection will be small compared to the overhead of the WebSockets approach (used when JSaddle is compiled with GHC), but it will be significant compared to hand crafted JS FFI calls.
179
162
180For the best performance you may want to write both JS FFI and JSaddle wrappers
163For the best performance you may want to write both JS FFI and JSaddle wrappers
181−for your JavaScript code. This is the approach taken by `ghcjs-dom`.
164+For your JavaScript code. This is the approach taken by recent versions of `ghcjs-dom`.
182
165
183For instance here is `getElementById` from the `ghcjs-dom-jsffi` (used by `ghcjs-dom` when compiled with GHCJS)
166For instance here is `getElementById` from the `ghcjs-dom-jsffi` (used by `ghcjs-dom` when compiled with GHCJS)
184
167
@@ -213,10 +196,7 @@ JSaddle does not support exceptions well. When compiled with GHCJS an exception
213at the point the exception is thrown.
196at the point the exception is thrown.
214
197
215When using GHC the Haskell executions will probably continue for a while before the exception is received at all by the Haskell code
198When using GHC the Haskell executions will probably continue for a while before the exception is received at all by the Haskell code
216−(because the lazy execution of the JS code). The exception will be thrown when the next synchronous command is executed.
199+(because the lazy execution of the JS code). The exception will be thrown when the the next synchronous command is executed.
217When it reaches the synchronous command a haskell type JSException will be thrown (this may not be the thread that initiated the
200When it reaches the synchronous command a haskell type JSException will be thrown (this may not be the thread that initiated the
218command that caused the exception). You can use `syncPoint` and `syncAfter` to force the exception to be thrown.
201command that caused the exception). You can use `syncPoint` and `syncAfter` to force the exception to be thrown.
219There are `JSM` versions of `catch` and `bracket` that also include a `syncPoint` call.
202There are `JSM` versions of `catch` and `bracket` that also include a `syncPoint` call.
220−
221−## Building with Stack
222−The `jsaddle-webkit2gtk` runner can be difficult to build with stack. See [this issue](https://github.com/ghcjs/jsaddle/issues/38#issuecomment-331392995) if you get stuck.
cabal.project+35−4
@@ -1,10 +1,41 @@
1+flags: -overloaded-methods -overloaded-signals -overloaded-properties
2+
1packages:
3packages:
2 jsaddle/
4 jsaddle/
3 jsaddle-warp/
5 jsaddle-warp/
4 jsaddle-wkwebview/
6 jsaddle-wkwebview/
7+-- jsaddle-webkitgtk/
5 jsaddle-webkit2gtk/
8 jsaddle-webkit2gtk/
6− jsaddle-clib/
7−
8−constraints: haskell-gi-overloading == 0.0
9
9
10−allow-newer: websockets:random, splitmix:time, *:time, *:unix, *:process, stm:base
10+package gi-atk
11+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
12+package gi-cairo
13+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
14+package gi-gdk
15+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
16+package gi-gdkpixbuf
17+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
18+package gi-gio
19+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
20+package gi-glib
21+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
22+package gi-gobject
23+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
24+package gi-gtk
25+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
26+package gi-gtk-hs
27+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
28+package gi-gtkosxapplication
29+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
30+package gi-gtksource
31+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
32+package gi-javascriptcore
33+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
34+package gi-pango
35+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
36+package gi-soup
37+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
38+package gi-webkit
39+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
40+package gi-webkit2
41+ flags: -overloaded-methods -overloaded-signals -overloaded-properties
default.nixnew file+8
@@ -0,0 +1,8 @@
1+haskellPackages: {
2+ jsaddle = haskellPackages.callPackage ./jsaddle {};
3+ jsaddle-warp = haskellPackages.callPackage ./jsaddle-warp {};
4+ jsaddle-wkwebview = haskellPackages.callPackage ./jsaddle-wkwebview {};
5+ jsaddle-webkit2gtk = haskellPackages.callPackage ./jsaddle-webkit2gtk {};
6+ jsaddle-webkitgtk = haskellPackages.callPackage ./jsaddle-webkitgtk {};
7+ jsaddle-clib = haskellPackages.callPackage ./jsaddle-clib {};
8+}
jsaddle-clib/cbits/include/jsaddle.h−13
@@ -4,21 +4,8 @@
4typedef struct native_callbacks {
4typedef struct native_callbacks {
5 void (* jsaddleStart) ();
5 void (* jsaddleStart) ();
6 void (* jsaddleResult) (char *);
6 void (* jsaddleResult) (char *);
7− char * (* jsaddleSyncResult) (char *);
8 char * jsaddleJsData;
7 char * jsaddleJsData;
9 char * jsaddleHtmlData;
8 char * jsaddleHtmlData;
10} native_callbacks;
9} native_callbacks;
11
10
12−typedef struct app_callbacks {
13− void (* mainActivity_onCreate) ();
14− void (* mainActivity_onStart) ();
15− void (* mainActivity_onResume) ();
16− void (* mainActivity_onPause) ();
17− void (* mainActivity_onStop) ();
18− void (* mainActivity_onDestroy) ();
19− void (* mainActivity_onRestart) ();
20− void (* mainActivity_onNewIntent) (char *, char *);
21− void (* firebaseInstanceIdService_sendRegistrationToServer) (char *);
22−} app_callbacks;
23−
24#endif
11#endif
jsaddle-clib/default.nixnew file+9
@@ -0,0 +1,9 @@
1+{ mkDerivation, aeson, base, bytestring, jsaddle, stdenv }:
2+mkDerivation {
3+ pname = "jsaddle-clib";
4+ version = "0.8.2.0";
5+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
6+ libraryHaskellDepends = [ aeson base bytestring jsaddle ];
7+ description = "Interface for JavaScript that works with GHCJS and GHC";
8+ license = stdenv.lib.licenses.mit;
9+}
jsaddle-clib/jsaddle-clib.cabal+4−7
@@ -1,5 +1,5 @@
1name: jsaddle-clib
1name: jsaddle-clib
2−version: 0.9.8.0
2+version: 0.8.3.0
3cabal-version: >=1.10
3cabal-version: >=1.10
4build-type: Simple
4build-type: Simple
5license: MIT
5license: MIT
@@ -21,19 +21,16 @@ library
21 Language.Javascript.JSaddle.CLib
21 Language.Javascript.JSaddle.CLib
22 Language.Javascript.JSaddle.CLib.Internal
22 Language.Javascript.JSaddle.CLib.Internal
23 build-depends:
23 build-depends:
24− aeson >=0.8.0.2 && <1.6,
24+ aeson >=0.8.0.2 && <1.2,
25 base <5,
25 base <5,
26− base-compat >=0.9.0 && <0.12,
27 bytestring >=0.10.6.0 && <0.11,
26 bytestring >=0.10.6.0 && <0.11,
28− jsaddle >= 0.9.0.0 && <0.10,
27+ jsaddle >= 0.8.0.0 && <0.9
29− data-default,
30− text
31 default-language: Haskell2010
28 default-language: Haskell2010
32 hs-source-dirs: src
29 hs-source-dirs: src
33 Include-dirs: cbits/include
30 Include-dirs: cbits/include
34 Includes: jsaddle.h
31 Includes: jsaddle.h
35 Install-includes: jsaddle.h
32 Install-includes: jsaddle.h
36− ghc-options: -ferror-spans -Wall
33+ ghc-options: -ferror-spans -Wall -Werror
37 if impl(ghcjs)
34 if impl(ghcjs)
38 hs-source-dirs: src-ghcjs
35 hs-source-dirs: src-ghcjs
39 else
36 else
jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hs+8−90
@@ -2,29 +2,19 @@
2module Language.Javascript.JSaddle.CLib
2module Language.Javascript.JSaddle.CLib
3 ( jsaddleInit
3 ( jsaddleInit
4 , NativeCallbacks (..)
4 , NativeCallbacks (..)
5− , AppCallbacks (..)
6− , AppConfig (..)
7− , pokeAppConfig
8− , appConfigToAppCallbacks
9 ) where
5 ) where
10
6
11−import Prelude ()
12−import Prelude.Compat
13−
14import Control.Monad (void)
7import Control.Monad (void)
15import Control.Concurrent (forkIO)
8import Control.Concurrent (forkIO)
16
9
17−import Data.Aeson (encode, decode)
10+import Data.Monoid ((<>))
18import Data.ByteString (useAsCString, packCString)
11import Data.ByteString (useAsCString, packCString)
19−import Data.ByteString.Char8 (unpack)
20import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
12import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
21−import Data.Default (def, Default)
13+import Data.ByteString.Char8 (unpack)
22−import Data.Text (Text)
14+import Data.Aeson (encode, decode)
23−import qualified Data.Text.Encoding as T
24
15
25import Foreign.C.String (CString, newCString)
16import Foreign.C.String (CString, newCString)
26import Foreign.Ptr (FunPtr, Ptr)
17import Foreign.Ptr (FunPtr, Ptr)
27−import Foreign.Storable (poke)
28import Foreign.Marshal.Utils (new)
18import Foreign.Marshal.Utils (new)
29
19
30import Language.Javascript.JSaddle (JSM)
20import Language.Javascript.JSaddle (JSM)
@@ -42,105 +32,33 @@ foreign import ccall safe "wrapper"
42foreign import ccall safe "wrapper"
32foreign import ccall safe "wrapper"
43 wrapMessageCallback :: (CString -> IO ()) -> IO (FunPtr (CString -> IO ()))
33 wrapMessageCallback :: (CString -> IO ()) -> IO (FunPtr (CString -> IO ()))
44
34
45−foreign import ccall safe "wrapper"
46− wrapMessageCallback2 :: (CString -> CString -> IO ()) -> IO (FunPtr (CString -> CString -> IO ()))
47−
48−foreign import ccall safe "wrapper"
49− wrapSyncCallback :: (CString -> IO CString) -> IO (FunPtr (CString -> IO CString))
50−
51jsaddleInit :: JSM () -> FunPtr (CString -> IO ()) -> IO (Ptr NativeCallbacks)
35jsaddleInit :: JSM () -> FunPtr (CString -> IO ()) -> IO (Ptr NativeCallbacks)
52jsaddleInit jsm evaluateJavascriptAsyncPtr = do
36jsaddleInit jsm evaluateJavascriptAsyncPtr = do
53 let evaluateJavascriptAsync = mkCallback evaluateJavascriptAsyncPtr
37 let evaluateJavascriptAsync = mkCallback evaluateJavascriptAsyncPtr
54− (processResult, processSyncResult, start) <- runJavaScript (\batch ->
38+ (processResult, start) <- runJavaScript (\batch ->
55− useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");")
39+ useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $
56 evaluateJavascriptAsync) jsm
40 evaluateJavascriptAsync) jsm
57− jsaddleStartPtr <- wrapStartCallback $ void $ forkIO start
41+ jsaddleStartPtr <- wrapStartCallback $ void $ forkIO $ start
58 jsaddleResultPtr <- wrapMessageCallback $ \s -> do
42 jsaddleResultPtr <- wrapMessageCallback $ \s -> do
59 result <- decode . fromStrict <$> packCString s
43 result <- decode . fromStrict <$> packCString s
60 case result of
44 case result of
61 Nothing -> error $ "jsaddle message decode failed: " <> show result
45 Nothing -> error $ "jsaddle message decode failed: " <> show result
62 Just r -> processResult r
46 Just r -> processResult r
63− jsaddleSyncResultPtr <- wrapSyncCallback $ \s -> do
64− result <- decode . fromStrict <$> packCString s
65− case result of
66− Nothing -> error $ "jsaddle message decode failed: " <> show result
67− Just r -> newCString =<< unpack . toStrict . encode <$> processSyncResult r
68 jsaddleJsPtr <- newCString $ unpack $ toStrict jsaddleJs
47 jsaddleJsPtr <- newCString $ unpack $ toStrict jsaddleJs
69 jsaddleHtmlPtr <- newCString $ unpack $ toStrict indexHtml
48 jsaddleHtmlPtr <- newCString $ unpack $ toStrict indexHtml
70− new NativeCallbacks
49+ new $ NativeCallbacks
71 { _nativeCallbacks_jsaddleStart = jsaddleStartPtr
50 { _nativeCallbacks_jsaddleStart = jsaddleStartPtr
72 , _nativeCallbacks_jsaddleResult = jsaddleResultPtr
51 , _nativeCallbacks_jsaddleResult = jsaddleResultPtr
73− , _nativeCallbacks_jsaddleSyncResult = jsaddleSyncResultPtr
74 , _nativeCallbacks_jsaddleJsData = jsaddleJsPtr
52 , _nativeCallbacks_jsaddleJsData = jsaddleJsPtr
75 , _nativeCallbacks_jsaddleHtmlData = jsaddleHtmlPtr
53 , _nativeCallbacks_jsaddleHtmlData = jsaddleHtmlPtr
76 }
54 }
77
55
78−data AppConfig = AppConfig
79− { _appConfig_mainActivityOnCreate :: IO ()
80− , _appConfig_mainActivityOnStart :: IO ()
81− , _appConfig_mainActivityOnResume :: IO ()
82− , _appConfig_mainActivityOnPause :: IO ()
83− , _appConfig_mainActivityOnStop :: IO ()
84− , _appConfig_mainActivityOnDestroy :: IO ()
85− , _appConfig_mainActivityOnRestart :: IO ()
86− , _appConfig_mainActivityOnNewIntent :: (Text -> Text -> IO ())
87− , _appConfig_firebaseInstanceIdServiceSendRegistrationToServer :: Text -> IO ()
88− }
89−
90−instance Default AppConfig where
91− def = AppConfig
92− { _appConfig_mainActivityOnCreate = return ()
93− , _appConfig_mainActivityOnStart = return ()
94− , _appConfig_mainActivityOnResume = return ()
95− , _appConfig_mainActivityOnPause = return ()
96− , _appConfig_mainActivityOnStop = return ()
97− , _appConfig_mainActivityOnDestroy = return ()
98− , _appConfig_mainActivityOnRestart = return ()
99− , _appConfig_mainActivityOnNewIntent = \_ _ -> return ()
100− , _appConfig_firebaseInstanceIdServiceSendRegistrationToServer = \_ -> return ()
101− }
102−
103−appConfigToAppCallbacks :: AppConfig -> IO AppCallbacks
104−appConfigToAppCallbacks c = do
105− create <- wrapStartCallback $ _appConfig_mainActivityOnCreate c
106− start <- wrapStartCallback $ _appConfig_mainActivityOnStart c
107− resume <- wrapStartCallback $ _appConfig_mainActivityOnResume c
108− pause <- wrapStartCallback $ _appConfig_mainActivityOnPause c
109− stop <- wrapStartCallback $ _appConfig_mainActivityOnStop c
110− destroy <- wrapStartCallback $ _appConfig_mainActivityOnDestroy c
111− restart <- wrapStartCallback $ _appConfig_mainActivityOnRestart c
112− newIntent <- wrapMessageCallback2 $ \intentAction intentData -> do
113− intentAction' <- fromUtf8CString intentAction
114− intentData' <- fromUtf8CString intentData
115− _appConfig_mainActivityOnNewIntent c intentAction' intentData'
116− firebaseRegPtr <- wrapMessageCallback $ \token -> do
117− token' <- fromUtf8CString token
118− _appConfig_firebaseInstanceIdServiceSendRegistrationToServer c token'
119− return $ AppCallbacks
120− { _appCallbacks_mainActivity_onCreate = create
121− , _appCallbacks_mainActivity_onStart = start
122− , _appCallbacks_mainActivity_onResume = resume
123− , _appCallbacks_mainActivity_onPause = pause
124− , _appCallbacks_mainActivity_onStop = stop
125− , _appCallbacks_mainActivity_onDestroy = destroy
126− , _appCallbacks_mainActivity_onRestart = restart
127− , _appCallbacks_mainActivity_onNewIntent = newIntent
128− , _appCallbacks_firebaseInstanceIdService_sendRegistrationToServer = firebaseRegPtr
129− }
130−
131−fromUtf8CString :: CString -> IO Text
132−fromUtf8CString = fmap T.decodeUtf8 . packCString
133−
134−pokeAppConfig :: Ptr AppCallbacks -> AppConfig -> IO ()
135−pokeAppConfig ptr cfg = poke ptr =<< appConfigToAppCallbacks cfg
136−
137jsaddleJs :: ByteString
56jsaddleJs :: ByteString
138jsaddleJs = ghcjsHelpers <> "\
57jsaddleJs = ghcjsHelpers <> "\
139 \runJSaddleBatch = (function() {\n\
58 \runJSaddleBatch = (function() {\n\
140 \ " <> initState <> "\n\
59 \ " <> initState <> "\n\
141 \ return function(batch) {\n\
60 \ return function(batch) {\n\
142− \ " <> runBatch (\a -> "jsaddle.postMessage(JSON.stringify(" <> a <> "));")
61+ \ " <> runBatch (\a -> "jsaddle.postMessage(JSON.stringify(" <> a <> "));") <> "\
143− (Just (\a -> "JSON.parse(jsaddle.syncMessage(JSON.stringify(" <> a <> ")))")) <> "\
144 \ };\n\
62 \ };\n\
145 \})();\n\
63 \})();\n\
146 \jsaddle.postReady();\n"
64 \jsaddle.postReady();\n"
jsaddle-clib/src/Language/Javascript/JSaddle/CLib/Internal.hsc−39
@@ -9,7 +9,6 @@ import Foreign.C
9data NativeCallbacks = NativeCallbacks
9data NativeCallbacks = NativeCallbacks
10 { _nativeCallbacks_jsaddleStart :: !(FunPtr (IO ())) -- void (jsaddleStart *)()
10 { _nativeCallbacks_jsaddleStart :: !(FunPtr (IO ())) -- void (jsaddleStart *)()
11 , _nativeCallbacks_jsaddleResult :: !(FunPtr (CString -> IO ())) -- void (jsaddleResult *)(char *)
11 , _nativeCallbacks_jsaddleResult :: !(FunPtr (CString -> IO ())) -- void (jsaddleResult *)(char *)
12− , _nativeCallbacks_jsaddleSyncResult :: !(FunPtr (CString -> IO CString)) -- char * (jsaddleSyncResult *)(char *)
13 , _nativeCallbacks_jsaddleJsData :: !CString -- char * jsaddleJsData
12 , _nativeCallbacks_jsaddleJsData :: !CString -- char * jsaddleJsData
14 , _nativeCallbacks_jsaddleHtmlData :: !CString -- char * jsaddleHtmlData
13 , _nativeCallbacks_jsaddleHtmlData :: !CString -- char * jsaddleHtmlData
15 }
14 }
@@ -20,48 +19,10 @@ instance Storable NativeCallbacks where
20 poke p nc = do
19 poke p nc = do
21 #{poke native_callbacks, jsaddleStart} p $ _nativeCallbacks_jsaddleStart nc
20 #{poke native_callbacks, jsaddleStart} p $ _nativeCallbacks_jsaddleStart nc
22 #{poke native_callbacks, jsaddleResult} p $ _nativeCallbacks_jsaddleResult nc
21 #{poke native_callbacks, jsaddleResult} p $ _nativeCallbacks_jsaddleResult nc
23− #{poke native_callbacks, jsaddleSyncResult} p $ _nativeCallbacks_jsaddleSyncResult nc
24 #{poke native_callbacks, jsaddleJsData} p $ _nativeCallbacks_jsaddleJsData nc
22 #{poke native_callbacks, jsaddleJsData} p $ _nativeCallbacks_jsaddleJsData nc
25 #{poke native_callbacks, jsaddleHtmlData} p $ _nativeCallbacks_jsaddleHtmlData nc
23 #{poke native_callbacks, jsaddleHtmlData} p $ _nativeCallbacks_jsaddleHtmlData nc
26 peek p = NativeCallbacks
24 peek p = NativeCallbacks
27 <$> #{peek native_callbacks, jsaddleStart} p
25 <$> #{peek native_callbacks, jsaddleStart} p
28 <*> #{peek native_callbacks, jsaddleResult} p
26 <*> #{peek native_callbacks, jsaddleResult} p
29− <*> #{peek native_callbacks, jsaddleSyncResult} p
30 <*> #{peek native_callbacks, jsaddleJsData} p
27 <*> #{peek native_callbacks, jsaddleJsData} p
31 <*> #{peek native_callbacks, jsaddleHtmlData} p
28 <*> #{peek native_callbacks, jsaddleHtmlData} p
32−
33−data AppCallbacks = AppCallbacks
34− { _appCallbacks_mainActivity_onCreate :: !(FunPtr (IO ()))
35− , _appCallbacks_mainActivity_onStart :: !(FunPtr (IO ()))
36− , _appCallbacks_mainActivity_onResume :: !(FunPtr (IO ()))
37− , _appCallbacks_mainActivity_onPause :: !(FunPtr (IO ()))
38− , _appCallbacks_mainActivity_onStop :: !(FunPtr (IO ()))
39− , _appCallbacks_mainActivity_onDestroy :: !(FunPtr (IO ()))
40− , _appCallbacks_mainActivity_onRestart :: !(FunPtr (IO ()))
41− , _appCallbacks_mainActivity_onNewIntent :: !(FunPtr (CString -> CString -> IO ()))
42− , _appCallbacks_firebaseInstanceIdService_sendRegistrationToServer :: !(FunPtr (CString -> IO ()))
43− }
44−
45−instance Storable AppCallbacks where
46− sizeOf _ = #{size app_callbacks}
47− alignment _ = #{alignment app_callbacks}
48− poke p nc = do
49− #{poke app_callbacks, mainActivity_onCreate} p $ _appCallbacks_mainActivity_onCreate nc
50− #{poke app_callbacks, mainActivity_onStart} p $ _appCallbacks_mainActivity_onStart nc
51− #{poke app_callbacks, mainActivity_onResume} p $ _appCallbacks_mainActivity_onResume nc
52− #{poke app_callbacks, mainActivity_onPause} p $ _appCallbacks_mainActivity_onPause nc
53− #{poke app_callbacks, mainActivity_onStop} p $ _appCallbacks_mainActivity_onStop nc
54− #{poke app_callbacks, mainActivity_onDestroy} p $ _appCallbacks_mainActivity_onDestroy nc
55− #{poke app_callbacks, mainActivity_onRestart} p $ _appCallbacks_mainActivity_onRestart nc
56− #{poke app_callbacks, mainActivity_onNewIntent} p $ _appCallbacks_mainActivity_onNewIntent nc
57− #{poke app_callbacks, firebaseInstanceIdService_sendRegistrationToServer} p $ _appCallbacks_firebaseInstanceIdService_sendRegistrationToServer nc
58− peek p = AppCallbacks
59− <$> #{peek app_callbacks, mainActivity_onCreate} p
60− <*> #{peek app_callbacks, mainActivity_onStart} p
61− <*> #{peek app_callbacks, mainActivity_onResume} p
62− <*> #{peek app_callbacks, mainActivity_onPause} p
63− <*> #{peek app_callbacks, mainActivity_onStop} p
64− <*> #{peek app_callbacks, mainActivity_onDestroy} p
65− <*> #{peek app_callbacks, mainActivity_onRestart} p
66− <*> #{peek app_callbacks, mainActivity_onNewIntent} p
67− <*> #{peek app_callbacks, firebaseInstanceIdService_sendRegistrationToServer} p
jsaddle-warp/default.nixnew file+26
@@ -0,0 +1,26 @@
1+{ mkDerivation, aeson, base, bytestring, containers, deepseq
2+, doctest, filepath, ghc-prim, http-types, jsaddle, lens, primitive
3+, process, QuickCheck, ref-tf, stdenv, stm, text, time
4+, transformers, wai, wai-websockets, warp, websockets, osx_sdk
5+, darwin, webdriver, phantomjs, ghc
6+}:
7+mkDerivation {
8+ pname = "jsaddle-warp";
9+ version = "0.8.2.0";
10+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
11+ libraryHaskellDepends = [
12+ aeson base containers http-types jsaddle stm text time transformers
13+ wai wai-websockets warp websockets
14+ ] ++ (if ghc.isGhcjs or false then [
15+ ] else if stdenv.isDarwin then [
16+# osx_sdk darwin.libobjc darwin.apple_sdk.libs.xpc
17+ ] else [
18+ ]);
19+ testHaskellDepends = [
20+ base bytestring deepseq doctest filepath ghc-prim jsaddle lens
21+ primitive process QuickCheck ref-tf webdriver phantomjs
22+ ];
23+ testTarget = "--test-option=${jsaddle.src}";
24+ description = "Interface for JavaScript that works with GHCJS and GHC";
25+ license = stdenv.lib.licenses.mit;
26+}
jsaddle-warp/jsaddle-warp.cabal+13−21
@@ -1,5 +1,5 @@
1name: jsaddle-warp
1name: jsaddle-warp
2−version: 0.9.8.0
2+version: 0.8.3.0
3cabal-version: >=1.10
3cabal-version: >=1.10
4build-type: Simple
4build-type: Simple
5license: MIT
5license: MIT
@@ -10,7 +10,7 @@ description:
10 This package provides an EDSL for calling JavaScript that
10 This package provides an EDSL for calling JavaScript that
11 can be used both from GHCJS and GHC. When using GHC
11 can be used both from GHCJS and GHC. When using GHC
12 the application is run using Warp and WebSockets to
12 the application is run using Warp and WebSockets to
13− drive a small JavaScript helper.
13+ drive a small JavaScipt helper.
14category: Web, Javascript
14category: Web, Javascript
15author: Hamish Mackenzie
15author: Hamish Mackenzie
16
16
@@ -24,23 +24,19 @@ library
24 if !impl(ghcjs -any)
24 if !impl(ghcjs -any)
25 exposed-modules:
25 exposed-modules:
26 Language.Javascript.JSaddle.WebSockets
26 Language.Javascript.JSaddle.WebSockets
27− other-modules:
28− Language.Javascript.JSaddle.WebSockets.Compat
29 build-depends:
27 build-depends:
30− aeson >=0.8.0.2 && <1.6,
28+ aeson >=0.8.0.2 && <1.2,
31− bytestring >=0.10.6.0 && <0.11,
32− containers >=0.5.6.2 && <0.7,
29+ containers >=0.5.6.2 && <0.6,
33− foreign-store >=0.2 && <0.3,
34− http-types >=0.8.6 && <0.13,
30+ http-types >=0.8.6 && <0.10,
35− jsaddle >=0.9.4.0 && <0.10,
31+ jsaddle >=0.8.0.0 && <0.9,
36− stm >=2.4.4 && <2.6,
32+ stm >=2.4.4 && <2.5,
37 text >=1.2.1.3 && <1.3,
33 text >=1.2.1.3 && <1.3,
38− time >=1.5.0.1 && <1.11,
34+ time >=1.5.0.1 && <1.8,
39 transformers >=0.4.2.0 && <0.6,
35 transformers >=0.4.2.0 && <0.6,
40 wai >=3.0.3.0 && <3.3,
36 wai >=3.0.3.0 && <3.3,
41 wai-websockets >=3.0.0.6 && <3.1,
37 wai-websockets >=3.0.0.6 && <3.1,
42− warp >=3.1.2 && <3.4,
38+ warp >=3.1.2 && <3.3,
43− websockets >=0.9.5.0 && <0.13
39+ websockets >=0.9.5.0 && <0.11
44 exposed-modules:
40 exposed-modules:
45 Language.Javascript.JSaddle.Warp
41 Language.Javascript.JSaddle.Warp
46 build-depends:
42 build-depends:
@@ -59,10 +55,8 @@ test-suite test-tool
59 bytestring -any,
55 bytestring -any,
60 containers -any,
56 containers -any,
61 deepseq -any,
57 deepseq -any,
62− doctest >=0.10.1 && <0.19,
58+ doctest >=0.10.1 && <0.12,
63− exceptions -any,
64 filepath >=1.4.0.0 && <1.5,
59 filepath >=1.4.0.0 && <1.5,
65− foreign-store -any,
66 ghc-prim -any,
60 ghc-prim -any,
67 http-types -any,
61 http-types -any,
68 jsaddle -any,
62 jsaddle -any,
@@ -71,17 +65,15 @@ test-suite test-tool
71 network -any,
65 network -any,
72 ref-tf -any,
66 ref-tf -any,
73 primitive -any,
67 primitive -any,
74− process >=1.2.3.0 && <1.7,
68+ process >=1.2.3.0 && <1.5,
75− random -any,
76 stm -any,
69 stm -any,
77 text -any,
70 text -any,
78 time -any,
71 time -any,
79 transformers -any,
72 transformers -any,
80− unliftio-core -any,
81 wai -any,
73 wai -any,
82 wai-websockets -any,
74 wai-websockets -any,
83 warp -any,
75 warp -any,
84− webdriver >=0.8.4 && <0.10,
76+ webdriver >=0.8.4 && <0.9,
85 websockets -any
77 websockets -any
86 type: exitcode-stdio-1.0
78 type: exitcode-stdio-1.0
87 main-is: DocTest.hs
79 main-is: DocTest.hs
jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs+12−13
@@ -17,22 +17,21 @@ module Language.Javascript.JSaddle.Test (
17 , listWindowProperties
17 , listWindowProperties
18) where
18) where
19
19
20−import Prelude hiding((!!), catch)
21import Control.Applicative
20import Control.Applicative
22−import Control.Concurrent (threadDelay, forkIO, ThreadId)
21+import Prelude hiding((!!), catch)
23−import Control.Concurrent.MVar
24− (tryTakeMVar, putMVar, takeMVar, newEmptyMVar, MVar)
25−import Control.Lens.Getter ((^.))
26−import Control.Monad (forever, forM)
27−import Control.Monad.IO.Class (MonadIO(..))
28import Control.Monad.Trans.Reader (runReaderT)
22import Control.Monad.Trans.Reader (runReaderT)
23+import Language.Javascript.JSaddle
24+import qualified Data.Text as T
25+import Control.Monad.IO.Class (MonadIO(..))
26+import Control.Monad (forever, forM)
27+import Control.Lens.Getter ((^.))
29import Data.Monoid ((<>))
28import Data.Monoid ((<>))
29+import Control.Concurrent.MVar
30+ (tryTakeMVar, putMVar, takeMVar, newEmptyMVar, MVar)
31+import System.IO.Unsafe (unsafePerformIO)
32+import Control.Concurrent (threadDelay, forkIO, ThreadId)
30import Data.Text (Text)
33import Data.Text (Text)
31−import qualified Data.Text as T
32−import Language.Javascript.JSaddle
33import Language.Javascript.JSaddle.Warp (run)
34import Language.Javascript.JSaddle.Warp (run)
34−import System.IO.Unsafe (unsafePerformIO)
35−import System.Timeout (timeout)
36
35
37context :: MVar JSContextRef
36context :: MVar JSContextRef
38context = unsafePerformIO newEmptyMVar
37context = unsafePerformIO newEmptyMVar
@@ -53,12 +52,12 @@ startServer =
53
52
54-- >>> testJSaddle $ ((global ^. js "console" . js "log") # ["Hello"])
53-- >>> testJSaddle $ ((global ^. js "console" . js "log") # ["Hello"])
55testJSaddle :: ToJSVal val => JSM val -> IO ()
54testJSaddle :: ToJSVal val => JSM val -> IO ()
56−testJSaddle f = timeout 10000000 (do
55+testJSaddle f = do
57 startServer
56 startServer
58 c <- takeMVar context
57 c <- takeMVar context
59 runReaderT (unJSM $ (f >>= valToText >>= liftIO . putStrLn . T.unpack)
58 runReaderT (unJSM $ (f >>= valToText >>= liftIO . putStrLn . T.unpack)
60 `catch` \ (JSException e) -> valToText e >>= liftIO . putStrLn . T.unpack) c
59 `catch` \ (JSException e) -> valToText e >>= liftIO . putStrLn . T.unpack) c
61− putMVar context c) >>= maybe (putStrLn "testJSaddle timed out") return
60+ putMVar context c
62
61
63debugLog :: String -> IO ()
62debugLog :: String -> IO ()
64debugLog _ = return ()
63debugLog _ = return ()
jsaddle-warp/src/Language/Javascript/JSaddle/Warp.hs+4−3
@@ -17,7 +17,8 @@ module Language.Javascript.JSaddle.Warp (
17 -- * Running JSM over WebSockets
17 -- * Running JSM over WebSockets
18 run
18 run
19#ifndef ghcjs_HOST_OS
19#ifndef ghcjs_HOST_OS
20− , module Language.Javascript.JSaddle.WebSockets
20+ , jsaddleOr
21+ , jsaddleApp
21#endif
22#endif
22) where
23) where
23
24
@@ -28,7 +29,7 @@ import Network.WebSockets (defaultConnectionOptions)
28
29
29import Language.Javascript.JSaddle.Types (JSM)
30import Language.Javascript.JSaddle.Types (JSM)
30import Language.Javascript.JSaddle.Run (syncPoint)
31import Language.Javascript.JSaddle.Run (syncPoint)
31−import Language.Javascript.JSaddle.WebSockets
32+import Language.Javascript.JSaddle.WebSockets (jsaddleOr, jsaddleApp)
32#endif
33#endif
33
34
34-- | Run the given 'JSM' action as the main entry point. Either directly
35-- | Run the given 'JSM' action as the main entry point. Either directly
@@ -39,6 +40,6 @@ run _port = id
39#else
40#else
40run :: Int -> JSM () -> IO ()
41run :: Int -> JSM () -> IO ()
41run port f =
42run port f =
42− runSettings (setPort port (setTimeout 3600 defaultSettings)) =<<
43+ runSettings (setPort port (setTimeout 3600 defaultSettings)) $
43 jsaddleOr defaultConnectionOptions (f >> syncPoint) jsaddleApp
44 jsaddleOr defaultConnectionOptions (f >> syncPoint) jsaddleApp
44#endif
45#endif
jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+55−286
@@ -1,7 +1,5 @@
1{-# LANGUAGE LambdaCase #-}
1{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE RecursiveDo #-}
5-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
6--
4--
7-- Module : Language.Javascript.JSaddle.WebSockets
5-- Module : Language.Javascript.JSaddle.WebSockets
@@ -18,302 +16,73 @@ module Language.Javascript.JSaddle.WebSockets (
18 -- * Running JSM over WebSockets
16 -- * Running JSM over WebSockets
19 jsaddleOr
17 jsaddleOr
20 , jsaddleApp
18 , jsaddleApp
21− , jsaddleWithAppOr
22− , jsaddleAppWithJs
23− , jsaddleAppWithJsOr
24− , jsaddleAppPartial
25− , jsaddleJs
26− , jsaddleJs'
27− , debug
28− , debugOr
29− , debugWrapper
30) where
19) where
31
20
32−import Control.Monad (when, void, forever)
21+import Control.Monad (forever)
33−import Control.Concurrent (killThread, forkIO, threadDelay)
22+import Control.Concurrent (forkIO, threadDelay)
34−import Control.Exception (handle, AsyncException, throwIO, fromException, finally)
23+import Control.Exception (handle, AsyncException, throwIO, fromException)
35
24
36import Data.Monoid ((<>))
25import Data.Monoid ((<>))
37import Data.Aeson (encode, decode)
26import Data.Aeson (encode, decode)
38
27
39−import Network.Wai
28+import Network.Wai (Application)
40− (Middleware, lazyRequestBody, Application, Request, Response,
41− ResponseReceived)
42import Network.WebSockets
29import Network.WebSockets
43− (defaultConnectionOptions, ConnectionOptions(..), sendTextData,
30+ (ConnectionOptions(..), sendTextData, receiveDataMessage,
44− receiveDataMessage, acceptRequest, ServerApp, sendPing)
31+ acceptRequest, ServerApp, sendPing)
32+import qualified Network.WebSockets as WS (DataMessage(..))
45import Network.Wai.Handler.WebSockets (websocketsOr)
33import Network.Wai.Handler.WebSockets (websocketsOr)
46−import Network.HTTP.Types (Status(..))
47
34
48−import Language.Javascript.JSaddle.Types (JSM(..), JSContextRef(..))
35+import Language.Javascript.JSaddle.Types (JSM(..))
49import qualified Network.Wai as W
36import qualified Network.Wai as W
50− (responseLBS, requestMethod, pathInfo, modifyResponse, responseStatus)
37+ (responseLBS, requestMethod, Application, pathInfo)
38+import Data.Text (Text)
51import qualified Data.Text as T (pack)
39import qualified Data.Text as T (pack)
52import qualified Network.HTTP.Types as H
40import qualified Network.HTTP.Types as H
53− (status403, status200)
41+ (status403, status200, status405)
54−import Language.Javascript.JSaddle.Run (syncPoint, runJavaScript)
42+import Language.Javascript.JSaddle.Run (runJavaScript)
55−import Language.Javascript.JSaddle.Run.Files (indexHtml, runBatch, ghcjsHelpers, initState)
43+import Language.Javascript.JSaddle.Run.Files (indexHtml, jsaddleJs)
56−import Language.Javascript.JSaddle.Debug
44+
57− (removeContext, addContext)
58−import Data.Maybe (fromMaybe)
59−import qualified Data.Map as M (empty, insert, lookup)
60−import Data.IORef
61− (readIORef, newIORef, atomicModifyIORef')
62−import Data.ByteString.Lazy (ByteString)
63−import qualified Data.ByteString.Lazy as LBS
64− (toStrict, stripPrefix)
65−import Control.Concurrent.MVar
66− (tryTakeMVar, MVar, tryPutMVar, modifyMVar_, putMVar, takeMVar,
67− readMVar, newMVar, newEmptyMVar, modifyMVar)
68−import Network.Wai.Handler.Warp
69− (defaultSettings, setTimeout, setPort, runSettings)
70−import Foreign.Store (readStore, lookupStore, writeStore, Store(..))
71−import Language.Javascript.JSaddle (askJSM)
72−import Control.Monad.IO.Class (MonadIO(..))
73−
74−import Language.Javascript.JSaddle.WebSockets.Compat (getTextMessageByteString)
75−import qualified Data.Text.Encoding as T (decodeUtf8)
76−
77−jsaddleOr :: ConnectionOptions -> JSM () -> Application -> IO Application
45+jsaddleOr :: ConnectionOptions -> JSM () -> Application -> Application
78−jsaddleOr opts entryPoint otherApp = do
46+jsaddleOr opts entryPoint = websocketsOr opts wsApp
79− syncHandlers <- newIORef M.empty
47+ where
80− asyncHandlers <- newIORef M.empty
81− let wsApp :: ServerApp
48+ wsApp :: ServerApp
82− wsApp pending_conn = do
49+ wsApp pending_conn = do
83− conn <- acceptRequest pending_conn
50+ conn <- acceptRequest pending_conn
84− initMsg <- receiveDataMessage conn
85− case getTextMessageByteString initMsg of
86− Just "" -> do
87− rec (processResult, processSyncResult, start) <- runJavaScript (sendTextData conn . encode) $ do
51+ (processResult, start) <- runJavaScript (sendTextData conn . encode) entryPoint
88− syncKey <- T.pack . show . contextId <$> askJSM
89− liftIO $ atomicModifyIORef' syncHandlers (\m -> (M.insert syncKey processSyncResult m, ()))
90− liftIO $ atomicModifyIORef' asyncHandlers (\m -> (M.insert syncKey processResult m, ()))
91− liftIO $ sendTextData conn (encode syncKey)
92− entryPoint
93− start
94− waitTillClosed conn
95− Just syncKey ->
96− M.lookup (T.decodeUtf8 $ LBS.toStrict syncKey) <$> readIORef syncHandlers >>= \case
97− Nothing -> error "jsaddle missing sync message handler"
98− Just processResult ->
99− forever $
52+ _ <- forkIO . forever $
100− receiveDataMessage conn >>= \msg -> case getTextMessageByteString msg of
53+ receiveDataMessage conn >>= \case
101− Just t ->
54+ (WS.Text t) ->
102− case decode t of
55+ case decode t of
103− Nothing -> error $ "jsaddle Results decode failed : " <> show t
56+ Nothing -> error $ "jsaddle WebSocket decode failed : " <> show t
104− Just r -> processResult r
57+ Just r -> processResult r
105− _ -> error "jsaddle WebSocket unexpected binary data"
106 _ -> error "jsaddle WebSocket unexpected binary data"
58 _ -> error "jsaddle WebSocket unexpected binary data"
107−
59+ start
60+ waitTillClosed conn
61+
108− -- Based on Network.WebSocket.forkPingThread
62+ -- Based on Network.WebSocket.forkPingThread
109− waitTillClosed conn = ignore `handle` go 1
63+ waitTillClosed conn = ignore `handle` go 1
110− where
64+ where
111− go :: Int -> IO ()
65+ go :: Int -> IO ()
112− go i = do
66+ go i = do
113− threadDelay (10 * 1000 * 1000)
67+ threadDelay (1 * 1000 * 1000)
114− sendPing conn (T.pack $ show i)
68+ sendPing conn (T.pack $ show i)
115− go (i + 1)
69+ go (i + 1)
116−
70+
117− ignore e = case fromException e of
71+ ignore e = case fromException e of
118− Just async -> throwIO (async :: AsyncException)
72+ Just async -> throwIO (async :: AsyncException)
119− Nothing -> return ()
73+ Nothing -> return ()
120−
121− syncHandler :: Application
122− syncHandler req sendResponse = case (W.requestMethod req, W.pathInfo req) of
123− ("POST", ["sync", syncKey]) -> do
124− body <- lazyRequestBody req
125− case decode body of
126− Nothing -> error $ "jsaddle sync message decode failed : " <> show body
127− Just result ->
128− M.lookup syncKey <$> readIORef syncHandlers >>= \case
129− Nothing -> error "jsaddle missing sync message handler"
130− Just handler -> do
131− next <- encode <$> handler result
132− sendResponse $ W.responseLBS H.status200 [("Content-Type", "application/json")] next
133− (method, _) -> (catch404 otherApp) req sendResponse
134− where catch404 = W.modifyResponse $ \resp ->
135− case (method, W.responseStatus resp) of
136− ("GET", Status 404 _) -> indexResponse
137− _ -> resp
138− return $ websocketsOr opts wsApp syncHandler
139−
140
74
141jsaddleApp :: Application
75jsaddleApp :: Application
142−jsaddleApp = jsaddleAppWithJs $ jsaddleJs False
76+jsaddleApp req = jsaddleAppPieces (W.pathInfo req) req
143−
77+
144−jsaddleAppWithJs :: ByteString -> Application
78+jsaddleAppPieces :: [Text] -> W.Application
145−jsaddleAppWithJs js req sendResponse =
79+jsaddleAppPieces _ req sendResponse
146− jsaddleAppWithJsOr js
80+ | W.requestMethod req `notElem` ["GET", "HEAD"] = sendResponse $ W.responseLBS
81+ H.status405
147− (\_ _ -> sendResponse $ W.responseLBS H.status403 [("Content-Type", "text/plain")] "Forbidden")
82+ [("Content-Type", "text/plain")]
148− req sendResponse
83+ "Only GET or HEAD is supported"
149−
150−jsaddleAppWithJsOr :: ByteString -> Application -> Application
151−jsaddleAppWithJsOr js otherApp req sendResponse =
152− fromMaybe (otherApp req sendResponse)
153− (jsaddleAppPartialWithJs js req sendResponse)
154−
155−jsaddleWithAppOr :: ConnectionOptions -> JSM () -> Application -> IO Application
156−jsaddleWithAppOr opts entryPoint otherApp = jsaddleOr opts entryPoint $ \req sendResponse ->
157− (fromMaybe (otherApp req sendResponse)
158− (jsaddleAppPartial req sendResponse))
159−
160−jsaddleAppPartial :: Request -> (Response -> IO ResponseReceived) -> Maybe (IO ResponseReceived)
161−jsaddleAppPartial = jsaddleAppPartialWithJs $ jsaddleJs False
162−
163−indexResponse :: Response
164−indexResponse = W.responseLBS H.status200 [("Content-Type", "text/html")] indexHtml
84+jsaddleAppPieces [] _req sendResponse = sendResponse $ W.responseLBS H.status200 [("Content-Type", "text/html")] indexHtml
165−
166−jsaddleAppPartialWithJs :: ByteString -> Request -> (Response -> IO ResponseReceived) -> Maybe (IO ResponseReceived)
167−jsaddleAppPartialWithJs js req sendResponse = case (W.requestMethod req, W.pathInfo req) of
168− ("GET", []) -> Just $ sendResponse indexResponse
169− ("GET", ["jsaddle.js"]) -> Just $ sendResponse $ W.responseLBS H.status200 [("Content-Type", "application/javascript")] js
85+jsaddleAppPieces ["jsaddle.js"] _req sendResponse = sendResponse $ W.responseLBS H.status200 [("Content-Type", "application/javascript")] jsaddleJs
170− _ -> Nothing
86+jsaddleAppPieces _rawPieces _req sendResponse = sendResponse $ W.responseLBS H.status403
171−
87+ [ ("Content-Type", "text/plain")
172−jsaddleJs :: Bool -> ByteString
88+ ] "Forbidden"
173−jsaddleJs = jsaddleJs' Nothing
174−
175−-- Use this to generate this string for embedding
176−-- sed -e 's|\\|\\\\|g' -e 's|^| \\|' -e 's|$|\\n\\|' -e 's|"|\\"|g' data/jsaddle.js | pbcopy
177−jsaddleJs' :: Maybe ByteString -> Bool -> ByteString
178−jsaddleJs' jsaddleUri refreshOnLoad = "\
179− \if(typeof global !== \"undefined\" && typeof require === \"function\") {\n\
180− \ global.window = global;\n\
181− \ global.WebSocket = require('ws');\n\
182− \}\n\
183− \\n\
184− \var connect = function() {\n\
185− \ var wsaddress = "
186− <> maybe "window.location.protocol.replace('http', 'ws')+\"//\"+window.location.hostname+(window.location.port?(\":\"+window.location.port):\"\")"
187− (\ s -> "\"ws" <> s <> "\"")
188− (jsaddleUri >>= LBS.stripPrefix "http")
189− <> ";\n\
190− \\n\
191− \ var ws0 = new WebSocket(wsaddress);\n\
192− \ var syncKey = \"\";\n\
193− \\n\
194− \ ws0.onopen = function(e) {\n\
195− \ ws0.send(\"\");\n\
196− \ var initialResults = [];\n\
197− \ var ws = {send: function(m) {initialResults.push(m);}};\n\
198− \ " <> initState <> "\n\
199− \\n\
200− \ ws0.onmessage = function(e) {\n\
201− \ var batch = JSON.parse(e.data);\n\
202− \ if(inCallback > 0) {\n\
203− \ asyncBatch = batch;\n\
204− \ return;\n\
205− \ }\n\
206− \ if(typeof batch === \"string\") {\n\
207− \ syncKey = batch;\n\
208− \ var ws1 = new WebSocket(wsaddress);\n\
209− \ ws1.onopen = function(e) {\n\
210− \ ws1.send(syncKey);\n\
211− \ initialResults.forEach(function(m){ ws1.send(m); });\n\
212− \ initialResults = null;\n\
213− \ ws = ws1;\n\
214− \ }\n" <>
215− (if refreshOnLoad
216− then " var xhr = new XMLHttpRequest();\n\
217− \ xhr.open('POST', '/reload/'+syncKey, true);\n\
218− \ xhr.onreadystatechange = function() {\n\
219− \ if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200)\n\
220− \ setTimeout(function(){window.location.reload();}, 100);\n\
221− \ };\n\
222− \ xhr.send();\n"
223− else "") <>
224− " return;\n\
225− \ }\n\
226− \\n\
227− \ " <> runBatch (\a -> "ws.send(JSON.stringify(" <> a <> "));")
228− (Just (\a -> "(function(){\n\
229− \ var xhr = new XMLHttpRequest();\n\
230− \ xhr.open('POST', '" <> fromMaybe "" jsaddleUri <> "/sync/'+syncKey, false);\n\
231− \ xhr.setRequestHeader(\"Content-type\", \"application/json\");\n\
232− \ xhr.send(JSON.stringify(" <> a <> "));\n\
233− \ return JSON.parse(xhr.response);})()")) <> "\
234− \ };\n\
235− \ };\n\
236− \ ws0.onerror = function() {\n\
237− \ setTimeout(connect, 1000);\n\
238− \ };\n\
239− \}\n\
240− \\n\
241− \ " <> ghcjsHelpers <> "\
242− \connect();\n\
243− \"
244−
245−-- | Start or restart the server.
246−-- To run this as part of every :reload use
247−-- > :def! reload (const $ return "::reload\nLanguage.Javascript.JSaddle.Warp.debug 3708 SomeMainModule.someMainFunction")
248−debug :: Int -> JSM () -> IO ()
249−debug port f = do
250− debugWrapper $ \withRefresh registerContext ->
251− runSettings (setPort port (setTimeout 3600 defaultSettings)) =<<
252− jsaddleOr defaultConnectionOptions (registerContext >> f >> syncPoint) (withRefresh $ jsaddleAppWithJs $ jsaddleJs True)
253− putStrLn $ "<a href=\"http://localhost:" <> show port <> "\">run</a>"
254−
255−debugOr :: Int -> JSM () -> Application -> IO ()
256−debugOr port f b = do
257− debugWrapper $ \withRefresh registerContext ->
258− runSettings (setPort port (setTimeout 3600 defaultSettings)) =<<
259− jsaddleOr defaultConnectionOptions (registerContext >> f >> syncPoint) (withRefresh $ jsaddleAppWithJsOr (jsaddleJs True) b)
260− putStrLn $ "<a href=\"http://localhost:" <> show port <> "\">run</a>"
261−
262−refreshMiddleware :: ((Response -> IO ResponseReceived) -> IO ResponseReceived) -> Middleware
263−refreshMiddleware refresh otherApp req sendResponse = case (W.requestMethod req, W.pathInfo req) of
264− ("POST", ["reload", _syncKey]) -> refresh sendResponse
265− _ -> otherApp req sendResponse
266−
267−debugWrapper :: (Middleware -> JSM () -> IO ()) -> IO ()
268−debugWrapper run = do
269− reloadMVar <- newEmptyMVar
270− reloadDoneMVars <- newMVar []
271− contexts <- newMVar []
272− let refresh sendResponse = do
273− reloadDone <- newEmptyMVar
274− modifyMVar_ reloadDoneMVars (return . (reloadDone:))
275− readMVar reloadMVar
276− r <- sendResponse $ W.responseLBS H.status200 [("Content-Type", "application/json")] ("reload" :: ByteString)
277− putMVar reloadDone ()
278− return r
279− start :: Int -> IO (IO Int)
280− start expectedConnections = do
281− serverDone <- newEmptyMVar
282− ready <- newEmptyMVar
283− let registerContext :: JSM ()
284− registerContext = do
285− uuid <- contextId <$> askJSM
286− browsersConnected <- liftIO $ modifyMVar contexts (\ctxs -> return (uuid:ctxs, length ctxs + 1))
287− addContext
288− when (browsersConnected == expectedConnections) . void . liftIO $ tryPutMVar ready ()
289− thread <- forkIO $
290− finally (run (refreshMiddleware refresh) registerContext)
291− (putMVar serverDone ())
292− _ <- forkIO $ threadDelay 10000000 >> void (tryPutMVar ready ())
293− when (expectedConnections /= 0) $ takeMVar ready
294− return $ do
295− putMVar reloadMVar ()
296− ctxs <- takeMVar contexts
297− mapM_ removeContext ctxs
298− takeMVar reloadDoneMVars >>= mapM_ takeMVar
299− tryTakeMVar serverDone >>= \case
300− Nothing -> do
301− killThread thread
302− takeMVar serverDone
303− Just _ -> return ()
304− return $ length ctxs
305− restarter :: MVar (Int -> IO (IO Int)) -> IO Int -> IO ()
306− restarter mvar stop = do
307− start' <- takeMVar mvar
308− n <- stop
309− start' n >>= restarter mvar
310− lookupStore storeId >>= \case
311− Nothing -> do
312− restartMVar <- newMVar start
313− void . forkIO $ restarter restartMVar (return 0)
314− void $ writeStore (Store storeId) restartMVar
315− Just shutdownStore -> do
316− restartMVar :: MVar (Int -> IO (IO Int)) <- readStore shutdownStore
317− void $ tryTakeMVar restartMVar
318− putMVar restartMVar start
319− where storeId = 354
jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets/Compat.hsdeleted−29
@@ -1,29 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−-----------------------------------------------------------------------------
3−--
4−-- Module : Language.Javascript.JSaddle.WebSockets.Compat
5−-- Copyright : (c) Hamish Mackenzie
6−-- License : MIT
7−--
8−-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
9−--
10−-- | This module is necessary to harmonize various versions of the websockets
11−-- package; we can't do this CPP inline in
12−-- Language.Javascript.JSaddle.WebSockets because that module uses multi-line
13−-- strings, which are incompatible with CPP
14−--
15−-----------------------------------------------------------------------------
16−module Language.Javascript.JSaddle.WebSockets.Compat where
17−
18−import Data.ByteString.Lazy (ByteString)
19−import qualified Network.WebSockets as WS (DataMessage(..))
20−
21−getTextMessageByteString :: WS.DataMessage -> Maybe ByteString
22−getTextMessageByteString msg = case msg of
23−#if MIN_VERSION_websockets(0,11,0)
24− (WS.Text t _) ->
25−#else
26− (WS.Text t) ->
27−#endif
28− Just t
29− _ -> Nothing
jsaddle-warp/tests/DocTest.hs+7−20
@@ -11,17 +11,18 @@ import System.IO (stderr, BufferMode(..), stdout, hSetBuffering)
11import System.FilePath ((</>))
11import System.FilePath ((</>))
12import System.Exit (exitFailure, exitWith, ExitCode(..))
12import System.Exit (exitFailure, exitWith, ExitCode(..))
13import System.Process (readProcess, system)
13import System.Process (readProcess, system)
14−import Control.Concurrent
14+import Control.Concurrent (forkIO)
15− (takeMVar, newEmptyMVar, forkIO, threadDelay, putMVar)
16import Control.Monad (void)
15import Control.Monad (void)
17import Control.Monad.IO.Class (MonadIO(..))
16import Control.Monad.IO.Class (MonadIO(..))
18import Data.Monoid ((<>))
17import Data.Monoid ((<>))
19import System.Environment (getArgs)
18import System.Environment (getArgs)
19+import Language.Javascript.JSaddle.Run.Files (jsaddleJs)
20+import qualified Data.ByteString.Lazy.Char8 as BS (unpack)
20import Test.WebDriver (runSession, defaultConfig, openPage, closeSession)
21import Test.WebDriver (runSession, defaultConfig, openPage, closeSession)
21
22
22main :: IO ()
23main :: IO ()
23main = do
24main = do
24− putStrLn "Testing JSaddle"
25+ putStrLn "Hello"
25 jsaddlePath <- getArgs >>= \case
26 jsaddlePath <- getArgs >>= \case
26 [arg] -> return arg
27 [arg] -> return arg
27 _ -> do
28 _ -> do
@@ -29,18 +30,14 @@ main = do
29 exitFailure
30 exitFailure
30 hSetBuffering stdout LineBuffering
31 hSetBuffering stdout LineBuffering
31 hSetBuffering stderr LineBuffering
32 hSetBuffering stderr LineBuffering
32− putStrLn "Checking for phantomjs"
33 node <- system "phantomjs --version" >>= \case
33 node <- system "phantomjs --version" >>= \case
34 ExitSuccess -> return ()
34 ExitSuccess -> return ()
35 e -> do
35 e -> do
36 putStrLn "phantomjs not found"
36 putStrLn "phantomjs not found"
37 exitWith e
37 exitWith e
38− putStrLn "Starting phantomjs"
39− forkIO . void $ readProcess "phantomjs" ["--webdriver=4444"] "" >>= putStr
38+ forkIO . void $ readProcess "phantomjs" ["--webdriver=4444"] (BS.unpack jsaddleJs) >>= putStr
40− threadDelay 5000000
39+ runSession defaultConfig $ do
41− putStrLn "Running Tests"
40+ openPage "http://localhost:3709"
42− done <- newEmptyMVar
43− forkIO $ do
44 liftIO $ doctest [
41 liftIO $ doctest [
45 "-hide-all-packages",
42 "-hide-all-packages",
46 "-package=base-" ++ VERSION_base,
43 "-package=base-" ++ VERSION_base,
@@ -64,10 +61,6 @@ main = do
64 "-package=ref-tf-" ++ VERSION_ref_tf,
61 "-package=ref-tf-" ++ VERSION_ref_tf,
65 "-package=deepseq-" ++ VERSION_deepseq,
62 "-package=deepseq-" ++ VERSION_deepseq,
66 "-package=ghc-prim-" ++ VERSION_ghc_prim,
63 "-package=ghc-prim-" ++ VERSION_ghc_prim,
67− "-package=exceptions-" ++ VERSION_exceptions,
68− "-package=unliftio-core-" ++ VERSION_unliftio_core,
69− "-package=random-" ++ VERSION_random,
70− "-package=foreign-store-" ++ VERSION_foreign_store,
71 "-i" <> "src",
64 "-i" <> "src",
72 "-i" <> "src-ghc",
65 "-i" <> "src-ghc",
73 "src/Language/Javascript/JSaddle/Test.hs",
66 "src/Language/Javascript/JSaddle/Test.hs",
@@ -90,7 +83,6 @@ main = do
90 jsaddlePath </> "src/Language/Javascript/JSaddle/Arguments.hs",
83 jsaddlePath </> "src/Language/Javascript/JSaddle/Arguments.hs",
91 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes.hs",
84 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes.hs",
92 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes/Internal.hs",
85 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes/Internal.hs",
93− jsaddlePath </> "src/Language/Javascript/JSaddle/Debug.hs",
94 jsaddlePath </> "src/Language/Javascript/JSaddle/Evaluate.hs",
86 jsaddlePath </> "src/Language/Javascript/JSaddle/Evaluate.hs",
95 jsaddlePath </> "src/Language/Javascript/JSaddle/Exception.hs",
87 jsaddlePath </> "src/Language/Javascript/JSaddle/Exception.hs",
96 jsaddlePath </> "src/Language/Javascript/JSaddle/Monad.hs",
88 jsaddlePath </> "src/Language/Javascript/JSaddle/Monad.hs",
@@ -104,9 +96,4 @@ main = do
104 jsaddlePath </> "src/Language/Javascript/JSaddle/String.hs",
96 jsaddlePath </> "src/Language/Javascript/JSaddle/String.hs",
105 jsaddlePath </> "src/Language/Javascript/JSaddle/Types.hs",
97 jsaddlePath </> "src/Language/Javascript/JSaddle/Types.hs",
106 jsaddlePath </> "src/Language/Javascript/JSaddle/Value.hs" ]
98 jsaddlePath </> "src/Language/Javascript/JSaddle/Value.hs" ]
107− putMVar done ()
108− threadDelay 5000000
109− runSession defaultConfig $ do
110− openPage "http://127.0.0.1:3709"
111− liftIO $ takeMVar done
112 closeSession
99 closeSession
jsaddle-webkit2gtk/default.nixnew file+16
@@ -0,0 +1,16 @@
1+{ mkDerivation, aeson, base, bytestring, directory, gi-gio, gi-glib
2+, gi-gtk, gi-javascriptcore, gi-webkit2, haskell-gi-base, jsaddle
3+, stdenv, text, unix, webkit2gtk3-javascriptcore
4+}:
5+mkDerivation {
6+ pname = "jsaddle-webkit2gtk";
7+ version = "0.8.2.2";
8+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
9+ libraryHaskellDepends = [
10+ aeson base bytestring directory gi-gio gi-glib gi-gtk
11+ gi-javascriptcore gi-webkit2 haskell-gi-base jsaddle text unix
12+ webkit2gtk3-javascriptcore
13+ ];
14+ description = "Interface for JavaScript that works with GHCJS and GHC";
15+ license = stdenv.lib.licenses.mit;
16+}
jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal+9−10
@@ -1,5 +1,5 @@
1name: jsaddle-webkit2gtk
1name: jsaddle-webkit2gtk
2−version: 0.9.8.0
2+version: 0.8.3.0
3cabal-version: >=1.10
3cabal-version: >=1.10
4build-type: Simple
4build-type: Simple
5license: MIT
5license: MIT
@@ -26,18 +26,17 @@ library
26 base <5
26 base <5
27 if !impl(ghcjs -any)
27 if !impl(ghcjs -any)
28 build-depends:
28 build-depends:
29− aeson >=0.8.0.2 && <1.6,
29+ aeson >=0.8.0.2 && <1.2,
30 base <5,
30 base <5,
31 bytestring >=0.10.6.0 && <0.11,
31 bytestring >=0.10.6.0 && <0.11,
32 directory >=1.0.0.2 && <1.4,
32 directory >=1.0.0.2 && <1.4,
33− gi-glib >=2.0.14 && <2.1,
33+ gi-glib >=2.0.6 && <2.1,
34− gi-gio >=2.0.14 && <2.1,
34+ gi-gio >=2.0.6 && <2.1,
35− gi-gtk >=3.0.17 && <3.1,
35+ gi-gtk >=3.0.6 && <3.1,
36− gi-webkit2 >=4.0.14 && <4.1,
36+ gi-webkit2 >=4.0.6 && <4.1,
37− gi-javascriptcore >=4.0.14 && <4.1,
37+ gi-javascriptcore >=4.0.6 && <4.1,
38− haskell-gi-base >=0.20 && <0.26,
38+ haskell-gi-base >=0.20 && <0.21,
39− haskell-gi-overloading >=0.0 && < 2.0,
40− jsaddle >=0.9.4.0 && <0.10,
39+ jsaddle >=0.8.0.0 && <0.9,
41 text >=1.2.1.3 && <1.3,
40 text >=1.2.1.3 && <1.3,
42 webkit2gtk3-javascriptcore >=0.14.0.0 && <0.15
41 webkit2gtk3-javascriptcore >=0.14.0.0 && <0.15
43 if !os(windows)
42 if !os(windows)
jsaddle-webkit2gtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+46−44
@@ -43,51 +43,54 @@ import Data.Monoid ((<>))
43import Data.ByteString.Lazy (toStrict, fromStrict)
43import Data.ByteString.Lazy (toStrict, fromStrict)
44import qualified Data.ByteString.Lazy as LB (ByteString)
44import qualified Data.ByteString.Lazy as LB (ByteString)
45import Data.Aeson (encode, decode)
45import Data.Aeson (encode, decode)
46+import Data.Text (Text)
46import qualified Data.Text as T (pack)
47import qualified Data.Text as T (pack)
48+import Data.Text.Foreign (fromPtr)
47import Data.Text.Encoding (decodeUtf8, encodeUtf8)
49import Data.Text.Encoding (decodeUtf8, encodeUtf8)
48
50
51+import Foreign.Ptr (castPtr, nullPtr)
52+
49import Data.GI.Base.BasicTypes (GObject)
53import Data.GI.Base.BasicTypes (GObject)
50import Data.GI.Base.Signals
54import Data.GI.Base.Signals
51 (connectSignalFunPtr, SignalConnectMode(..), SignalConnectMode,
55 (connectSignalFunPtr, SignalConnectMode(..), SignalConnectMode,
52 SignalHandlerId)
56 SignalHandlerId)
57+import Data.GI.Base.ManagedPtr (withManagedPtr)
53
58
54import GI.GLib (timeoutAdd, idleAdd, pattern PRIORITY_HIGH, pattern PRIORITY_DEFAULT)
59import GI.GLib (timeoutAdd, idleAdd, pattern PRIORITY_HIGH, pattern PRIORITY_DEFAULT)
55import qualified GI.Gtk as Gtk (main, init)
60import qualified GI.Gtk as Gtk (main, init)
56import GI.Gtk
61import GI.Gtk
57 (windowSetPosition, windowSetDefaultSize, windowNew,
62 (windowSetPosition, windowSetDefaultSize, windowNew,
58− scrolledWindowNew, Adjustment, containerAdd,
63+ scrolledWindowNew, noAdjustment, containerAdd,
59 WindowType(..), WindowPosition(..), widgetDestroy,
64 WindowType(..), WindowPosition(..), widgetDestroy,
60 widgetGetToplevel, widgetShowAll, onWidgetDestroy,
65 widgetGetToplevel, widgetShowAll, onWidgetDestroy,
61 mainQuit)
66 mainQuit)
62−import GI.Gio (Cancellable)
67+import GI.Gio (noCancellable)
63−import GI.JavaScriptCore (valueToString)
68+import GI.JavaScriptCore (Value(..), GlobalContext(..))
64import GI.WebKit2
69import GI.WebKit2
65− (scriptDialogPromptSetText, scriptDialogPromptGetDefaultText,
66− scriptDialogGetMessage, scriptDialogGetDialogType,
67− onWebViewScriptDialog, WebView,
68− setSettingsEnableWriteConsoleMessagesToStdout,
70+ (WebView, setSettingsEnableWriteConsoleMessagesToStdout,
69− setSettingsEnableJavascript, webViewNewWithUserContentManager,
71+ setSettingsEnableJavascript,
70− userContentManagerNew,
72+ webViewNewWithUserContentManager, userContentManagerNew,
71 userContentManagerRegisterScriptMessageHandler,
73 userContentManagerRegisterScriptMessageHandler,
72− javascriptResultGetJsValue,
74+ javascriptResultGetValue, javascriptResultGetGlobalContext,
73 webViewGetUserContentManager,
75 webViewGetUserContentManager,
74 mk_UserContentManagerScriptMessageReceivedCallback,
76 mk_UserContentManagerScriptMessageReceivedCallback,
75 wrap_UserContentManagerScriptMessageReceivedCallback,
77 wrap_UserContentManagerScriptMessageReceivedCallback,
76 webViewRunJavascript, LoadEvent(..),
78 webViewRunJavascript, LoadEvent(..),
77 UserContentManagerScriptMessageReceivedCallback, webViewLoadHtml,
79 UserContentManagerScriptMessageReceivedCallback, webViewLoadHtml,
78 onWebViewLoadChanged, setSettingsEnableDeveloperExtras,
80 onWebViewLoadChanged, setSettingsEnableDeveloperExtras,
79− webViewSetSettings, webViewGetSettings, ScriptDialogType(..))
81+ webViewSetSettings, webViewGetSettings)
82+
83+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
84+ (JSValueRef, JSContextRef)
85+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
86+ (jsstringgetcharactersptr, jsstringgetlength, jsstringrelease)
87+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
88+ (jsvaluetostringcopy)
80
89
81−import Language.Javascript.JSaddle (JSM, Results, Batch)
90+import Language.Javascript.JSaddle (JSM, Results)
82import Language.Javascript.JSaddle.Run (runJavaScript)
91import Language.Javascript.JSaddle.Run (runJavaScript)
83import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
92import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
84
93
85−noAdjustment :: Maybe Adjustment
86−noAdjustment = Nothing
87−
88−noCancellable :: Maybe Cancellable
89−noCancellable = Nothing
90−
91quitWebView :: WebView -> IO ()
94quitWebView :: WebView -> IO ()
92quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
95quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
93 widgetDestroy w
96 widgetDestroy w
@@ -103,6 +106,21 @@ postGUIAsync :: IO () -> IO ()
103postGUIAsync action =
106postGUIAsync action =
104 void . idleAdd PRIORITY_DEFAULT $ action >> return False
107 void . idleAdd PRIORITY_DEFAULT $ action >> return False
105
108
109+withJSContextRef :: GlobalContext -> (JSContextRef -> IO a) -> IO a
110+withJSContextRef (GlobalContext ctx) f = withManagedPtr ctx (f . castPtr)
111+
112+withJSValueRef :: Value -> (JSValueRef -> IO a) -> IO a
113+withJSValueRef (Value ptr) f = withManagedPtr ptr (f . castPtr)
114+
115+valueToText :: JSContextRef -> JSValueRef -> IO Text
116+valueToText ctxRef s = do
117+ jsstring <- jsvaluetostringcopy ctxRef s nullPtr
118+ l <- jsstringgetlength jsstring
119+ p <- jsstringgetcharactersptr jsstring
120+ result <- fromPtr (castPtr p) (fromIntegral l)
121+ jsstringrelease jsstring
122+ return result
123+
106run :: JSM () -> IO ()
124run :: JSM () -> IO ()
107run main = do
125run main = do
108 _ <- Gtk.init Nothing
126 _ <- Gtk.init Nothing
@@ -127,17 +145,17 @@ run main = do
127 void . onWebViewLoadChanged webView $ \case
145 void . onWebViewLoadChanged webView $ \case
128 LoadEventFinished -> runInWebView main webView
146 LoadEventFinished -> runInWebView main webView
129 _ -> return ()
147 _ -> return ()
130− webViewLoadHtml webView "" . Just $ "file://" <> T.pack pwd <> "/index.html"
148+ webViewLoadHtml webView "" . Just $ "file://" <> T.pack pwd <> "/"
131 installQuitHandler webView
149 installQuitHandler webView
132 Gtk.main
150 Gtk.main
133
151
134runInWebView :: JSM () -> WebView -> IO ()
152runInWebView :: JSM () -> WebView -> IO ()
135runInWebView f webView = do
153runInWebView f webView = do
136− (processResults, syncResults, start) <- runJavaScript (\batch -> postGUIAsync $
154+ (processResults, start) <- runJavaScript (\batch -> postGUIAsync $
137 webViewRunJavascript webView (decodeUtf8 . toStrict $ "runJSaddleBatch(" <> encode batch <> ");") noCancellable Nothing)
155 webViewRunJavascript webView (decodeUtf8 . toStrict $ "runJSaddleBatch(" <> encode batch <> ");") noCancellable Nothing)
138 f
156 f
139
157
140− addJSaddleHandler webView processResults syncResults
158+ addJSaddleHandler webView processResults
141 webViewRunJavascript webView (decodeUtf8 $ toStrict jsaddleJs) noCancellable . Just $
159 webViewRunJavascript webView (decodeUtf8 $ toStrict jsaddleJs) noCancellable . Just $
142 \_obj _asyncResult ->
160 \_obj _asyncResult ->
143 void $ forkIO start
161 void $ forkIO start
@@ -151,32 +169,17 @@ connectUserContentManagerScriptMessageReceived obj cb after = liftIO $ do
151 let cb' = wrap_UserContentManagerScriptMessageReceivedCallback cb
169 let cb' = wrap_UserContentManagerScriptMessageReceivedCallback cb
152 cb'' <- mk_UserContentManagerScriptMessageReceivedCallback cb'
170 cb'' <- mk_UserContentManagerScriptMessageReceivedCallback cb'
153 connectSignalFunPtr obj "script-message-received::jsaddle" cb'' after
171 connectSignalFunPtr obj "script-message-received::jsaddle" cb'' after
154−#if MIN_VERSION_haskell_gi_base(0,23,0)
155− Nothing
156−#endif
157
172
158−addJSaddleHandler :: WebView -> (Results -> IO ()) -> (Results -> IO Batch) -> IO ()
173+addJSaddleHandler :: WebView -> (Results -> IO ()) -> IO ()
159−addJSaddleHandler webView processResult syncResults = do
174+addJSaddleHandler webView processResult = do
160 manager <- webViewGetUserContentManager webView
175 manager <- webViewGetUserContentManager webView
161 _ <- onUserContentManagerScriptMessageReceived manager $ \result -> do
176 _ <- onUserContentManagerScriptMessageReceived manager $ \result -> do
162− arg <- javascriptResultGetJsValue result
177+ ctx <- javascriptResultGetGlobalContext result
163− bs <- encodeUtf8 <$> valueToString arg
178+ arg <- javascriptResultGetValue result
179+ bs <- withJSContextRef ctx $ \ctxRef ->
180+ withJSValueRef arg $ \argRef ->
181+ encodeUtf8 <$> valueToText ctxRef argRef
164 mapM_ processResult (decode (fromStrict bs))
182 mapM_ processResult (decode (fromStrict bs))
165− _ <- onWebViewScriptDialog webView $ \dialog ->
166− scriptDialogGetDialogType dialog >>= \case
167− ScriptDialogTypePrompt ->
168− scriptDialogGetMessage dialog >>= \case
169− "JSaddleSync" -> do
170− resultsText <- scriptDialogPromptGetDefaultText dialog
171− case decode (fromStrict $ encodeUtf8 resultsText) of
172− Just results -> do
173− batch <- syncResults results
174− scriptDialogPromptSetText dialog (decodeUtf8 . toStrict $ encode batch)
175− return True
176− Nothing -> return False
177− _ -> return False
178− _ -> return False
179−
180 void $ userContentManagerRegisterScriptMessageHandler manager "jsaddle"
183 void $ userContentManagerRegisterScriptMessageHandler manager "jsaddle"
181
184
182jsaddleJs :: LB.ByteString
185jsaddleJs :: LB.ByteString
@@ -185,7 +188,6 @@ jsaddleJs = ghcjsHelpers <> mconcat
185 , initState
188 , initState
186 , "\nreturn function(batch) {\n"
189 , "\nreturn function(batch) {\n"
187 , runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));\n")
190 , runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));\n")
188− (Just (\a -> "JSON.parse(window.prompt(\"JSaddleSync\", JSON.stringify(" <> a <> ")))"))
189 , "};\n"
191 , "};\n"
190 , "})()"
192 , "})()"
191 ]
193 ]
jsaddle-webkitgtk/default.nixnew file+16
@@ -0,0 +1,16 @@
1+{ mkDerivation, aeson, base, bytestring, directory, gi-glib, gi-gtk
2+, gi-javascriptcore, gi-webkit, haskell-gi-base, jsaddle, stdenv
3+, text, unix, webkitgtk3-javascriptcore
4+}:
5+mkDerivation {
6+ pname = "jsaddle-webkitgtk";
7+ version = "0.8.2.2";
8+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
9+ libraryHaskellDepends = [
10+ aeson base bytestring directory gi-glib gi-gtk gi-javascriptcore
11+ gi-webkit haskell-gi-base jsaddle text unix
12+ webkitgtk3-javascriptcore
13+ ];
14+ description = "Interface for JavaScript that works with GHCJS and GHC";
15+ license = stdenv.lib.licenses.mit;
16+}
jsaddle-webkitgtk/jsaddle-webkitgtk.cabal+3−3
@@ -1,5 +1,5 @@
1name: jsaddle-webkitgtk
1name: jsaddle-webkitgtk
2−version: 0.9.0.0
2+version: 0.8.3.0
3cabal-version: >=1.10
3cabal-version: >=1.10
4build-type: Simple
4build-type: Simple
5license: MIT
5license: MIT
@@ -23,7 +23,7 @@ library
23 exposed-modules:
23 exposed-modules:
24 Language.Javascript.JSaddle.WebKitGTK
24 Language.Javascript.JSaddle.WebKitGTK
25 build-depends:
25 build-depends:
26− aeson >=0.8.0.2 && <1.3,
26+ aeson >=0.8.0.2 && <1.2,
27 base <5,
27 base <5,
28 bytestring >=0.10.6.0 && <0.11,
28 bytestring >=0.10.6.0 && <0.11,
29 directory >=1.0.0.2 && <1.4,
29 directory >=1.0.0.2 && <1.4,
@@ -32,7 +32,7 @@ library
32 gi-webkit >=3.0.6 && <3.1,
32 gi-webkit >=3.0.6 && <3.1,
33 gi-javascriptcore >=3.0.6 && <3.1,
33 gi-javascriptcore >=3.0.6 && <3.1,
34 haskell-gi-base >=0.20 && <0.21,
34 haskell-gi-base >=0.20 && <0.21,
35− jsaddle >=0.9.0.0 && <0.10,
35+ jsaddle >=0.8.0.0 && <0.9,
36 text >=1.2.1.3 && <1.3,
36 text >=1.2.1.3 && <1.3,
37 webkitgtk3-javascriptcore >=0.14.0.0 && <0.15
37 webkitgtk3-javascriptcore >=0.14.0.0 && <0.15
38 if !os(windows)
38 if !os(windows)
jsaddle-webkitgtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+2−2
@@ -185,7 +185,7 @@ runInWebView f webView = do
185 jsvalueprotect ctxRef result
185 jsvalueprotect ctxRef result
186 return result
186 return result
187
187
188− (processResults, _, start) <- runJavaScript (\batch -> postGUIAsync $
188+ (processResults, start) <- runJavaScript (\batch -> postGUIAsync $
189 withJSContextRef ctx $ \ctxRef ->
189 withJSContextRef ctx $ \ctxRef ->
190 withJSString (decodeUtf8 . toStrict $ encode batch) $ \script -> do
190 withJSString (decodeUtf8 . toStrict $ encode batch) $ \script -> do
191 val <- jsvaluemakestring ctxRef script
191 val <- jsvaluemakestring ctxRef script
@@ -222,7 +222,7 @@ jsaddleJs = ghcjsHelpers <> mconcat
222 , initState
222 , initState
223 , "\nreturn function(batchJSON) {\n"
223 , "\nreturn function(batchJSON) {\n"
224 , " var batch = JSON.parse(batchJSON);\n"
224 , " var batch = JSON.parse(batchJSON);\n"
225− , runBatch (\a -> "window.jsaddleCallback(JSON.stringify(" <> a <> "));\n") Nothing
225+ , runBatch (\a -> "window.jsaddleCallback(JSON.stringify(" <> a <> "));\n")
226 , "};\n"
226 , "};\n"
227 , "})()\n"
227 , "})()\n"
228 ]
228 ]
jsaddle-wkwebview/cbits-cocoa/AppDelegate.m+6−65
@@ -2,10 +2,7 @@
2#import <Cocoa/Cocoa.h>
2#import <Cocoa/Cocoa.h>
3#import <WebKit/WebKit.h>
3#import <WebKit/WebKit.h>
4
4
5−extern void callIO(HsStablePtr);
6−extern bool callWithCStringReturningBool(const char * _Nonnull, HsStablePtr);
7−extern void callWithCString(const char * _Nonnull, HsStablePtr);
8−extern void callWithWebView(WKWebView *, HsStablePtr);
5+extern void withWebView(WKWebView *, HsStablePtr);
9
6
10@interface AppDelegate : NSObject <NSApplicationDelegate>
7@interface AppDelegate : NSObject <NSApplicationDelegate>
11@property (nonatomic, assign) IBOutlet NSWindow *window;
8@property (nonatomic, assign) IBOutlet NSWindow *window;
@@ -14,12 +11,6 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
14- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
11- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
15@end
12@end
16
13
17−HsStablePtr global_willFinishLaunchingWithOptions = 0;
18−HsStablePtr global_didFinishLaunchingWithOptions = 0;
19−HsStablePtr global_applicationUniversalLink = 0;
20−HsStablePtr global_applicationOpenFile = 0;
21−uint64_t global_developerExtrasEnabled = 1;
22−
23@implementation AppDelegate
14@implementation AppDelegate
24
15
25- (void)applicationWillTerminate:(NSNotification *)aNotification {
16- (void)applicationWillTerminate:(NSNotification *)aNotification {
@@ -40,39 +31,18 @@ uint64_t global_developerExtrasEnabled = 1;
40 return self;
31 return self;
41}
32}
42
33
43−-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
44− bool b = callWithCStringReturningBool([filename UTF8String], global_applicationOpenFile);
45− return b ? YES : NO;
46−}
47−
48-(void)applicationWillFinishLaunching:(NSNotification *)notification {
34-(void)applicationWillFinishLaunching:(NSNotification *)notification {
49 WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
35 WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
50− if (global_developerExtrasEnabled) {
51− [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
36+ [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
52− }
53− WKWebView *webView = [[WKWebView alloc] initWithFrame: [_window.contentView frame] configuration:theConfiguration];
37+ WKWebView *webView = [[WKWebView alloc] initWithFrame:_window.contentView.frame configuration:theConfiguration];
54 [_window setContentView:webView];
38 [_window setContentView:webView];
55− callWithWebView(webView, _handler);
39+ withWebView(webView, _handler);
56− callIO(global_willFinishLaunchingWithOptions);
57− // Listen to URL events
58− NSAppleEventManager *manager = [NSAppleEventManager sharedAppleEventManager];
59− [manager
60− setEventHandler:self
61− andSelector:@selector(applicationUniversalLink:)
62− forEventClass:kInternetEventClass
63− andEventID:kAEGetURL];
64−}
65−
66−- (void)applicationUniversalLink:(NSAppleEventDescriptor *)event {
67− NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
68− callWithCString([url UTF8String], global_applicationUniversalLink);
69}
40}
70
41
71-(void)applicationDidFinishLaunching:(NSNotification *)notification {
42-(void)applicationDidFinishLaunching:(NSNotification *)notification {
72 [_window orderFrontRegardless];
43 [_window orderFrontRegardless];
73 [_window center];
44 [_window center];
74 [NSApp activateIgnoringOtherApps:YES];
45 [NSApp activateIgnoringOtherApps:YES];
75− callIO(global_didFinishLaunchingWithOptions);
76}
46}
77
47
78-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
48-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
@@ -81,42 +51,13 @@ uint64_t global_developerExtrasEnabled = 1;
81
51
82@end
52@end
83
53
84−void runInWKWebView(HsStablePtr handler,
54+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
85− const char * _Nonnull progName,
86− HsStablePtr hs_willFinishLaunchingWithOptions,
87− HsStablePtr hs_didFinishLaunchingWithOptions,
88− HsStablePtr hs_applicationDidBecomeActive,
89− HsStablePtr hs_applicationWillResignActive,
90− HsStablePtr hs_applicationDidEnterBackground,
91− HsStablePtr hs_applicationWillEnterForeground,
92− HsStablePtr hs_applicationWillTerminate,
93− HsStablePtr hs_applicationSignificantTimeChange,
94− HsStablePtr hs_applicationUniversalLink,
95− const uint64_t hs_requestAuthorizationWithOptions,
96− const uint64_t hs_requestAuthorizationOptionBadge,
97− const uint64_t hs_requestAuthorizationOptionSound,
98− const uint64_t hs_requestAuthorizationOptionAlert,
99− const uint64_t hs_requestAuthorizationOptionCarPlay,
100− const uint64_t hs_registerForRemoteNotifications,
101− HsStablePtr hs_didRegisterForRemoteNotificationsWithDeviceToken,
102− HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError,
103− const uint64_t hs_developerExtrasEnabled,
104− HsStablePtr hs_applicationOpenFile) {
105 @autoreleasepool {
55 @autoreleasepool {
106− global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
107− global_didFinishLaunchingWithOptions = hs_didFinishLaunchingWithOptions;
108− global_applicationUniversalLink = hs_applicationUniversalLink;
109− global_applicationOpenFile = hs_applicationOpenFile;
110− global_developerExtrasEnabled = hs_developerExtrasEnabled;
111 NSApplication *application = [NSApplication sharedApplication];
56 NSApplication *application = [NSApplication sharedApplication];
112 AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
57 AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
113 [application setDelegate:appDelegate];
58 [application setDelegate:appDelegate];
114 const char * _Nonnull argv [] = {"", 0};
59 const char * _Nonnull argv [] = {"", 0};
115− [application setActivationPolicy:NSApplicationActivationPolicyRegular];
60+ NSApplicationMain(0, argv);
116− [application
117− performSelectorOnMainThread:@selector(run)
118− withObject:nil
119− waitUntilDone:YES];
120 }
61 }
121}
62}
122
63
jsaddle-wkwebview/cbits-uikit/AppDelegate.m+12−134
@@ -1,197 +1,75 @@
1#import "AppDelegate.h"
1#import "AppDelegate.h"
2#import "ViewController.h"
2#import "ViewController.h"
3#import <UserNotifications/UserNotifications.h>
3#import <UserNotifications/UserNotifications.h>
4−#import <stdint.h>
5
4
6−extern void callIO(HsStablePtr);
5+extern void didRegisterForRemoteNotificationsWithDeviceTokenCallback(HsStablePtr, const char * _Nonnull);
7−extern void callWithCString(const char * _Nonnull, HsStablePtr);
8−extern void callWithCIntCString(int n, const char * _Nonnull, HsStablePtr);
9
6
10@interface AppDelegate ()
7@interface AppDelegate ()
11
8
12@end
9@end
13
10
14HsStablePtr globalHandler = 0;
11HsStablePtr globalHandler = 0;
15−HsStablePtr global_willFinishLaunchingWithOptions = 0;
12+HsStablePtr globalDeviceTokenHook = 0;
16−HsStablePtr global_didFinishLaunchingWithOptions = 0;
17−HsStablePtr global_applicationDidBecomeActive = 0;
18−HsStablePtr global_applicationWillResignActive = 0;
19−HsStablePtr global_applicationDidEnterBackground = 0;
20−HsStablePtr global_applicationWillEnterForeground = 0;
21−HsStablePtr global_applicationWillTerminate = 0;
22−HsStablePtr global_applicationSignificantTimeChange = 0;
23−HsStablePtr global_applicationUniversalLink = 0;
24−HsStablePtr global_applicationDidReceiveRemoteNotification = 0;
25−uint64_t global_requestAuthorizationWithOptions = 0;
26−uint64_t global_requestAuthorizationOptionBadge = 0;
27−uint64_t global_requestAuthorizationOptionSound = 0;
28−uint64_t global_requestAuthorizationOptionAlert = 0;
29−uint64_t global_requestAuthorizationOptionCarPlay = 0;
30−uint64_t global_registerForRemoteNotifications = 0;
31−HsStablePtr global_didRegisterForRemoteNotificationsWithDeviceToken = 0;
32−HsStablePtr global_didFailToRegisterForRemoteNotificationsWithError = 0;
33
13
34@implementation AppDelegate
14@implementation AppDelegate
35
15
36−- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
37− // Tells the delegate that the launch process has begun but that state restoration has not yet occurred.
38− callIO(global_willFinishLaunchingWithOptions);
39− return YES;
40−}
41−
42- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
16- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
43− // Tells the delegate that the launch process is almost done and the app is almost ready to run.
44 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
17 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
45 // Override point for customization after application launch.
18 // Override point for customization after application launch.
46 self.window.rootViewController = [[ViewController alloc] initWithHandler:globalHandler];
19 self.window.rootViewController = [[ViewController alloc] initWithHandler:globalHandler];
47 self.window.backgroundColor = [UIColor whiteColor];
20 self.window.backgroundColor = [UIColor whiteColor];
48 [self.window makeKeyAndVisible];
21 [self.window makeKeyAndVisible];
49 UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
22 UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
50− center.delegate = self;
51− if (global_requestAuthorizationWithOptions) {
52− UNAuthorizationOptions options = (UNAuthorizationOptions)0;
53− if (global_requestAuthorizationOptionBadge) {
54− options = options | UNAuthorizationOptionBadge;
55− }
56− if (global_requestAuthorizationOptionSound) {
57− options = options | UNAuthorizationOptionSound;
58− }
59− if (global_requestAuthorizationOptionAlert) {
60− options = options | UNAuthorizationOptionAlert;
61− }
62− if (global_requestAuthorizationOptionCarPlay) {
63− options = options | UNAuthorizationOptionCarPlay;
64− }
65− [center requestAuthorizationWithOptions:(options)
23+ [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge)
66− completionHandler:^(BOOL granted, NSError * _Nullable error) {
24+ completionHandler:^(BOOL granted, NSError * _Nullable error) {
67− // Handler used to alter application behavior based on types of notifications authorized
25+ // Handler used to alter application behavior based on types of notifications authorized
26+ // Perhaps we don't need to do anything here.
68− }];
27+ }];
69− if (global_registerForRemoteNotifications) {
70− [application registerForRemoteNotifications];
28+ [application registerForRemoteNotifications];
71− }
72− }
73− callIO(global_didFinishLaunchingWithOptions);
74 return YES;
29 return YES;
75}
30}
76
31
77- (void)applicationWillResignActive:(UIApplication *)application {
32- (void)applicationWillResignActive:(UIApplication *)application {
78 // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
33 // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
79 // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
34 // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
80− callIO(global_applicationWillResignActive);
81}
35}
82
36
83
37
84- (void)applicationDidEnterBackground:(UIApplication *)application {
38- (void)applicationDidEnterBackground:(UIApplication *)application {
85 // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
39 // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
86 // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
40 // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
87− callIO(global_applicationDidEnterBackground);
88}
41}
89
42
90
43
91- (void)applicationWillEnterForeground:(UIApplication *)application {
44- (void)applicationWillEnterForeground:(UIApplication *)application {
92 // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
45 // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
93− callIO(global_applicationWillEnterForeground);
94}
46}
95
47
96
48
97- (void)applicationDidBecomeActive:(UIApplication *)application {
49- (void)applicationDidBecomeActive:(UIApplication *)application {
98 // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
50 // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
99− callIO(global_applicationDidBecomeActive);
100}
51}
101
52
102
53
103- (void)applicationWillTerminate:(UIApplication *)application {
54- (void)applicationWillTerminate:(UIApplication *)application {
104 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
55 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
105− callIO(global_applicationWillTerminate);
106}
56}
107
57
108- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
58- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
109− // Tells the delegate that the app successfully registered with Apple Push Notification service (APNs).
110 NSString *deviceTokenString = [deviceToken base64EncodedStringWithOptions: 0];
59 NSString *deviceTokenString = [deviceToken base64EncodedStringWithOptions: 0];
111− callWithCString([deviceTokenString UTF8String], global_didRegisterForRemoteNotificationsWithDeviceToken);
60+ didRegisterForRemoteNotificationsWithDeviceTokenCallback(globalDeviceTokenHook, [deviceTokenString UTF8String]);
112−}
113−
114−- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
115− // Sent when the application receives remote notifications in the foreground or background
116− // TODO Allow a configurable CString -> IO () to be passed into AppDelegateConfig
117− if ([userInfo valueForKeyPath:@"aps.badge"] != nil) {
118− [UIApplication sharedApplication].applicationIconBadgeNumber=[[[userInfo objectForKey:@"aps"] objectForKey:@"badge"] intValue];
119− }
120− NSError *error;
121− NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfo options:0 error:&error];
122− if (jsonData) {
123− NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
124− callWithCIntCString((int) application.applicationState, [jsonString UTF8String], global_applicationDidReceiveRemoteNotification);
125− } else {
126− NSLog(@"jsaddle-wkwebview didReceiveRemoteNotification handler: error while serialising push notification as JSON");
127− }
128− completionHandler(UIBackgroundFetchResultNewData);
129}
61}
130
62
131- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
63- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
132− // Sent to the delegate when Apple Push Notification service cannot successfully complete the registration process.
64+ // Called when remote notification registration failed
133− NSString *errorString = [error localizedDescription];
134− callWithCString([errorString UTF8String], global_didFailToRegisterForRemoteNotificationsWithError);
135−}
136−
137−- (void)applicationSignificantTimeChange:(UIApplication *)application {
138− // Tells the delegate when there is a significant change in the time.
139− callIO(global_applicationSignificantTimeChange);
140−}
141−
142−- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
143− // TODO: Reroute universal links when they're clicked in-app.
144− // https://developer.apple.com/reference/webkit/wknavigationdelegate/1455643-webview?language=objc
145− if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb] && userActivity.webpageURL) {
146− callWithCString([userActivity.webpageURL.absoluteString UTF8String], global_applicationUniversalLink);
147− return YES;
148− }
149− return NO;
150}
65}
151
66
152@end
67@end
153
68
154−void runInWKWebView(HsStablePtr handler,
69+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName, HsStablePtr deviceTokenHook) {
155− const char * _Nonnull progName,
156− HsStablePtr hs_willFinishLaunchingWithOptions,
157− HsStablePtr hs_didFinishLaunchingWithOptions,
158− HsStablePtr hs_applicationDidBecomeActive,
159− HsStablePtr hs_applicationWillResignActive,
160− HsStablePtr hs_applicationDidEnterBackground,
161− HsStablePtr hs_applicationWillEnterForeground,
162− HsStablePtr hs_applicationWillTerminate,
163− HsStablePtr hs_applicationSignificantTimeChange,
164− HsStablePtr hs_applicationUniversalLink,
165− HsStablePtr hs_applicationDidReceiveRemoteNotification,
166− const uint64_t hs_requestAuthorizationWithOptions,
167− const uint64_t hs_requestAuthorizationOptionBadge,
168− const uint64_t hs_requestAuthorizationOptionSound,
169− const uint64_t hs_requestAuthorizationOptionAlert,
170− const uint64_t hs_requestAuthorizationOptionCarPlay,
171− const uint64_t hs_registerForRemoteNotifications,
172− HsStablePtr hs_didRegisterForRemoteNotificationsWithDeviceToken,
173− HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError,
174− const uint64_t hs_developerExtrasEnabled) {
175 @autoreleasepool {
70 @autoreleasepool {
176 globalHandler = handler;
71 globalHandler = handler;
177− global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
72+ globalDeviceTokenHook = deviceTokenHook;
178− global_didFinishLaunchingWithOptions = hs_didFinishLaunchingWithOptions;
179− global_applicationDidBecomeActive = hs_applicationDidBecomeActive;
180− global_applicationWillResignActive = hs_applicationWillResignActive;
181− global_applicationDidEnterBackground = hs_applicationDidEnterBackground;
182− global_applicationWillEnterForeground = hs_applicationWillEnterForeground;
183− global_applicationWillTerminate = hs_applicationWillTerminate;
184− global_applicationSignificantTimeChange = hs_applicationSignificantTimeChange;
185− global_applicationUniversalLink = hs_applicationUniversalLink;
186− global_applicationDidReceiveRemoteNotification = hs_applicationDidReceiveRemoteNotification;
187− global_requestAuthorizationWithOptions = hs_requestAuthorizationWithOptions;
188− global_requestAuthorizationOptionBadge = hs_requestAuthorizationOptionBadge;
189− global_requestAuthorizationOptionSound = hs_requestAuthorizationOptionSound;
190− global_requestAuthorizationOptionAlert = hs_requestAuthorizationOptionAlert;
191− global_requestAuthorizationOptionCarPlay = hs_requestAuthorizationOptionCarPlay;
192− global_registerForRemoteNotifications = hs_registerForRemoteNotifications;
193− global_didRegisterForRemoteNotificationsWithDeviceToken = hs_didRegisterForRemoteNotificationsWithDeviceToken;
194− global_didFailToRegisterForRemoteNotificationsWithError = hs_didFailToRegisterForRemoteNotificationsWithError;
195 const char * _Nonnull argv [] = {"", 0};
73 const char * _Nonnull argv [] = {"", 0};
196 UIApplicationMain(0, argv, nil, NSStringFromClass([AppDelegate class]));
74 UIApplicationMain(0, argv, nil, NSStringFromClass([AppDelegate class]));
197 }
75 }
jsaddle-wkwebview/cbits-uikit/ViewController.m+2−2
@@ -1,6 +1,6 @@
1#import "ViewController.h"
1#import "ViewController.h"
2
2
3−extern void callWithWebView(WKWebView *, HsStablePtr);
3+extern void withWebView(WKWebView *, HsStablePtr);
4
4
5@interface ViewController ()
5@interface ViewController ()
6
6
@@ -27,7 +27,7 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
27
27
28- (void)viewDidLoad {
28- (void)viewDidLoad {
29 [super viewDidLoad];
29 [super viewDidLoad];
30− callWithWebView(_webView, _handler);
30+ withWebView(_webView, _handler);
31}
31}
32
32
33
33
jsaddle-wkwebview/cbits/WKWebView.m+18−42
@@ -4,30 +4,26 @@
4
4
5BOOL openApp(NSURL * url);
5BOOL openApp(NSURL * url);
6
6
7+extern void jsaddleStart(HsStablePtr);
8+extern void jsaddleResult(HsStablePtr, const char * _Nonnull result);
9+
7−@interface JSaddleHandler : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
10+@interface JSaddleHandler : NSObject<WKNavigationDelegate, WKScriptMessageHandler>
8
11
9@property (nonatomic, assign) HsStablePtr startHandler;
12@property (nonatomic, assign) HsStablePtr startHandler;
10@property (nonatomic, assign) HsStablePtr resultHandler;
13@property (nonatomic, assign) HsStablePtr resultHandler;
11−@property (nonatomic, assign) HsStablePtr syncHandler;
12−@property (nonatomic, assign) void (^completionHandler)(NSString *result);
13
14
14−- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler syncHandler:(HsStablePtr)syncHandler;
15+- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler;
15
16
16@end
17@end
17
18
18−extern void jsaddleStart(HsStablePtr);
19−extern void jsaddleResult(HsStablePtr, const char * _Nonnull result);
20−extern void jsaddleSyncResult(HsStablePtr, JSaddleHandler *, const char * _Nonnull result);
21−
22@implementation JSaddleHandler
19@implementation JSaddleHandler
23
20
24−- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler syncHandler:(HsStablePtr)syncHandler
21+- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler
25{
22{
26 self = [super init];
23 self = [super init];
27 if (self) {
24 if (self) {
28 _startHandler = startHandler;
25 _startHandler = startHandler;
29 _resultHandler = resultHandler;
26 _resultHandler = resultHandler;
30− _syncHandler = syncHandler;
31 }
27 }
32 return self;
28 return self;
33}
29}
@@ -43,27 +39,15 @@ extern void jsaddleSyncResult(HsStablePtr, JSaddleHandler *, const char * _Nonnu
43 jsaddleResult(self.resultHandler, [s UTF8String]);
39 jsaddleResult(self.resultHandler, [s UTF8String]);
44}
40}
45
41
46−- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
47− defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *result))completionHandler
48−{
49− if([prompt isEqualToString:@"JSaddleSync"]) {
50− _completionHandler = completionHandler;
51− jsaddleSyncResult(self.syncHandler, self, [defaultText UTF8String]);
52− }
53− else {
54− // TODO decide what if anything should go here
55− }
56−}
57−
58- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
42- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
59{
43{
60 NSURL *url = navigationAction.request.URL;
44 NSURL *url = navigationAction.request.URL;
61
45
62 if(!navigationAction.targetFrame && ![url.scheme isEqualToString:@"file"]) {
46 if(!navigationAction.targetFrame && ![url.scheme isEqualToString:@"file"]) {
63 if(openApp(url))
47 if(openApp(url))
64− decisionHandler(WKNavigationActionPolicyCancel);
48+ decisionHandler(WKNavigationActionPolicyCancel);
65− else
49+ else
66− decisionHandler(WKNavigationActionPolicyAllow);
50+ decisionHandler(WKNavigationActionPolicyAllow);
67 }
51 }
68 else
52 else
69 decisionHandler(WKNavigationActionPolicyAllow);
53 decisionHandler(WKNavigationActionPolicyAllow);
@@ -71,14 +55,10 @@ extern void jsaddleSyncResult(HsStablePtr, JSaddleHandler *, const char * _Nonnu
71
55
72@end
56@end
73
57
74−void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler, HsStablePtr syncHandler) {
58+void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler) {
75− JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler syncHandler:syncHandler];
59+ JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler];
76 [[[webView configuration] userContentController] addScriptMessageHandler:handler name:@"jsaddle"];
60 [[[webView configuration] userContentController] addScriptMessageHandler:handler name:@"jsaddle"];
77−#ifdef USE_UIKIT
78− webView.scrollView.bounces = NO;
79−#endif
80 webView.navigationDelegate = handler;
61 webView.navigationDelegate = handler;
81− webView.UIDelegate = handler;
82}
62}
83
63
84void evaluateJavaScript(WKWebView *webView, const char * _Nonnull js) {
64void evaluateJavaScript(WKWebView *webView, const char * _Nonnull js) {
@@ -89,17 +69,10 @@ void evaluateJavaScript(WKWebView *webView, const char * _Nonnull js) {
89 });
69 });
90}
70}
91
71
92−void completeSync(JSaddleHandler *handler, const char * _Nonnull s) {
93− NSString *string = [[NSString alloc] initWithCString:s encoding:NSUTF8StringEncoding];
94− handler.completionHandler(string);
95− handler.completionHandler = NULL;
96−}
97−
98−void loadHTMLStringWithBaseURL(WKWebView *webView, const char * _Nonnull html, const char * _Nonnull url) {
72+void loadHTMLString(WKWebView *webView, const char * _Nonnull html) {
99− NSString *htmlString = [NSString stringWithCString:html encoding:NSUTF8StringEncoding];
73+ NSString *htmlString = [NSString stringWithCString:html encoding:NSUTF8StringEncoding];
100− NSURL *baseURL = [NSURL URLWithString:[[NSString alloc] initWithCString:url encoding:NSUTF8StringEncoding]];
101− dispatch_async(dispatch_get_main_queue(), ^{
74+ dispatch_async(dispatch_get_main_queue(), ^{
102− [webView loadHTMLString:htmlString baseURL:baseURL];
75+ [webView loadHTMLString:htmlString baseURL:NULL];
103 });
76 });
104}
77}
105
78
@@ -122,3 +95,6 @@ void loadBundleFile(WKWebView *webView, const char * _Nonnull file, const char *
122 }
95 }
123 }
96 }
124}
97}
98+
99+
100+
jsaddle-wkwebview/default.nix+14−14
@@ -1,14 +1,14 @@
1−{ filterSource ? (x: x) }:
1+{ mkDerivation, stdenv, aeson, base, bytestring, jsaddle
2−self: super:
2+, buildPackages
3−let
3+}:
4− pkgs = self.callPackage ({pkgs}: pkgs) {};
4+
5− inherit (pkgs) lib;
5+mkDerivation {
6−in
6+ pname = "jsaddle-wkwebview";
7−with pkgs.haskell.lib;
7+ version = "0.8.2.0";
8−overrideCabal (self.callCabal2nix "jsaddle-wkwebview" (filterSource ./.) {}) (drv: {
8+ src = ./.;
9− libraryFrameworkDepends = (drv.libraryFrameworkDepends or []) ++
9+ libraryHaskellDepends = [ aeson base bytestring jsaddle ]
10− (if pkgs.stdenv.hostPlatform.useiOSPrebuilt then [
10+ # should use `librarySystemDepends` but it is not propagated
11− "${pkgs.buildPackages.darwin.xcode}/Contents/Developer/Platforms/${pkgs.stdenv.hostPlatform.xcodePlatform}.platform/Developer/SDKs/${pkgs.stdenv.hostPlatform.xcodePlatform}.sdk/System"
11+ ++ (with buildPackages; [ osx_sdk darwin.libobjc darwin.apple_sdk.libs.xpc ]);
12− ] else (with pkgs.buildPackages.darwin.apple_sdk.frameworks; [ Cocoa WebKit ]));
12+ description = "Interface for JavaScript that works with GHCJS and GHC";
13− buildDepends = lib.optional (!pkgs.stdenv.hostPlatform.useiOSPrebuilt) [ pkgs.buildPackages.darwin.cf-private ];
13+ license = stdenv.lib.licenses.mit;
14−})
14+}
jsaddle-wkwebview/jsaddle-wkwebview.cabal+9−20
@@ -1,5 +1,5 @@
1name: jsaddle-wkwebview
1name: jsaddle-wkwebview
2−version: 0.9.8.0
2+version: 0.8.3.0
3cabal-version: >=1.10
3cabal-version: >=1.10
4build-type: Simple
4build-type: Simple
5license: MIT
5license: MIT
@@ -25,41 +25,30 @@ source-repository head
25library
25library
26 exposed-modules:
26 exposed-modules:
27 Language.Javascript.JSaddle.WKWebView
27 Language.Javascript.JSaddle.WKWebView
28+ Language.Javascript.JSaddle.WKWebView.Internal
28 build-depends:
29 build-depends:
30+ aeson >=0.8.0.2 && <1.2,
29− base <5
31+ base <5,
32+ bytestring >=0.10.6.0 && <0.11,
33+ jsaddle >= 0.8.0.0 && <0.9
30 default-language: Haskell2010
34 default-language: Haskell2010
31 hs-source-dirs: src
35 hs-source-dirs: src
36+ frameworks: Foundation, WebKit
32 ghc-options: -ferror-spans -Wall
37 ghc-options: -ferror-spans -Wall
33− if os(linux)
38+ c-sources:
34− buildable: False
39+ cbits/WKWebView.m
35 if impl(ghcjs)
40 if impl(ghcjs)
36 hs-source-dirs: src-ghcjs
41 hs-source-dirs: src-ghcjs
37 else
42 else
38− frameworks: Foundation, WebKit
39− build-depends:
40− aeson >=0.8.0.2 && <1.6,
41− bytestring >=0.10.6.0 && <0.11,
42− directory,
43− jsaddle >= 0.9.4.0 && <0.10,
44− text,
45− data-default,
46− containers
47− exposed-modules:
48− Language.Javascript.JSaddle.WKWebView.Internal
49 hs-source-dirs: src-ghc
43 hs-source-dirs: src-ghc
50− c-sources:
51− cbits/WKWebView.m
52− cc-options: -Wno-everything
53 if os(ios)
44 if os(ios)
54 frameworks: UIKit, UserNotifications
45 frameworks: UIKit, UserNotifications
55 if flag(include-app-delegate)
46 if flag(include-app-delegate)
56 c-sources:
47 c-sources:
57 cbits-uikit/AppDelegate.m
48 cbits-uikit/AppDelegate.m
58 cbits-uikit/ViewController.m
49 cbits-uikit/ViewController.m
59− cpp-options: -DUSE_UIKIT
60 else
50 else
61 frameworks: Cocoa
51 frameworks: Cocoa
62 if flag(include-app-delegate)
52 if flag(include-app-delegate)
63 c-sources:
53 c-sources:
64 cbits-cocoa/AppDelegate.m
54 cbits-cocoa/AppDelegate.m
65− cpp-options: -DUSE_COCOA
jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+15−197
@@ -1,219 +1,37 @@
1module Language.Javascript.JSaddle.WKWebView
1module Language.Javascript.JSaddle.WKWebView
2 ( jsaddleMain
2 ( jsaddleMain
3− , jsaddleMainHTMLWithBaseURL
4 , jsaddleMainFile
3 , jsaddleMainFile
5 , WKWebView(..)
4 , WKWebView(..)
6 , run
5 , run
7− , run'
8− , runWithAppConfig
9− , runHTMLWithBaseURL
10 , runFile
6 , runFile
11 , mainBundleResourcePath
7 , mainBundleResourcePath
12− , ApplicationState (..)
13− , AppDelegateConfig (..)
14− , AppDelegateNotificationConfig (..)
15− , AuthorizationOption (..)
16 ) where
8 ) where
17
9
18import Data.ByteString (ByteString)
10import Data.ByteString (ByteString)
19−import Data.Default (Default, def)
20−import Data.Set (Set)
21−import qualified Data.Set as Set
22−import Data.Word
23−import Foreign.Marshal.Utils (fromBool)
24−import Language.Javascript.JSaddle.WKWebView.Internal
25− (jsaddleMain, jsaddleMainHTMLWithBaseURL, jsaddleMainFile, WKWebView(..), mainBundleResourcePath)
11+import Language.Javascript.JSaddle.WKWebView.Internal (jsaddleMain, jsaddleMainFile, WKWebView(..), mainBundleResourcePath)
26import System.Environment (getProgName)
12import System.Environment (getProgName)
27−import Foreign.C.String (CString, withCString)
13+import Foreign.C.String (CString, withCString, peekCString)
28−import Foreign.C.Types (CBool, CInt)
29import Foreign.StablePtr (StablePtr, newStablePtr)
14import Foreign.StablePtr (StablePtr, newStablePtr)
30import Language.Javascript.JSaddle (JSM)
15import Language.Javascript.JSaddle (JSM)
31
16
32−foreign import ccall runInWKWebView
17+foreign import ccall runInWKWebView :: StablePtr (WKWebView -> IO ()) -> CString -> StablePtr (CString -> IO ()) -> IO ()
33− :: StablePtr (WKWebView -> IO ())
34− -> CString
35− -> StablePtr (IO ()) -- willFinishLaunchingWithOptions
36− -> StablePtr (IO ()) -- didFinishLaunchingWithOptions
37− -> StablePtr (IO ()) -- applicationDidBecomeActive
38− -> StablePtr (IO ()) -- applicationWillResignActive
39− -> StablePtr (IO ()) -- applicationDidEnterBackground
40− -> StablePtr (IO ()) -- applicationWillEnterForeground
41− -> StablePtr (IO ()) -- applicationWillTerminate
42− -> StablePtr (IO ()) -- applicationSignificantTimeChange
43− -> StablePtr (CString -> IO ()) -- applicationUniversalLink
44− -> StablePtr (CInt -> CString -> IO ()) -- applicationDidReceiveRemoteNotification
45− -> Word64 -- whether to run requestAuthorizationWithOptions
46− -> Word64 -- Ask for Badge authorization
47− -> Word64 -- Ask for Sound authorization
48− -> Word64 -- Ask for Alert authorization
49− -> Word64 -- Ask for CarPlay authorization
50− -> Word64 -- registerForRemoteNotifications
51− -> StablePtr (CString -> IO ()) -- didRegisterForRemoteNotificationsWithDeviceToken
52− -> StablePtr (CString -> IO ()) -- didFailToRegisterForRemoteNotificationsWithError
53− -> Word64 -- developerExtrasEnabled
54− -> StablePtr (CString -> IO CBool) -- applicationOpenFile
55− -> IO ()
56−
57−data AppDelegateConfig = AppDelegateConfig
58− { _appDelegateConfig_willFinishLaunchingWithOptions :: IO ()
59− , _appDelegateConfig_didFinishLaunchingWithOptions :: IO ()
60− , _appDelegateConfig_applicationDidBecomeActive :: IO ()
61− , _appDelegateConfig_applicationWillResignActive :: IO ()
62− , _appDelegateConfig_applicationDidEnterBackground :: IO ()
63− , _appDelegateConfig_applicationWillEnterForeground :: IO ()
64− , _appDelegateConfig_applicationWillTerminate :: IO ()
65− , _appDelegateConfig_applicationSignificantTimeChange :: IO ()
66− , _appDelegateConfig_applicationUniversalLink :: CString -> IO ()
67− -- ^ Called when the app is launched from a URL. 'CString' contains the URL.
68− --
69− -- The Core Foundation plist key CFBundleURLTypes controls which URL schemes
70− -- the app should respond to.
71− , _appDelegateConfig_applicationDidReceiveRemoteNotification :: ApplicationState -> CString -> IO ()
72− -- ^ Called when the application receives a remote notification.
73− -- 'ApplicationState' is the current state of the app.
74− -- The 'CString' JSON-encoded user info contained in the notification.
75− -- For more information see https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623117-application?language=objc
76− , _appDelegateConfig_appDelegateNotificationConfig :: AppDelegateNotificationConfig
77− , _appDelegateConfig_developerExtrasEnabled :: Bool
78− -- ^ Allow devtools in the app. Defaults to 'True'
79− , _appDelegateConfig_applicationOpenFile :: CString -> IO Bool
80− -- ^ Called when the app is launched by opening a file. 'CString' contains
81− -- the file path. Return value is 'True' if the file was successfully
82− -- opened. Defaults to ignoring the file and returning 'False'.
83− --
84− -- The Core Foundation plist key CFBundleDocumentTypes controls which file
85− -- types should be associated with the app.
86− }
87−
88−instance Default AppDelegateConfig where
89− def = AppDelegateConfig
90− { _appDelegateConfig_willFinishLaunchingWithOptions = return ()
91− , _appDelegateConfig_didFinishLaunchingWithOptions = return ()
92− , _appDelegateConfig_applicationDidBecomeActive = return ()
93− , _appDelegateConfig_applicationWillResignActive = return ()
94− , _appDelegateConfig_applicationDidEnterBackground = return ()
95− , _appDelegateConfig_applicationWillEnterForeground = return ()
96− , _appDelegateConfig_applicationWillTerminate = return ()
97− , _appDelegateConfig_applicationSignificantTimeChange = return ()
98− , _appDelegateConfig_applicationUniversalLink = \_ -> return ()
99− , _appDelegateConfig_applicationDidReceiveRemoteNotification = \_ _ -> return ()
100− , _appDelegateConfig_appDelegateNotificationConfig = def
101− , _appDelegateConfig_developerExtrasEnabled = True
102− , _appDelegateConfig_applicationOpenFile = \_ -> return False
103− }
104−
105−data AuthorizationOption = AuthorizationOption_Badge
106− | AuthorizationOption_Sound
107− | AuthorizationOption_Alert
108− | AuthorizationOption_CarPlay
109− deriving (Show, Read, Eq, Ord)
110−
111−data ApplicationState = ApplicationState_Active
112− | ApplicationState_Inactive
113− | ApplicationState_Background
114− | ApplicationState_Unknown CInt
115− deriving (Show, Read, Eq, Ord)
116−
117−data AppDelegateNotificationConfig = AppDelegateNotificationConfig
118− { _appDelegateNotificationConfig_requestAuthorizationWithOptions :: Maybe (Set AuthorizationOption)
119− , _appDelegateNotificationConfig_registerForRemoteNotifications :: Bool
120− , _appDelegateNotificationConfig_didRegisterForRemoteNotificationsWithDeviceToken :: CString -> IO ()
121− , _appDelegateNotificationConfig_didFailToRegisterForRemoteNotificationsWithError :: CString -> IO ()
122− }
123−
124−instance Default AppDelegateNotificationConfig where
125− def = AppDelegateNotificationConfig
126− { _appDelegateNotificationConfig_requestAuthorizationWithOptions = Nothing
127− , _appDelegateNotificationConfig_registerForRemoteNotifications = False
128− , _appDelegateNotificationConfig_didRegisterForRemoteNotificationsWithDeviceToken = \_ -> return ()
129− , _appDelegateNotificationConfig_didFailToRegisterForRemoteNotificationsWithError = \_ -> return ()
130− }
131−
132
18
133-- | Run JSaddle in a WKWebView
19-- | Run JSaddle in a WKWebView
134run :: JSM () -> IO ()
20run :: JSM () -> IO ()
21+run f = do
135−run f = run' def (jsaddleMain f)
22+ handler <- newStablePtr (jsaddleMain f)
136−
23+ progName <- getProgName
137−-- | Run JSaddle in a WKWebView
24+ deviceTokenHandler <- newStablePtr (\s -> putStrLn =<< peekCString s)
138−runWithAppConfig :: AppDelegateConfig -> JSM () -> IO ()
25+ withCString progName $ \pn -> runInWKWebView handler pn deviceTokenHandler
139−runWithAppConfig cfg = run' cfg . jsaddleMain
140
26
141-- | Run JSaddle in a WKWebView first loading the specified file
27-- | Run JSaddle in a WKWebView first loading the specified file
142-- from the mainBundle (relative to the resourcePath).
28-- from the mainBundle (relative to the resourcePath).
143−runFile
144− :: ByteString -- ^ The file to navigate to.
29+runFile :: ByteString -- ^ The file to navigate to.
145− -> ByteString -- ^ The path to allow read access to.
30+ -> ByteString -- ^ The path to allow read access to.
146− -> AppDelegateConfig
147− -> JSM ()
31+ -> JSM () -> IO ()
148− -> IO ()
149−runFile url allowing cfg = run' cfg . jsaddleMainFile url allowing
32+runFile url allowing f = do
150−
151−-- | Run JSaddle in a WKWebView first loading the specified html
152−-- as though it came from the specified URL
153−-- (calls WKWebKit function loadHTMLString).
154−runHTMLWithBaseURL
155− :: ByteString -- ^ HTML to load.
156− -> ByteString -- ^ pretend it came from this URL.
157− -> AppDelegateConfig
158− -> JSM ()
159− -> IO ()
160−runHTMLWithBaseURL url allowing cfg = run' cfg . jsaddleMainHTMLWithBaseURL url allowing
161−
162−run' :: AppDelegateConfig
163− -> (WKWebView -> IO ())
164− -> IO ()
165−run' cfg main = do
166− handler <- newStablePtr main
33+ handler <- newStablePtr (jsaddleMainFile url allowing f)
167 progName <- getProgName
34 progName <- getProgName
35+ deviceTokenHandler <- newStablePtr (\s -> putStrLn =<< peekCString s)
36+ withCString progName $ \pn -> runInWKWebView handler pn deviceTokenHandler
168
37
169− -- AppDelegate callbacks
170− willFinishLaunchingWithOptions <- newStablePtr $ _appDelegateConfig_willFinishLaunchingWithOptions cfg
171− didFinishLaunchingWithOptions <- newStablePtr $ _appDelegateConfig_didFinishLaunchingWithOptions cfg
172− applicationDidBecomeActive <- newStablePtr $ _appDelegateConfig_applicationDidBecomeActive cfg
173− applicationWillResignActive <- newStablePtr $ _appDelegateConfig_applicationWillResignActive cfg
174− applicationDidEnterBackground <- newStablePtr $ _appDelegateConfig_applicationDidEnterBackground cfg
175− applicationWillEnterForeground <- newStablePtr $ _appDelegateConfig_applicationWillEnterForeground cfg
176− applicationWillTerminate <- newStablePtr $ _appDelegateConfig_applicationWillTerminate cfg
177− applicationSignificantTimeChange <- newStablePtr $ _appDelegateConfig_applicationSignificantTimeChange cfg
178− applicationUniversalLink <- newStablePtr $ _appDelegateConfig_applicationUniversalLink cfg
179− applicationDidReceiveRemoteNotification <- newStablePtr $ \n s -> do
180− let appState = case n of
181− 0 -> ApplicationState_Active
182− 1 -> ApplicationState_Inactive
183− 2 -> ApplicationState_Background
184− _ -> ApplicationState_Unknown n
185− _appDelegateConfig_applicationDidReceiveRemoteNotification cfg appState s
186− applicationOpenFile <- newStablePtr $ fmap fromBool . _appDelegateConfig_applicationOpenFile cfg
187−
188− -- AppDelegate notification configuration
189− let ncfg = _appDelegateConfig_appDelegateNotificationConfig cfg
190− (requestAuthorizationWithOptions, authorizationOptions) =
191− case _appDelegateNotificationConfig_requestAuthorizationWithOptions ncfg of
192− Nothing -> (False, Set.empty)
193− Just opts -> (True, opts)
194− registerForRemoteNotifications = _appDelegateNotificationConfig_registerForRemoteNotifications ncfg
195− didRegisterForRemoteNotificationsWithDeviceToken <- newStablePtr $ _appDelegateNotificationConfig_didRegisterForRemoteNotificationsWithDeviceToken ncfg
196− didFailToRegisterForRemoteNotificationsWithError <- newStablePtr $ _appDelegateNotificationConfig_didFailToRegisterForRemoteNotificationsWithError ncfg
197− withCString progName $ \pn -> runInWKWebView
198− handler
199− pn
200− willFinishLaunchingWithOptions
201− didFinishLaunchingWithOptions
202− applicationDidBecomeActive
203− applicationWillResignActive
204− applicationDidEnterBackground
205− applicationWillEnterForeground
206− applicationWillTerminate
207− applicationSignificantTimeChange
208− applicationUniversalLink
209− applicationDidReceiveRemoteNotification
210− (fromBool requestAuthorizationWithOptions)
211− (fromBool $ Set.member AuthorizationOption_Badge authorizationOptions)
212− (fromBool $ Set.member AuthorizationOption_Sound authorizationOptions)
213− (fromBool $ Set.member AuthorizationOption_Alert authorizationOptions)
214− (fromBool $ Set.member AuthorizationOption_CarPlay authorizationOptions)
215− (fromBool registerForRemoteNotifications)
216− didRegisterForRemoteNotificationsWithDeviceToken
217− didFailToRegisterForRemoteNotificationsWithError
218− (fromBool $ _appDelegateConfig_developerExtrasEnabled cfg)
219− applicationOpenFile
jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+19−71
@@ -1,7 +1,6 @@
1{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
1{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
2module Language.Javascript.JSaddle.WKWebView.Internal
2module Language.Javascript.JSaddle.WKWebView.Internal
3 ( jsaddleMain
3 ( jsaddleMain
4− , jsaddleMainHTMLWithBaseURL
5 , jsaddleMainFile
4 , jsaddleMainFile
6 , WKWebView(..)
5 , WKWebView(..)
7 , mainBundleResourcePath
6 , mainBundleResourcePath
@@ -16,64 +15,39 @@ import Data.ByteString (useAsCString, packCString)
16import qualified Data.ByteString as BS (ByteString)
15import qualified Data.ByteString as BS (ByteString)
17import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
16import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
18import Data.Aeson (encode, decode)
17import Data.Aeson (encode, decode)
19−import qualified Data.Text as T (pack)
20−import Data.Text.Encoding (encodeUtf8)
21
18
22import Foreign.C.String (CString)
19import Foreign.C.String (CString)
23−import Foreign.C.Types (CBool(..), CInt(..))
24import Foreign.Ptr (Ptr, nullPtr)
20import Foreign.Ptr (Ptr, nullPtr)
25import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
21import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
26
22
27−import Language.Javascript.JSaddle (Results, Batch, JSM)
23+import Language.Javascript.JSaddle (Results, JSM)
28import Language.Javascript.JSaddle.Run (runJavaScript)
24import Language.Javascript.JSaddle.Run (runJavaScript)
29import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
25import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
30
26
31−import System.Directory (getCurrentDirectory)
32−
33newtype WKWebView = WKWebView (Ptr WKWebView)
27newtype WKWebView = WKWebView (Ptr WKWebView)
34−newtype JSaddleHandler = JSaddleHandler (Ptr JSaddleHandler)
35
28
36foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO ()
29foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO ()
37foreign export ccall jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
30foreign export ccall jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
38−foreign export ccall jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()
39−foreign export ccall callWithWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
31+foreign export ccall withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
40−foreign export ccall callWithCIntCString :: CInt -> CString -> StablePtr (CInt -> CString -> IO ()) -> IO ()
32+foreign export ccall didRegisterForRemoteNotificationsWithDeviceTokenCallback :: StablePtr (CString -> IO ()) -> CString -> IO ()
41−foreign export ccall callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
42−foreign export ccall callWithCStringReturningBool :: CString -> StablePtr (CString -> IO CBool) -> IO CBool
43−foreign export ccall callIO :: StablePtr (IO ()) -> IO ()
44−foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> StablePtr (Results -> IO Batch) -> IO ()
33+foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> IO ()
45−foreign import ccall loadHTMLStringWithBaseURL :: WKWebView -> CString -> CString -> IO ()
34+foreign import ccall loadHTMLString :: WKWebView -> CString -> IO ()
46foreign import ccall loadBundleFile :: WKWebView -> CString -> CString -> IO ()
35foreign import ccall loadBundleFile :: WKWebView -> CString -> CString -> IO ()
47foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()
36foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()
48−foreign import ccall completeSync :: JSaddleHandler -> CString -> IO ()
49foreign import ccall mainBundleResourcePathC :: IO CString
37foreign import ccall mainBundleResourcePathC :: IO CString
50
38
51-- | Run JSaddle in WKWebView
39-- | Run JSaddle in WKWebView
52jsaddleMain :: JSM () -> WKWebView -> IO ()
40jsaddleMain :: JSM () -> WKWebView -> IO ()
53jsaddleMain f webView = do
41jsaddleMain f webView = do
54− pwd <- getCurrentDirectory
55− let baseURL = encodeUtf8 $ "file://" <> T.pack pwd <> "/index.html"
56− jsaddleMainHTMLWithBaseURL (toStrict indexHtml) baseURL f webView
57−
58−-- | Run JSaddle in WKWebView with initial html and base url
59−jsaddleMainHTMLWithBaseURL
60− :: BS.ByteString -- ^ HTML
61− -> BS.ByteString -- ^ Base URL
62− -> JSM ()
63− -> WKWebView
64− -> IO ()
65−jsaddleMainHTMLWithBaseURL initialHTML baseURL f webView =
66 jsaddleMain' f webView $
42 jsaddleMain' f webView $
67− useAsCString baseURL $ \url ->
43+ useAsCString (toStrict indexHtml) $ loadHTMLString webView
68− useAsCString initialHTML $ \html ->
69− loadHTMLStringWithBaseURL webView html url
70
44
71-- | Run JSaddle in a WKWebView first loading the specified file
45-- | Run JSaddle in a WKWebView first loading the specified file
72-- from the mainBundle (relative to the resourcePath).
46-- from the mainBundle (relative to the resourcePath).
73jsaddleMainFile :: BS.ByteString -- ^ The file to navigate to.
47jsaddleMainFile :: BS.ByteString -- ^ The file to navigate to.
74 -> BS.ByteString -- ^ The path to allow read access to.
48 -> BS.ByteString -- ^ The path to allow read access to.
75 -> JSM () -> WKWebView -> IO ()
49 -> JSM () -> WKWebView -> IO ()
76−jsaddleMainFile url allowing f webView =
50+jsaddleMainFile url allowing f webView = do
77 jsaddleMain' f webView $
51 jsaddleMain' f webView $
78 useAsCString url $ \u ->
52 useAsCString url $ \u ->
79 useAsCString allowing $ \a ->
53 useAsCString allowing $ \a ->
@@ -83,15 +57,14 @@ jsaddleMain' :: JSM () -> WKWebView -> IO () -> IO ()
83jsaddleMain' f webView loadHtml = do
57jsaddleMain' f webView loadHtml = do
84 ready <- newEmptyMVar
58 ready <- newEmptyMVar
85
59
86− (processResult, syncResult, start) <- runJavaScript (\batch ->
60+ (processResult, start) <- runJavaScript (\batch ->
87 useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $
61 useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $
88 evaluateJavaScript webView)
62 evaluateJavaScript webView)
89 f
63 f
90
64
91 startHandler <- newStablePtr (putMVar ready ())
65 startHandler <- newStablePtr (putMVar ready ())
92 resultHandler <- newStablePtr processResult
66 resultHandler <- newStablePtr processResult
93− syncResultHandler <- newStablePtr syncResult
94− addJSaddleHandler webView startHandler resultHandler syncResultHandler
67+ addJSaddleHandler webView startHandler resultHandler
95 loadHtml
68 loadHtml
96 void . forkOS $ do
69 void . forkOS $ do
97 takeMVar ready
70 takeMVar ready
@@ -105,52 +78,26 @@ jsaddleResult ptrHandler s = do
105 processResult <- deRefStablePtr ptrHandler
78 processResult <- deRefStablePtr ptrHandler
106 result <- packCString s
79 result <- packCString s
107 case decode (fromStrict result) of
80 case decode (fromStrict result) of
108− Nothing -> error $ "jsaddle Results decode failed : " <> show result
81+ Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
109 Just r -> processResult r
82 Just r -> processResult r
110
83
111−jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()
112−jsaddleSyncResult ptrHandler jsaddleHandler s = do
113− syncProcessResult <- deRefStablePtr ptrHandler
114− result <- packCString s
115− case decode (fromStrict result) of
116− Nothing -> error $ "jsaddle Results decode failed : " <> show result
117− Just r -> do -- void . forkIO $ do
118− batch <- syncProcessResult r
119− useAsCString (toStrict $ encode batch) $
120− completeSync jsaddleHandler
121−
122−callWithWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
84+withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
123−callWithWebView webView ptrF = do
85+withWebView webView ptrF = do
124 f <- deRefStablePtr ptrF
86 f <- deRefStablePtr ptrF
125 f webView
87 f webView
126
88
127−callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
89+-- | A base64 encoded device token
128−callWithCString c fptr = do
90+didRegisterForRemoteNotificationsWithDeviceTokenCallback :: StablePtr (CString -> IO ()) -> CString -> IO ()
129− f <- deRefStablePtr fptr
91+didRegisterForRemoteNotificationsWithDeviceTokenCallback f token = do
130− f c
131−
132−callWithCIntCString :: CInt -> CString -> StablePtr (CInt -> CString -> IO ()) -> IO ()
133−callWithCIntCString n s fptr = do
134− f <- deRefStablePtr fptr
135− f n s
136−
137−callWithCStringReturningBool :: CString -> StablePtr (CString -> IO CBool) -> IO CBool
138−callWithCStringReturningBool c fptr = do
139− f <- deRefStablePtr fptr
92+ f' <- deRefStablePtr f
140− f c
93+ f' token
141−
142−callIO :: StablePtr (IO ()) -> IO ()
143−callIO ptr = do
144− f <- deRefStablePtr ptr
145− f
146
94
147jsaddleJs :: ByteString
95jsaddleJs :: ByteString
148jsaddleJs = ghcjsHelpers <> "\
96jsaddleJs = ghcjsHelpers <> "\
149 \runJSaddleBatch = (function() {\n\
97 \runJSaddleBatch = (function() {\n\
150 \ " <> initState <> "\n\
98 \ " <> initState <> "\n\
151 \ return function(batch) {\n\
99 \ return function(batch) {\n\
152− \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));\n")
100+ \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));") <> "\
153− (Just (\a -> "JSON.parse(window.prompt(\"JSaddleSync\", JSON.stringify(" <> a <> ")))")) <> "\
154 \ };\n\
101 \ };\n\
155 \})();\n\
102 \})();\n\
156 \"
103 \"
@@ -172,3 +119,4 @@ mainBundleResourcePath = do
172 if bs == nullPtr
119 if bs == nullPtr
173 then return Nothing
120 then return Nothing
174 else Just <$> packCString bs
121 else Just <$> packCString bs
122+
jsaddle/default.nixnew file+17
@@ -0,0 +1,17 @@
1+{ mkDerivation, aeson, attoparsec, base, base64-bytestring
2+, bytestring, containers, deepseq, filepath, ghc-prim, http-types
3+, lens, primitive, process, ref-tf, scientific, stdenv, stm, text
4+, time, transformers, unordered-containers, vector
5+}:
6+mkDerivation {
7+ pname = "jsaddle";
8+ version = "0.8.0.1";
9+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
10+ libraryHaskellDepends = [
11+ aeson attoparsec base base64-bytestring bytestring containers
12+ deepseq filepath ghc-prim http-types lens primitive process ref-tf
13+ scientific stm text time transformers unordered-containers vector
14+ ];
15+ description = "Interface for JavaScript that works with GHCJS and GHC";
16+ license = stdenv.lib.licenses.mit;
17+}
jsaddle/jsaddle.cabal+15−20
@@ -1,5 +1,5 @@
1name: jsaddle
1name: jsaddle
2−version: 0.9.8.0
2+version: 0.8.3.0
3cabal-version: >=1.10
3cabal-version: >=1.10
4build-type: Simple
4build-type: Simple
5license: MIT
5license: MIT
@@ -10,7 +10,7 @@ description:
10 This package provides an EDSL for calling JavaScript that
10 This package provides an EDSL for calling JavaScript that
11 can be used both from GHCJS and GHC. When using GHC
11 can be used both from GHCJS and GHC. When using GHC
12 the application is run using Warp and WebSockets to
12 the application is run using Warp and WebSockets to
13− drive a small JavaScript helper.
13+ drive a small JavaScipt helper.
14category: Web, Javascript
14category: Web, Javascript
15author: Hamish Mackenzie
15author: Hamish Mackenzie
16
16
@@ -36,20 +36,18 @@ library
36 else
36 else
37 build-depends:
37 build-depends:
38 attoparsec >=0.11 && <0.14,
38 attoparsec >=0.11 && <0.14,
39− containers >=0.5.6.2 && <0.7,
39+ containers >=0.5.6.2 && <0.6,
40 deepseq >=1.3 && < 1.5,
40 deepseq >=1.3 && < 1.5,
41 filepath >=1.4.0.0 && <1.5,
41 filepath >=1.4.0.0 && <1.5,
42 ghc-prim,
42 ghc-prim,
43− http-types >=0.8.6 && <0.13,
43+ http-types >=0.8.6 && <0.10,
44− process >=1.2.3.0 && <1.7,
44+ process >=1.2.3.0 && <1.5,
45− random >= 1.1 && < 1.3,
46 ref-tf >=0.4.0.1 && <0.5,
45 ref-tf >=0.4.0.1 && <0.5,
47 scientific >=0.3 && <0.4,
46 scientific >=0.3 && <0.4,
48− stm >=2.4.4 && <2.6,
47+ stm >=2.4.4 && <2.5,
49− time >=1.5.0.1 && <1.11,
48+ time >=1.5.0.1 && <1.8,
50 unordered-containers >=0.2 && <0.3,
49 unordered-containers >=0.2 && <0.3,
51− vector >=0.10 && <0.13,
50+ vector >=0.10 && <0.12
52− unliftio-core >=0.1 && < 0.3
53 exposed-modules:
51 exposed-modules:
54 Data.JSString
52 Data.JSString
55 Data.JSString.Internal
53 Data.JSString.Internal
@@ -63,7 +61,6 @@ library
63 GHCJS.Prim
61 GHCJS.Prim
64 GHCJS.Prim.Internal
62 GHCJS.Prim.Internal
65 GHCJS.Types
63 GHCJS.Types
66− GHCJS.Concurrent
67 GHCJS.Foreign
64 GHCJS.Foreign
68 GHCJS.Foreign.Internal
65 GHCJS.Foreign.Internal
69 GHCJS.Internal.Types
66 GHCJS.Internal.Types
@@ -84,10 +81,8 @@ library
84 JavaScript.Array.Internal
81 JavaScript.Array.Internal
85 JavaScript.Object
82 JavaScript.Object
86 JavaScript.Object.Internal
83 JavaScript.Object.Internal
87− Language.Javascript.JSaddle.Debug
88 Language.Javascript.JSaddle.Native
84 Language.Javascript.JSaddle.Native
89 Language.Javascript.JSaddle.Native.Internal
85 Language.Javascript.JSaddle.Native.Internal
90− Language.Javascript.JSaddle.Null
91 hs-source-dirs: src-ghc
86 hs-source-dirs: src-ghc
92 exposed-modules:
87 exposed-modules:
93 Language.Javascript.JSaddle
88 Language.Javascript.JSaddle
@@ -107,14 +102,12 @@ library
107 Language.Javascript.JSaddle.Value
102 Language.Javascript.JSaddle.Value
108 Language.Javascript.JSaddle.Types
103 Language.Javascript.JSaddle.Types
109 build-depends:
104 build-depends:
110− aeson >=0.11.3.0 && <1.6,
105+ aeson >=0.8.0.2 && <1.2,
111− base >=4.9 && <5,
106+ base <5,
112− base-compat >=0.9.0 && <0.12,
113− base64-bytestring >=1.0.0.1 && <1.2,
107+ base64-bytestring >=1.0.0.1 && <1.1,
114 bytestring >=0.10.6.0 && <0.11,
108 bytestring >=0.10.6.0 && <0.11,
115− exceptions >=0.8 && <0.11,
116− lens >=3.8.5 && <5.1,
109+ lens >=3.8.5 && <4.16,
117− primitive >=0.6.1.0 && <0.8,
110+ primitive >=0.6.1.0 && <0.7,
118 text >=1.2.1.3 && <1.3,
111 text >=1.2.1.3 && <1.3,
119 transformers >=0.4.2.0 && <0.6
112 transformers >=0.4.2.0 && <0.6
120 default-language: Haskell2010
113 default-language: Haskell2010
@@ -124,3 +117,5 @@ library
124 cpp-options: -DCHECK_UNCHECKED
117 cpp-options: -DCHECK_UNCHECKED
125 if flag(call-stacks) || flag(check-unchecked)
118 if flag(call-stacks) || flag(check-unchecked)
126 cpp-options: -DJSADDLE_HAS_CALL_STACK
119 cpp-options: -DJSADDLE_HAS_CALL_STACK
120+
121+
jsaddle/src-ghc/Data/JSString/Internal/Type.hs+1−2
@@ -38,11 +38,10 @@ import Data.Aeson (ToJSON(..), FromJSON(..))
38import Data.Data (Data)
38import Data.Data (Data)
39-- import Data.Text.Internal.Unsafe.Char (ord)
39-- import Data.Text.Internal.Unsafe.Char (ord)
40import Data.Typeable (Typeable)
40import Data.Typeable (Typeable)
41−import Data.Semigroup (Semigroup)
42import GHC.Exts (Char(..), ord#, andI#, (/=#), isTrue#)
41import GHC.Exts (Char(..), ord#, andI#, (/=#), isTrue#)
43
42
44-- | A wrapper around a JavaScript string
43-- | A wrapper around a JavaScript string
45−newtype JSString = JSString Text deriving(Show, Read, IsString, Semigroup, Monoid, Ord, Eq, Data, ToJSON, FromJSON, Typeable)
44+newtype JSString = JSString Text deriving(Show, Read, IsString, Monoid, Ord, Eq, Data, ToJSON, FromJSON, Typeable)
46
45
47instance NFData JSString where rnf !_ = ()
46instance NFData JSString where rnf !_ = ()
48
47
jsaddle/src-ghc/GHCJS/Buffer.hs+2−2
@@ -52,7 +52,7 @@ getInt32Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "i3"
52{-# INLINE getInt32Array #-}
52{-# INLINE getInt32Array #-}
53
53
54getUint8Array :: SomeBuffer any -> GHCJSPure (I.SomeUint8Array any)
54getUint8Array :: SomeBuffer any -> GHCJSPure (I.SomeUint8Array any)
55−getUint8Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "u8"
55+getUint8Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "u3"
56{-# INLINE getUint8Array #-}
56{-# INLINE getUint8Array #-}
57
57
58getUint16Array :: SomeBuffer any -> GHCJSPure (I.SomeUint16Array any)
58getUint16Array :: SomeBuffer any -> GHCJSPure (I.SomeUint16Array any)
@@ -85,7 +85,7 @@ clone = js_clone
85
85
86fromByteString :: ByteString -> GHCJSPure (Buffer, Int, Int)
86fromByteString :: ByteString -> GHCJSPure (Buffer, Int, Int)
87fromByteString bs = GHCJSPure $ do
87fromByteString bs = GHCJSPure $ do
88− buffer <- SomeBuffer <$> jsg1 "h$newByteArrayFromBase64String" (decodeUtf8 $ B64.encode bs)
88+ buffer <- SomeBuffer <$> jsg1 "h$newByteArrayBase64String" (decodeUtf8 $ B64.encode bs)
89 return (buffer, 0, BS.length bs)
89 return (buffer, 0, BS.length bs)
90{-# INLINE fromByteString #-}
90{-# INLINE fromByteString #-}
91
91
jsaddle/src-ghc/GHCJS/Concurrent.hsdeleted−22
@@ -1,22 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−
3−module GHCJS.Concurrent ( OnBlocked(..)
4− , WouldBlockException(..)
5− ) where
6−
7−import GHCJS.Prim
8−
9−import Data.Data
10−
11−{- |
12− The runtime tries to run synchronous threads to completion. Sometimes it's
13− not possible to continue running a thread, for example when the thread
14− tries to take an empty 'MVar'. The runtime can then either throw a
15− 'WouldBlockException', aborting the blocking action, or continue the
16− thread asynchronously.
17− -}
18−
19−data OnBlocked = ContinueAsync -- ^ continue the thread asynchronously if blocked
20− | ThrowWouldBlock -- ^ throw 'WouldBlockException' if blocked
21− deriving (Data, Typeable, Enum, Show, Eq, Ord)
22−
jsaddle/src-ghc/GHCJS/Foreign/Internal.hs+10−12
@@ -27,28 +27,26 @@ import Language.Javascript.JSaddle.Native.Internal
27 (valueToBool)
27 (valueToBool)
28import Data.Typeable (Typeable)
28import Data.Typeable (Typeable)
29import GHCJS.Prim (isNull, isUndefined)
29import GHCJS.Prim (isNull, isUndefined)
30−import System.IO.Unsafe (unsafePerformIO)
31−import Data.IORef (newIORef)
32
30
33jsTrue :: JSVal
31jsTrue :: JSVal
34−jsTrue = JSVal . unsafePerformIO $ newIORef 3
32+jsTrue = JSVal 3
35−{-# NOINLINE jsTrue #-}
33+{-# INLINE jsTrue #-}
36
34
37jsFalse :: JSVal
35jsFalse :: JSVal
38−jsFalse = JSVal . unsafePerformIO $ newIORef 2
36+jsFalse = JSVal 2
39−{-# NOINLINE jsFalse #-}
37+{-# INLINE jsFalse #-}
40
38
41jsNull :: JSVal
39jsNull :: JSVal
42−jsNull = JSVal . unsafePerformIO $ newIORef 0
40+jsNull = JSVal 0
43−{-# NOINLINE jsNull #-}
41+{-# INLINE jsNull #-}
44
42
45toJSBool :: Bool -> JSVal
43toJSBool :: Bool -> JSVal
46−toJSBool b = JSVal . unsafePerformIO . newIORef $ if b then 3 else 2
44+toJSBool b = JSVal $ if b then 3 else 2
47−{-# NOINLINE toJSBool #-}
45+{-# INLINE toJSBool #-}
48
46
49jsUndefined :: JSVal
47jsUndefined :: JSVal
50−jsUndefined = JSVal . unsafePerformIO $ newIORef 1
48+jsUndefined = JSVal 1
51−{-# NOINLINE jsUndefined #-}
49+{-# INLINE jsUndefined #-}
52
50
53isTruthy :: JSVal -> GHCJSPure Bool
51isTruthy :: JSVal -> GHCJSPure Bool
54isTruthy = GHCJSPure . valueToBool
52isTruthy = GHCJSPure . valueToBool
jsaddle/src-ghc/GHCJS/Prim/Internal.hs+3−5
@@ -17,8 +17,6 @@ import Unsafe.Coerce (unsafeCoerce)
17import Data.Aeson (ToJSON(..), FromJSON(..))
17import Data.Aeson (ToJSON(..), FromJSON(..))
18
18
19import qualified GHC.Exception as Ex
19import qualified GHC.Exception as Ex
20−import Data.IORef (newIORef, IORef)
21−import System.IO.Unsafe (unsafePerformIO)
22
20
23-- A reference to a particular JavaScript value inside the JavaScript context
21-- A reference to a particular JavaScript value inside the JavaScript context
24type JSValueRef = Int64
22type JSValueRef = Int64
@@ -27,7 +25,7 @@ type JSValueRef = Int64
27 JSVal is a boxed type that can be used as FFI
25 JSVal is a boxed type that can be used as FFI
28 argument or result.
26 argument or result.
29-}
27-}
30−newtype JSVal = JSVal (IORef JSValueRef)
28+newtype JSVal = JSVal JSValueRef deriving(Show, ToJSON, FromJSON)
31
29
32instance NFData JSVal where
30instance NFData JSVal where
33 rnf x = x `seq` ()
31 rnf x = x `seq` ()
@@ -51,8 +49,8 @@ mkJSException ref =
51 return (JSException (unsafeCoerce ref) "")
49 return (JSException (unsafeCoerce ref) "")
52
50
53jsNull :: JSVal
51jsNull :: JSVal
54−jsNull = JSVal . unsafePerformIO $ newIORef 0
52+jsNull = JSVal 0
55−{-# NOINLINE jsNull #-}
53+{-# INLINE jsNull #-}
56
54
57{- | If a synchronous thread tries to do something that can only
55{- | If a synchronous thread tries to do something that can only
58 be done asynchronously, and the thread is set up to not
56 be done asynchronously, and the thread is set up to not
jsaddle/src-ghc/GHCJS/Types.hs+3−5
@@ -27,21 +27,19 @@ import GHC.Int
27import GHC.Types
27import GHC.Types
28import GHC.Prim
28import GHC.Prim
29import GHC.Ptr
29import GHC.Ptr
30−import GHC.IORef
31−import GHC.IO.Unsafe (unsafePerformIO)
32
30
33import Control.DeepSeq
31import Control.DeepSeq
34import Unsafe.Coerce
32import Unsafe.Coerce
35
33
36−type Ref# = IORef Int64
34+type Ref# = Int64
37
35
38mkRef :: Ref# -> JSVal
36mkRef :: Ref# -> JSVal
39mkRef = JSVal
37mkRef = JSVal
40{-# INLINE mkRef #-}
38{-# INLINE mkRef #-}
41
39
42nullRef :: JSVal
40nullRef :: JSVal
43−nullRef = JSVal . unsafePerformIO $ newIORef 0
41+nullRef = JSVal 0
44−{-# NOINLINE nullRef #-}
42+{-# INLINE nullRef #-}
45
43
46--toPtr :: JSVal -> Ptr a
44--toPtr :: JSVal -> Ptr a
47--toPtr (JSVal x) = unsafeCoerce (Ptr' x 0#)
45--toPtr (JSVal x) = unsafeCoerce (Ptr' x 0#)
jsaddle/src-ghc/JavaScript/Array.hs+2−2
@@ -48,7 +48,7 @@ import Language.Javascript.JSaddle.Types
48 (JSM, JSVal, SomeJSArray(..), JSArray,
48 (JSM, JSVal, SomeJSArray(..), JSArray,
49 MutableJSArray, GHCJSPure(..))
49 MutableJSArray, GHCJSPure(..))
50import Control.Lens.Operators ((^.))
50import Control.Lens.Operators ((^.))
51−import Language.Javascript.JSaddle.Object (js2, js0, (<##), js1, js)
51+import Language.Javascript.JSaddle.Object (js2, js0, (##), js1, js)
52import Language.Javascript.JSaddle.Value (valToNumber)
52import Language.Javascript.JSaddle.Value (valToNumber)
53import JavaScript.Array.Internal (create, fromList, fromListIO, toList, toListIO, index, read, push)
53import JavaScript.Array.Internal (create, fromList, fromListIO, toList, toListIO, index, read, push)
54
54
@@ -69,7 +69,7 @@ append (SomeJSArray x) (SomeJSArray y) = SomeJSArray <$> x ^. js1 "concat" y
69{-# INLINE append #-}
69{-# INLINE append #-}
70
70
71write :: Int -> JSVal -> MutableJSArray -> JSM ()
71write :: Int -> JSVal -> MutableJSArray -> JSM ()
72−write n e (SomeJSArray x) = void $ (x <## n) e
72+write n e (SomeJSArray x) = void $ (x ## n) e
73{-# INLINE write #-}
73{-# INLINE write #-}
74
74
75pop :: MutableJSArray -> JSM JSVal
75pop :: MutableJSArray -> JSM JSVal
jsaddle/src-ghc/JavaScript/Array/Internal.hs+2
@@ -21,6 +21,8 @@ import Data.JSString.Internal.Type (JSString(..))
21import Language.Javascript.JSaddle.Types (JSM, SomeJSArray(..), JSArray, MutableJSArray, STJSArray, Object(..), GHCJSPure(..))
21import Language.Javascript.JSaddle.Types (JSM, SomeJSArray(..), JSArray, MutableJSArray, STJSArray, Object(..), GHCJSPure(..))
22import Language.Javascript.JSaddle.Native.Internal
22import Language.Javascript.JSaddle.Native.Internal
23 (newArray, getPropertyByName, getPropertyAtIndex, callAsFunction, valueToNumber)
23 (newArray, getPropertyByName, getPropertyAtIndex, callAsFunction, valueToNumber)
24+import Language.Javascript.JSaddle.Run
25+ (Command(..), Result(..), AsyncCommand(..), sendCommand, sendLazyCommand)
24
26
25create :: JSM MutableJSArray
27create :: JSM MutableJSArray
26create = SomeJSArray <$> newArray []
28create = SomeJSArray <$> newArray []
jsaddle/src/Language/Javascript/JSaddle.hs+5−5
@@ -9,7 +9,7 @@
9-- | This package provides an EDSL for calling JavaScript that
9-- | This package provides an EDSL for calling JavaScript that
10-- can be used both from GHCJS and GHC. When using GHC
10-- can be used both from GHCJS and GHC. When using GHC
11-- the application is run using Warp and WebSockets to
11-- the application is run using Warp and WebSockets to
12−-- drive a small JavaScript helper.
12+-- drive a small JavaScipt helper.
13-----------------------------------------------------------------------------
13-----------------------------------------------------------------------------
14
14
15module Language.Javascript.JSaddle (
15module Language.Javascript.JSaddle (
@@ -35,10 +35,10 @@ module Language.Javascript.JSaddle (
35 --
35 --
36 -- Where it makes sense code examples are given in two forms. One
36 -- Where it makes sense code examples are given in two forms. One
37 -- that uses 'eval' to run a purely JavaScript version and one that
37 -- that uses 'eval' to run a purely JavaScript version and one that
38− -- uses more of the JSaddle EDSL feature being demonstrated.
38+ -- uses more of the JSaddle EDSL feature being demonstated.
39
39
40 -- * Calling Haskell from JavaScript
40 -- * Calling Haskell from JavaScript
41− -- | You can call back into Haskell from JavaScript using 'fun' to
41+ -- | You can call back into haskell from JavaScript using 'fun' to
42 -- convert a Haskell function in the JSM monad into a javascript
42 -- convert a Haskell function in the JSM monad into a javascript
43 -- value.
43 -- value.
44
44
@@ -52,8 +52,8 @@ module Language.Javascript.JSaddle (
52 -- and the server will run the Haskell code. The JSaddle parts will
52 -- and the server will run the Haskell code. The JSaddle parts will
53 -- be executed by sending commands back to the browser.
53 -- be executed by sending commands back to the browser.
54
54
55− -- Although the JavaScript code is executed in the strict order
55+ -- Although the code JavaScript code is executed in the the strict order
56− -- set out by the EDSL it is done asynchronously to the Haskell code.
56+ -- set out by the EDSL it done asynchonously to the Haskell code.
57 -- This improves the performance by reducing the number of round trips
57 -- This improves the performance by reducing the number of round trips
58 -- needed between the Haskell and JavaScript code.
58 -- needed between the Haskell and JavaScript code.
59
59
jsaddle/src/Language/Javascript/JSaddle/Debug.hsdeleted−48
@@ -1,48 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−-----------------------------------------------------------------------------
4−--
5−-- Module : Language.Javascript.JSaddle.WebSockets
6−-- Copyright : (c) Hamish Mackenzie
7−-- License : MIT
8−--
9−-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
10−--
11−-- |
12−--
13−-----------------------------------------------------------------------------
14−
15−module Language.Javascript.JSaddle.Debug (
16− contexts
17− , addContext
18− , removeContext
19− , runOnAll
20− , runOnAll_
21−) where
22−
23−import Language.Javascript.JSaddle
24− (runJSM, askJSM, JSM, JSContextRef(..))
25−import Data.IORef (readIORef, atomicModifyIORef', newIORef, IORef)
26−import System.IO.Unsafe (unsafePerformIO)
27−import Data.Monoid ((<>))
28−import Control.Monad.IO.Class (MonadIO(..))
29−import Data.Int (Int64)
30−
31−contexts :: IORef [JSContextRef]
32−contexts = unsafePerformIO $ newIORef []
33−{-# NOINLINE contexts #-}
34−
35−addContext :: JSM ()
36−addContext = do
37− ctx <- askJSM
38− liftIO $ atomicModifyIORef' contexts $ \c -> (c <> [ctx], ())
39−
40−removeContext :: MonadIO m => Int64 -> m ()
41−removeContext cid =
42− liftIO $ atomicModifyIORef' contexts $ \c -> (filter ((/= cid) . contextId) c, ())
43−
44−runOnAll :: MonadIO m => JSM a -> m [a]
45−runOnAll f = liftIO (readIORef contexts) >>= mapM (runJSM f)
46−
47−runOnAll_ :: MonadIO m => JSM a -> m ()
48−runOnAll_ f = liftIO (readIORef contexts) >>= mapM_ (runJSM f)
jsaddle/src/Language/Javascript/JSaddle/Evaluate.hs+1−1
@@ -50,7 +50,7 @@ eval :: (ToJSString script)
50 -> JSM JSVal
50 -> JSM JSVal
51#ifdef ghcjs_HOST_OS
51#ifdef ghcjs_HOST_OS
52eval script = liftIO $ js_eval (toJSString script)
52eval script = liftIO $ js_eval (toJSString script)
53−foreign import javascript safe "$r = eval($1);"
53+foreign import javascript unsafe "$r = eval($1);"
54 js_eval :: JSString -> IO JSVal
54 js_eval :: JSString -> IO JSVal
55#else
55#else
56eval = evaluateScript . toJSString
56eval = evaluateScript . toJSString
jsaddle/src/Language/Javascript/JSaddle/Monad.hs+26−3
@@ -38,12 +38,13 @@ module Language.Javascript.JSaddle.Monad (
38 , bracket
38 , bracket
39) where
39) where
40
40
41+import Prelude hiding (read)
41#ifndef ghcjs_HOST_OS
42#ifndef ghcjs_HOST_OS
42−import Control.Monad.Trans.Reader (runReaderT, ask)
43+import Control.Monad.Trans.Reader (runReaderT, ask, ReaderT(..))
43#endif
44#endif
44import Control.Monad.IO.Class (MonadIO(..))
45import Control.Monad.IO.Class (MonadIO(..))
45−import Control.Monad.Catch (catch, bracket)
46+import qualified Control.Exception as E (Exception, catch, bracket)
46−import Language.Javascript.JSaddle.Types (JSM(..), MonadJSM, liftJSM, JSContextRef)
47+import Language.Javascript.JSaddle.Types (JSM(..), MonadJSM, liftJSM, JSContextRef(..))
47import Language.Javascript.JSaddle.Run (syncPoint, syncAfter, waitForAnimationFrame, nextAnimationFrame)
48import Language.Javascript.JSaddle.Run (syncPoint, syncAfter, waitForAnimationFrame, nextAnimationFrame)
48
49
49-- | Gets the JavaScript context from the monad
50-- | Gets the JavaScript context from the monad
@@ -65,3 +66,25 @@ runJSM f = liftIO . runReaderT (unJSM f)
65-- | Alternative version of 'runJSM'
66-- | Alternative version of 'runJSM'
66runJSaddle :: MonadIO m => JSContextRef -> JSM a -> m a
67runJSaddle :: MonadIO m => JSContextRef -> JSM a -> m a
67runJSaddle = flip runJSM
68runJSaddle = flip runJSM
69+
70+
71+-- | Wrapped version of 'E.catch' that runs in a MonadIO that works
72+-- a bit better with 'JSM'
73+catch :: E.Exception e
74+ => JSM b
75+ -> (e -> JSM b)
76+ -> JSM b
77+t `catch` c = do
78+ r <- askJSM
79+ liftIO (runJSM (syncAfter t) r `E.catch` \e -> runJSM (c e) r)
80+
81+-- | Wrapped version of 'E.bracket' that runs in a MonadIO that works
82+-- a bit better with 'JSM'
83+bracket :: JSM a -> (a -> JSM b) -> (a -> JSM c) -> JSM c
84+bracket aquire release f = do
85+ r <- askJSM
86+ liftIO $ E.bracket
87+ (runJSM (syncAfter aquire) r)
88+ (\x -> runJSM (syncAfter $ release x) r)
89+ (\x -> runJSM (syncAfter $ f x) r)
90+
jsaddle/src/Language/Javascript/JSaddle/Native/Internal.hs+31−55
@@ -1,6 +1,3 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE MagicHash #-}
3−{-# LANGUAGE UnboxedTuples #-}
4-----------------------------------------------------------------------------
1-----------------------------------------------------------------------------
5--
2--
6-- Module : Language.Javascript.JSaddle.Native
3-- Module : Language.Javascript.JSaddle.Native
@@ -30,8 +27,7 @@ module Language.Javascript.JSaddle.Native.Internal (
30 , callAsFunction
27 , callAsFunction
31 , callAsConstructor
28 , callAsConstructor
32 , newEmptyObject
29 , newEmptyObject
33− , newAsyncCallback
30+ , newCallback
34− , newSyncCallback
35 , newArray
31 , newArray
36 , evaluateScript
32 , evaluateScript
37 , deRefVal
33 , deRefVal
@@ -48,6 +44,7 @@ module Language.Javascript.JSaddle.Native.Internal (
48) where
44) where
49
45
50import Control.Monad.IO.Class (MonadIO(..))
46import Control.Monad.IO.Class (MonadIO(..))
47+import Control.Monad.Primitive (touch)
51
48
52import Data.Aeson (Value)
49import Data.Aeson (Value)
53
50
@@ -57,36 +54,32 @@ import Language.Javascript.JSaddle.Types
57 JSStringReceived(..), JSStringForSend(..), JSObjectForSend(..))
54 JSStringReceived(..), JSStringForSend(..), JSObjectForSend(..))
58import Language.Javascript.JSaddle.Monad (askJSM)
55import Language.Javascript.JSaddle.Monad (askJSM)
59import Language.Javascript.JSaddle.Run
56import Language.Javascript.JSaddle.Run
60− (Command(..), Result(..), sendCommand,
57+ (Command(..), AsyncCommand(..), Result(..), sendCommand,
61 sendAsyncCommand, sendLazyCommand, wrapJSVal)
58 sendAsyncCommand, sendLazyCommand, wrapJSVal)
62−import GHC.IORef (IORef(..), readIORef)
63−import GHC.STRef (STRef(..))
64−import GHC.IO (IO(..))
65−import GHC.Base (touch#)
66
59
67wrapJSString :: MonadIO m => JSStringReceived -> m JSString
60wrapJSString :: MonadIO m => JSStringReceived -> m JSString
68wrapJSString (JSStringReceived ref) = return $ JSString ref
61wrapJSString (JSStringReceived ref) = return $ JSString ref
69
62
70−touchIORef :: IORef a -> IO ()
71−touchIORef (IORef (STRef r#)) = IO $ \s -> case touch# r# s of s' -> (# s', () #)
72−
73withJSVal :: MonadIO m => JSVal -> (JSValueForSend -> m a) -> m a
63withJSVal :: MonadIO m => JSVal -> (JSValueForSend -> m a) -> m a
74−withJSVal (JSVal ref) f = do
64+withJSVal v@(JSVal ref) f =
75− result <- (f . JSValueForSend) =<< liftIO (readIORef ref)
65+ do result <- f (JSValueForSend ref)
76− liftIO $ touchIORef ref
66+ liftIO $ touch v
77 return result
67 return result
78
68
79withJSVals :: MonadIO m => [JSVal] -> ([JSValueForSend] -> m a) -> m a
69withJSVals :: MonadIO m => [JSVal] -> ([JSValueForSend] -> m a) -> m a
80withJSVals v f =
70withJSVals v f =
81− do result <- f =<< mapM (\(JSVal ref) -> liftIO $ JSValueForSend <$> readIORef ref) v
71+ do result <- f (map (\(JSVal ref) -> JSValueForSend ref) v)
82− liftIO $ mapM_ (\(JSVal ref) -> touchIORef ref) v
72+ liftIO $ mapM_ touch v
83 return result
73 return result
84
74
85withObject :: MonadIO m => Object -> (JSObjectForSend -> m a) -> m a
75withObject :: MonadIO m => Object -> (JSObjectForSend -> m a) -> m a
86withObject (Object o) f = withJSVal o (f . JSObjectForSend)
76withObject (Object o) f = withJSVal o (f . JSObjectForSend)
87
77
88withJSString :: MonadIO m => JSString -> (JSStringForSend -> m a) -> m a
78withJSString :: MonadIO m => JSString -> (JSStringForSend -> m a) -> m a
89−withJSString (JSString ref) f = f (JSStringForSend ref)
79+withJSString v@(JSString ref) f =
80+ do result <- f (JSStringForSend ref)
81+ liftIO $ touch v
82+ return result
90
83
91setPropertyByName :: JSString -> JSVal -> Object -> JSM ()
84setPropertyByName :: JSString -> JSVal -> Object -> JSM ()
92setPropertyByName name val this =
85setPropertyByName name val this =
@@ -143,21 +136,13 @@ newEmptyObject :: JSM Object
143newEmptyObject = Object <$> sendLazyCommand NewEmptyObject
136newEmptyObject = Object <$> sendLazyCommand NewEmptyObject
144{-# INLINE newEmptyObject #-}
137{-# INLINE newEmptyObject #-}
145
138
146−newAsyncCallback :: JSCallAsFunction -> JSM Object
147−newAsyncCallback f = do
148− object <- Object <$> sendLazyCommand NewAsyncCallback
149− add <- addCallback <$> askJSM
150− liftIO $ add object f
151− return object
152−{-# INLINE newAsyncCallback #-}
153−
154−newSyncCallback :: JSCallAsFunction -> JSM Object
139+newCallback :: JSCallAsFunction -> JSM Object
155−newSyncCallback f = do
140+newCallback f = do
156− object <- Object <$> sendLazyCommand NewSyncCallback
141+ object <- Object <$> sendLazyCommand NewCallback
157 add <- addCallback <$> askJSM
142 add <- addCallback <$> askJSM
158 liftIO $ add object f
143 liftIO $ add object f
159 return object
144 return object
160−{-# INLINE newSyncCallback #-}
145+{-# INLINE newCallback #-}
161
146
162newArray :: [JSVal] -> JSM JSVal
147newArray :: [JSVal] -> JSM JSVal
163newArray xs = withJSVals xs $ \xs' -> sendLazyCommand (NewArray xs')
148newArray xs = withJSVals xs $ \xs' -> sendLazyCommand (NewArray xs')
@@ -172,14 +157,13 @@ deRefVal value = withJSVal value $ sendCommand . DeRefVal
172{-# INLINE deRefVal #-}
157{-# INLINE deRefVal #-}
173
158
174valueToBool :: JSVal -> JSM Bool
159valueToBool :: JSVal -> JSM Bool
175−valueToBool v@(JSVal ref) = liftIO (readIORef ref) >>= \case
176− 0 -> return False -- null
160+valueToBool (JSVal 0) = return False -- null
177− 1 -> return False -- undefined
161+valueToBool (JSVal 1) = return False -- undefined
178− 2 -> return False -- false
162+valueToBool (JSVal 2) = return False -- false
179− 3 -> return True -- true
163+valueToBool (JSVal 3) = return True -- true
180− _ -> withJSVal v $ \rval -> do
164+valueToBool v = withJSVal v $ \rval -> do
181− ~(ValueToBoolResult result) <- sendCommand (ValueToBool rval)
165+ ~(ValueToBoolResult result) <- sendCommand (ValueToBool rval)
182− return result
166+ return result
183{-# INLINE valueToBool #-}
167{-# INLINE valueToBool #-}
184
168
185valueToNumber :: JSVal -> JSM Double
169valueToNumber :: JSVal -> JSM Double
@@ -208,25 +192,17 @@ valueToJSONValue value = withJSVal value $ \rval -> do
208{-# INLINE valueToJSONValue #-}
192{-# INLINE valueToJSONValue #-}
209
193
210isNull :: JSVal -> JSM Bool
194isNull :: JSVal -> JSM Bool
211−isNull v@(JSVal ref) = liftIO (readIORef ref) >>= \case
212− 0 -> return True -- null
195+isNull (JSVal 0) = return True
213− 1 -> return False -- undefined
214− 2 -> return False -- false
215− 3 -> return False -- true
216− _ -> withJSVal v $ \rval -> do
196+isNull v = withJSVal v $ \rval -> do
217− ~(IsNullResult result) <- sendCommand $ IsNull rval
197+ ~(IsNullResult result) <- sendCommand $ IsNull rval
218− return result
198+ return result
219{-# INLINE isNull #-}
199{-# INLINE isNull #-}
220
200
221isUndefined :: JSVal -> JSM Bool
201isUndefined :: JSVal -> JSM Bool
222−isUndefined v@(JSVal ref) = liftIO (readIORef ref) >>= \case
202+isUndefined (JSVal 1) = return True
223− 0 -> return False -- null
224− 1 -> return True -- undefined
225− 2 -> return False -- false
226− 3 -> return False -- true
227− _ -> withJSVal v $ \rval -> do
203+isUndefined v = withJSVal v $ \rval -> do
228− ~(IsUndefinedResult result) <- sendCommand $ IsUndefined rval
204+ ~(IsUndefinedResult result) <- sendCommand $ IsUndefined rval
229− return result
205+ return result
230{-# INLINE isUndefined #-}
206{-# INLINE isUndefined #-}
231
207
232strictEqual :: JSVal -> JSVal -> JSM Bool
208strictEqual :: JSVal -> JSVal -> JSM Bool
jsaddle/src/Language/Javascript/JSaddle/Null.hsdeleted−56
@@ -1,56 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−-----------------------------------------------------------------------------
4−--
5−-- Module : Language.Javascript.JSaddle.WebSockets
6−-- Copyright : (c) Hamish Mackenzie
7−-- License : MIT
8−--
9−-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
10−--
11−-- |
12−--
13−-----------------------------------------------------------------------------
14−
15−module Language.Javascript.JSaddle.Null (
16− run
17−) where
18−
19−import Language.Javascript.JSaddle.Types
20− (BatchResults(..), JSM, JSStringReceived(..), Batch(..),
21− Results(..), Result(..), Command(..))
22−import Control.Concurrent.Chan (readChan, writeChan, newChan)
23−import Language.Javascript.JSaddle.Run (runJavaScript)
24−import Control.Concurrent (forkIO)
25−import Control.Monad (forever)
26−import Data.Aeson (Value(..))
27−import Data.Maybe (mapMaybe)
28−
29−-- | This is for performance testing JSaddle code that does not need to
30−-- to read anything back from the JavaScript context.
31−-- Anthing that does try to read will get JS null, 0 or "" back (depending
32−-- on how the value is read).
33−run :: JSM () -> IO ()
34−run f = do
35− batches <- newChan
36− (processResult, _processSyncResult, start) <- runJavaScript (writeChan batches) f
37− _ <- forkIO $ forever $
38− readChan batches >>= \case
39− Batch commands _ batchNumber ->
40− processResult $ BatchResults batchNumber . Success [] $ mapMaybe (\case
41− Left _ -> Nothing
42− Right command -> Just $
43− case command of
44− DeRefVal _ -> DeRefValResult 0 ""
45− ValueToBool _ -> ValueToBoolResult False
46− ValueToNumber _ -> ValueToNumberResult 0
47− ValueToString _ -> ValueToStringResult (JSStringReceived "")
48− ValueToJSON _ -> ValueToJSONResult (JSStringReceived "null")
49− ValueToJSONValue _ -> ValueToJSONValueResult Null
50− IsNull _ -> IsNullResult True
51− IsUndefined _ -> IsUndefinedResult False
52− StrictEqual _ _ -> StrictEqualResult False
53− InstanceOf _ _ -> InstanceOfResult False
54− PropertyNames _ -> PropertyNamesResult []
55− Sync -> SyncResult) commands
56− start
jsaddle/src/Language/Javascript/JSaddle/Object.hs+19−51
@@ -66,7 +66,6 @@ module Language.Javascript.JSaddle.Object (
66 -- * Calling Haskell From JavaScript
66 -- * Calling Haskell From JavaScript
67 , Function(..)
67 , Function(..)
68 , function
68 , function
69− , asyncFunction
70 , freeFunction
69 , freeFunction
71 , fun
70 , fun
72 , JSCallAsFunction
71 , JSCallAsFunction
@@ -103,7 +102,7 @@ import Data.Coerce (coerce)
103#ifdef ghcjs_HOST_OS
102#ifdef ghcjs_HOST_OS
104import GHCJS.Types (nullRef)
103import GHCJS.Types (nullRef)
105import GHCJS.Foreign.Callback
104import GHCJS.Foreign.Callback
106− (releaseCallback, syncCallback2, asyncCallback2, OnBlocked(..), Callback)
105+ (releaseCallback, syncCallback2, OnBlocked(..), Callback)
107import GHCJS.Marshal (ToJSVal(..))
106import GHCJS.Marshal (ToJSVal(..))
108import JavaScript.Array (MutableJSArray)
107import JavaScript.Array (MutableJSArray)
109import qualified JavaScript.Array as Array (toListIO, fromListIO)
108import qualified JavaScript.Array as Array (toListIO, fromListIO)
@@ -116,13 +115,12 @@ import Language.Javascript.JSaddle.Types
116#else
115#else
117import GHCJS.Marshal.Internal (ToJSVal(..))
116import GHCJS.Marshal.Internal (ToJSVal(..))
118import Language.Javascript.JSaddle.Native
117import Language.Javascript.JSaddle.Native
119− (newAsyncCallback, newSyncCallback, callAsFunction, callAsConstructor)
118+ (newCallback, callAsFunction, callAsConstructor)
120import Language.Javascript.JSaddle.Monad (askJSM, JSM)
119import Language.Javascript.JSaddle.Monad (askJSM, JSM)
121import Language.Javascript.JSaddle.Types
120import Language.Javascript.JSaddle.Types
122− (JSValueForSend(..), AsyncCommand(..), JSString, Object(..),
121+ (JSString, Object(..), SomeJSArray(..),
123− SomeJSArray(..), JSVal(..), JSCallAsFunction, JSContextRef(..))
122+ JSVal(..), JSCallAsFunction, JSContextRef(..))
124import JavaScript.Object.Internal (create, listProps)
123import JavaScript.Object.Internal (create, listProps)
125−import Language.Javascript.JSaddle.Run (sendAsyncCommand)
126#endif
124#endif
127import JavaScript.Array.Internal (fromListIO)
125import JavaScript.Array.Internal (fromListIO)
128import Language.Javascript.JSaddle.Value (valToObject)
126import Language.Javascript.JSaddle.Value (valToObject)
@@ -132,8 +130,6 @@ import Language.Javascript.JSaddle.Arguments (MakeArgs(..))
132import Control.Monad.IO.Class (MonadIO(..))
130import Control.Monad.IO.Class (MonadIO(..))
133import Language.Javascript.JSaddle.Properties
131import Language.Javascript.JSaddle.Properties
134import Control.Lens (IndexPreservingGetter, to)
132import Control.Lens (IndexPreservingGetter, to)
135−import Data.IORef (newIORef, readIORef)
136−import System.IO.Unsafe (unsafePerformIO)
137
133
138-- $setup
134-- $setup
139-- >>> import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
135-- >>> import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
@@ -439,16 +435,9 @@ newtype Function = Function {functionObject :: Object}
439#endif
435#endif
440
436
441
437
442−#ifdef ghcjs_HOST_OS
443−foreign import javascript unsafe "$r = function () { $1(this, arguments); }"
444− makeFunctionWithCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO Object
445−#endif
446−
447-- ^ Make a JavaScript function object that wraps a Haskell function.
438-- ^ Make a JavaScript function object that wraps a Haskell function.
448−-- Calls made to the function will be synchronous where possible
449−-- (on GHCJS it uses on `syncCallback2` with `ContinueAsync`).
450function :: JSCallAsFunction -- ^ Haskell function to call
439function :: JSCallAsFunction -- ^ Haskell function to call
451− -> JSM Function -- ^ Returns a JavaScript function object that will
440+ -> JSM Function -- ^ Returns a JavaScript function object that will
452 -- call the Haskell one when it is called
441 -- call the Haskell one when it is called
453#ifdef ghcjs_HOST_OS
442#ifdef ghcjs_HOST_OS
454function f = do
443function f = do
@@ -456,26 +445,11 @@ function f = do
456 rargs <- Array.toListIO (coerce args)
445 rargs <- Array.toListIO (coerce args)
457 f this this rargs -- TODO pass function object through
446 f this this rargs -- TODO pass function object through
458 Function callback <$> makeFunctionWithCallback callback
447 Function callback <$> makeFunctionWithCallback callback
448+foreign import javascript unsafe "$r = function () { $1(this, arguments); }"
449+ makeFunctionWithCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO Object
459#else
450#else
460function f = do
451function f = do
461− object <- newSyncCallback f
452+ object <- newCallback f
462− return $ Function object
463−#endif
464−
465−-- ^ Make a JavaScript function object that wraps a Haskell function.
466−-- Calls made to the function will be Asynchronous.
467−asyncFunction :: JSCallAsFunction -- ^ Haskell function to call
468− -> JSM Function -- ^ Returns a JavaScript function object that will
469− -- call the Haskell one when it is called
470−#ifdef ghcjs_HOST_OS
471−asyncFunction f = do
472− callback <- asyncCallback2 $ \this args -> do
473− rargs <- Array.toListIO (coerce args)
474− f this this rargs -- TODO pass function object through
475− Function callback <$> makeFunctionWithCallback callback
476−#else
477−asyncFunction f = do
478− object <- newAsyncCallback f
479 return $ Function object
453 return $ Function object
480#endif
454#endif
481
455
@@ -484,16 +458,9 @@ freeFunction :: Function -> JSM ()
484freeFunction (Function callback _) = liftIO $
458freeFunction (Function callback _) = liftIO $
485 releaseCallback callback
459 releaseCallback callback
486#else
460#else
487−freeFunction (Function (Object (JSVal objectRef))) = do
461+freeFunction (Function object) = do
488− -- By now the callback should ideally have been removed from whatever
462+ free <- freeCallback <$> askJSM
489− -- events it was added to.
463+ liftIO $ free object
490− -- In case a call to the callback is still pending (perhaps just being sent
491− -- on the JS side) we use FreeCallback to queue the callback to be freed when
492− -- the next batch of results comes back fro JS.
493− -- We are not using withJSVal to keep JS value "alive" because FreeCallback
494− -- does not use the it.
495− n <- liftIO $ readIORef objectRef
496− sendAsyncCommand (FreeCallback (JSValueForSend n))
497#endif
464#endif
498
465
499instance ToJSVal Function where
466instance ToJSVal Function where
@@ -526,12 +493,13 @@ array args = do
526 Object . coerce <$> fromListIO rargs
493 Object . coerce <$> fromListIO rargs
527
494
528-- | JavaScript's global object
495-- | JavaScript's global object
496+global :: Object
529#ifdef ghcjs_HOST_OS
497#ifdef ghcjs_HOST_OS
498+global = js_window
530−foreign import javascript unsafe "$r = globalThis"
499+foreign import javascript unsafe "$r = window"
531− global :: Object
500+ js_window :: Object
532#else
501#else
533−global :: Object
502+global = Object (JSVal 4)
534−global = Object . JSVal . unsafePerformIO $ newIORef 4
535#endif
503#endif
536
504
537-- | Get a list containing the property names present on a given object
505-- | Get a list containing the property names present on a given object
@@ -556,7 +524,7 @@ objCallAsFunction :: MakeArgs args
556objCallAsFunction f this args = do
524objCallAsFunction f this args = do
557 rargs <- makeArgs args >>= liftIO . Array.fromListIO
525 rargs <- makeArgs args >>= liftIO . Array.fromListIO
558 liftIO $ js_apply f this rargs
526 liftIO $ js_apply f this rargs
559−foreign import javascript safe "$r = $1.apply($2, $3)"
527+foreign import javascript unsafe "$r = $1.apply($2, $3)"
560 js_apply :: Object -> Object -> MutableJSArray -> IO JSVal
528 js_apply :: Object -> Object -> MutableJSArray -> IO JSVal
561#else
529#else
562objCallAsFunction f this args = do
530objCallAsFunction f this args = do
@@ -576,7 +544,7 @@ objCallAsConstructor :: MakeArgs args
576objCallAsConstructor f args = do
544objCallAsConstructor f args = do
577 rargs <- makeArgs args >>= liftIO . Array.fromListIO
545 rargs <- makeArgs args >>= liftIO . Array.fromListIO
578 liftIO $ js_new f rargs
546 liftIO $ js_new f rargs
579−foreign import javascript safe "\
547+foreign import javascript unsafe "\
580 switch($2.length) {\
548 switch($2.length) {\
581 case 0 : $r = new $1(); break;\
549 case 0 : $r = new $1(); break;\
582 case 1 : $r = new $1($2[0]); break;\
550 case 1 : $r = new $1($2[0]); break;\
@@ -612,5 +580,5 @@ nullObject :: Object
612#ifdef ghcjs_HOST_OS
580#ifdef ghcjs_HOST_OS
613nullObject = Object nullRef
581nullObject = Object nullRef
614#else
582#else
615−nullObject = Object . JSVal . unsafePerformIO $ newIORef 0
583+nullObject = Object (JSVal 0)
616#endif
584#endif
jsaddle/src/Language/Javascript/JSaddle/Run.hs+82−150
@@ -1,9 +1,6 @@
1−{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
3{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE MagicHash #-}
6−{-# LANGUAGE UnboxedTuples #-}
7-----------------------------------------------------------------------------
4-----------------------------------------------------------------------------
8--
5--
9-- Module : Language.Javascript.JSaddle.Run
6-- Module : Language.Javascript.JSaddle.Run
@@ -37,58 +34,40 @@ module Language.Javascript.JSaddle.Run (
37) where
34) where
38
35
39#ifdef ghcjs_HOST_OS
36#ifdef ghcjs_HOST_OS
40−import Language.Javascript.JSaddle.Types (JSM, syncPoint, syncAfter)
37+import Language.Javascript.JSaddle.Types (JSM)
41import qualified JavaScript.Web.AnimationFrame as GHCJS
38import qualified JavaScript.Web.AnimationFrame as GHCJS
42 (waitForAnimationFrame)
39 (waitForAnimationFrame)
43#else
40#else
44−import Control.Exception (throwIO, evaluate)
41+import Control.Exception (throwIO)
45−import Control.Monad (void, when, zipWithM_)
42+import Control.Monad (void, when, forever, zipWithM_)
46import Control.Monad.IO.Class (MonadIO(..))
43import Control.Monad.IO.Class (MonadIO(..))
47import Control.Monad.Trans.Reader (ask, runReaderT)
44import Control.Monad.Trans.Reader (ask, runReaderT)
48import Control.Monad.STM (atomically)
45import Control.Monad.STM (atomically)
49−import Control.Concurrent (forkIO, myThreadId)
46+import Control.Concurrent (forkIO)
50import Control.Concurrent.STM.TChan
47import Control.Concurrent.STM.TChan
51 (tryReadTChan, TChan, readTChan, writeTChan, newTChanIO)
48 (tryReadTChan, TChan, readTChan, writeTChan, newTChanIO)
52import Control.Concurrent.STM.TVar
49import Control.Concurrent.STM.TVar
53 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
50 (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
54import Control.Concurrent.MVar
51import Control.Concurrent.MVar
55− (tryTakeMVar, MVar, putMVar, takeMVar, newMVar, newEmptyMVar, readMVar, modifyMVar)
52+ (MVar, MVar, putMVar, takeMVar, newEmptyMVar)
56
53
57import System.IO.Unsafe (unsafeInterleaveIO)
54import System.IO.Unsafe (unsafeInterleaveIO)
58import System.Mem.Weak (addFinalizer)
55import System.Mem.Weak (addFinalizer)
59−import System.Random
60
56
61−import GHC.Base (IO(..), mkWeak#)
62−import GHC.Conc (ThreadId(..))
63import Data.Monoid ((<>))
57import Data.Monoid ((<>))
64−import qualified Data.Text as T (unpack, pack)
58+import qualified Data.Text as T (unpack)
65import qualified Data.Map as M (lookup, delete, insert, empty, size)
59import qualified Data.Map as M (lookup, delete, insert, empty, size)
66−import qualified Data.Set as S (empty, member, insert, delete)
67import Data.Time.Clock (getCurrentTime,diffUTCTime)
60import Data.Time.Clock (getCurrentTime,diffUTCTime)
68−import Data.IORef
69− (mkWeakIORef, newIORef, atomicWriteIORef, readIORef)
61+import Data.IORef (newIORef, atomicWriteIORef, readIORef)
70
62
71import Language.Javascript.JSaddle.Types
63import Language.Javascript.JSaddle.Types
72− (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),
64+ (Command(..), AsyncCommand(..), Result(..), Results(..), JSContextRef(..), JSVal(..),
73− Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..), syncPoint, syncAfter, sendCommand)
65+ Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
74import Language.Javascript.JSaddle.Exception (JSException(..))
66import Language.Javascript.JSaddle.Exception (JSException(..))
67+-- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal)
75−import Control.DeepSeq (force, deepseq)
68+import Control.DeepSeq (deepseq)
76−#if MIN_VERSION_base(4,11,0)
77−import GHC.Stats (getRTSStatsEnabled, getRTSStats, RTSStats(..), gcdetails_live_bytes, gc)
78−#else
79import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
69import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
80#endif
70#endif
81−import Data.Foldable (forM_)
82−#endif
83−
84−#ifndef ghcjs_HOST_OS
85−#if MIN_VERSION_base(4,11,0)
86−currentBytesUsed = gcdetails_live_bytes . gc
87−#else
88−getRTSStatsEnabled = getGCStatsEnabled
89−getRTSStats = getGCStats
90−#endif
91−#endif
92
71
93-- | Enable (or disable) JSaddle logging
72-- | Enable (or disable) JSaddle logging
94enableLogging :: Bool -> JSM ()
73enableLogging :: Bool -> JSM ()
@@ -100,6 +79,27 @@ enableLogging v = do
100 liftIO $ f v
79 liftIO $ f v
101#endif
80#endif
102
81
82+-- | Forces execution of pending asyncronous code
83+syncPoint :: JSM ()
84+#ifdef ghcjs_HOST_OS
85+syncPoint = return ()
86+#else
87+syncPoint = do
88+ SyncResult <- sendCommand Sync
89+ return ()
90+#endif
91+
92+-- | Forces execution of pending asyncronous code after performing `f`
93+syncAfter :: JSM a -> JSM a
94+#ifdef ghcjs_HOST_OS
95+syncAfter = id
96+#else
97+syncAfter f = do
98+ result <- f
99+ syncPoint
100+ return result
101+#endif
102+
103-- | On GHCJS this is 'JavaScript.Web.AnimationFrame.waitForAnimationFrame'.
103-- | On GHCJS this is 'JavaScript.Web.AnimationFrame.waitForAnimationFrame'.
104-- On GHC it will delay the execution of the current batch of asynchronous
104-- On GHC it will delay the execution of the current batch of asynchronous
105-- command when they are sent to JavaScript. It will not delay the Haskell
105-- command when they are sent to JavaScript. It will not delay the Haskell
@@ -125,12 +125,17 @@ nextAnimationFrame f = do
125 syncAfter (f t)
125 syncAfter (f t)
126
126
127#ifndef ghcjs_HOST_OS
127#ifndef ghcjs_HOST_OS
128+sendCommand :: Command -> JSM Result
129+sendCommand cmd = do
130+ s <- doSendCommand <$> JSM ask
131+ liftIO $ s cmd
132+
128sendLazyCommand :: (JSValueForSend -> AsyncCommand) -> JSM JSVal
133sendLazyCommand :: (JSValueForSend -> AsyncCommand) -> JSM JSVal
129sendLazyCommand cmd = do
134sendLazyCommand cmd = do
130 nextRefTVar <- nextRef <$> JSM ask
135 nextRefTVar <- nextRef <$> JSM ask
131 n <- liftIO . atomically $ do
136 n <- liftIO . atomically $ do
132 n <- subtract 1 <$> readTVar nextRefTVar
137 n <- subtract 1 <$> readTVar nextRefTVar
133− writeTVar nextRefTVar $! n
138+ writeTVar nextRefTVar n
134 return n
139 return n
135 s <- doSendAsyncCommand <$> JSM ask
140 s <- doSendAsyncCommand <$> JSM ask
136 liftIO $ s (cmd $ JSValueForSend n)
141 liftIO $ s (cmd $ JSValueForSend n)
@@ -141,112 +146,66 @@ sendAsyncCommand cmd = do
141 s <- doSendAsyncCommand <$> JSM ask
146 s <- doSendAsyncCommand <$> JSM ask
142 liftIO $ s cmd
147 liftIO $ s cmd
143
148
144−runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), Results -> IO Batch, IO ())
149+runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), IO ())
145runJavaScript sendBatch entryPoint = do
150runJavaScript sendBatch entryPoint = do
146− contextId' <- randomIO
147 startTime' <- getCurrentTime
151 startTime' <- getCurrentTime
148 recvMVar <- newEmptyMVar
152 recvMVar <- newEmptyMVar
149− lastAsyncBatch <- newEmptyMVar
150 commandChan <- newTChanIO
153 commandChan <- newTChanIO
151 callbacks <- newTVarIO M.empty
154 callbacks <- newTVarIO M.empty
152 nextRef' <- newTVarIO 0
155 nextRef' <- newTVarIO 0
153− finalizerThreads' <- newMVar S.empty
154− animationFrameHandlers' <- newMVar []
155 loggingEnabled <- newIORef False
156 loggingEnabled <- newIORef False
156− liveRefs' <- newMVar S.empty
157 let ctx = JSContextRef {
157 let ctx = JSContextRef {
158− contextId = contextId'
159− , startTime = startTime'
158+ startTime = startTime'
160− , doSendCommand = \cmd -> cmd `deepseq` do
159+ , doSendCommand = \cmd -> cmd `deepseq` do
161− result <- newEmptyMVar
160+ result <- newEmptyMVar
162− atomically $ writeTChan commandChan (Right (cmd, result))
161+ atomically $ writeTChan commandChan (Right (cmd, result))
163− unsafeInterleaveIO $
162+ unsafeInterleaveIO $
164− takeMVar result >>= \case
163+ takeMVar result >>= \case
165− (ThrowJSValue v) -> do
166− jsval <- wrapJSVal' ctx v
167− throwIO $ JSException jsval
164+ (ThrowJSValue (JSValueReceived v)) -> throwIO $ JSException (JSVal v)
168− r -> return r
165+ r -> return r
169− , doSendAsyncCommand = \cmd -> cmd `deepseq` atomically (writeTChan commandChan $ Left cmd)
166+ , doSendAsyncCommand = \cmd -> cmd `deepseq` atomically (writeTChan commandChan $ Left cmd)
170− , addCallback = \(Object (JSVal ioref)) cb -> do
171− val <- readIORef ioref
172− atomically $ modifyTVar' callbacks (M.insert val cb)
167+ , addCallback = \(Object (JSVal val)) cb -> atomically $ modifyTVar' callbacks (M.insert val cb)
168+ , freeCallback = \(Object (JSVal val)) -> atomically $ modifyTVar' callbacks (M.delete val)
173− , nextRef = nextRef'
169+ , nextRef = nextRef'
174− , doEnableLogging = atomicWriteIORef loggingEnabled
170+ , doEnableLogging = atomicWriteIORef loggingEnabled
175− , finalizerThreads = finalizerThreads'
176− , animationFrameHandlers = animationFrameHandlers'
177− , liveRefs = liveRefs'
178− }
171+ }
179− processResults :: Bool -> Results -> IO ()
180− processResults syncCallbacks = \case
172+ let processResults = \case
181 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
173 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
182− (Callback n br (JSValueReceived fNumber) f this a) -> do
174+ (Callback f this a) -> do
183− putMVar recvMVar (n, br)
175+ logInfo ("Call " <>)
184− f' <- runReaderT (unJSM $ wrapJSVal f) ctx
176+ f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx
185 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
177 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
186 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
178 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
187− logInfo (("Call " <> show fNumber <> " ") <>)
188 (M.lookup fNumber <$> liftIO (readTVarIO callbacks)) >>= \case
179 (M.lookup fNumber <$> liftIO (readTVarIO callbacks)) >>= \case
189 Nothing -> liftIO $ putStrLn "Callback called after it was freed"
180 Nothing -> liftIO $ putStrLn "Callback called after it was freed"
190− Just cb -> void . forkIO $ do
191− runReaderT (unJSM $ cb f' this' args) ctx
181+ Just cb -> void . forkIO $ runReaderT (unJSM $ cb f' this' args) ctx
192− when syncCallbacks $
193− doSendAsyncCommand ctx EndSyncBlock
194− Duplicate nBatch nExpected -> do
195− putStrLn $ "Error : Unexpected Duplicate. syncCallbacks=" <> show syncCallbacks <>
196− " nBatch=" <> show nBatch <> " nExpected=" <> show nExpected
197− void $ doSendCommand ctx Sync
198− BatchResults n br -> putMVar recvMVar (n, br)
182+ m -> putMVar recvMVar m
199− asyncResults :: Results -> IO ()
200− asyncResults results =
201− void . forkIO $ processResults False results
202− syncResults :: Results -> IO Batch
203− syncResults results = do
204− void . forkIO $ processResults True results
205− readMVar lastAsyncBatch
206 logInfo s =
183 logInfo s =
207 readIORef loggingEnabled >>= \case
184 readIORef loggingEnabled >>= \case
208 True -> do
185 True -> do
209− currentBytesUsedStr <- getRTSStatsEnabled >>= \case
186+ currentBytesUsedStr <- getGCStatsEnabled >>= \case
210− True -> show . currentBytesUsed <$> getRTSStats
187+ True -> show . currentBytesUsed <$> getGCStats
211 False -> return "??"
188 False -> return "??"
212 cbCount <- M.size <$> readTVarIO callbacks
189 cbCount <- M.size <$> readTVarIO callbacks
213− putStrLn . s $ "M " <> currentBytesUsedStr <> " CB " <> show cbCount <> " "
190+ putStrLn . s $ "M " <> currentBytesUsedStr <> "CB " <> show cbCount
214 False -> return ()
191 False -> return ()
215− _ <- forkIO . numberForeverFromM_ 1 $ \nBatch ->
216− readBatch nBatch commandChan >>= \case
192+ _ <- forkIO . forever $ readBatch commandChan >>= \case
217− (batch@(Batch cmds _ _), resultMVars) -> do
193+ (batch@(Batch cmds _), resultMVars) -> do
218 logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))
194 logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))
219− _ <- tryTakeMVar lastAsyncBatch
220− putMVar lastAsyncBatch batch
221 sendBatch batch
195 sendBatch batch
222− takeResult recvMVar nBatch >>= \case
196+ takeMVar recvMVar >>= \case
223− (n, _) | n /= nBatch -> error $ "Unexpected jsaddle results (expected batch " <> show nBatch <> ", got batch " <> show n <> ")"
224− (_, Success callbacksToFree results)
225− | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
197+ Success results | length results /= length resultMVars -> error "Unexpected number of jsaddle results"
226− | otherwise -> do
227− zipWithM_ putMVar resultMVars results
198+ | otherwise -> zipWithM_ putMVar resultMVars results
228− forM_ callbacksToFree $ \(JSValueReceived val) ->
229− atomically (modifyTVar' callbacks (M.delete val))
230− (_, Failure callbacksToFree results exception err) -> do
199+ Failure results exception -> do
231 -- The exception will only be rethrown in Haskell if/when one of the
200 -- The exception will only be rethrown in Haskell if/when one of the
232 -- missing results (if any) is evaluated.
201 -- missing results (if any) is evaluated.
233 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
202 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
234− putStrLn err
235 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
203 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
236− forM_ callbacksToFree $ \(JSValueReceived val) ->
204+ _ -> error "Unexpected jsaddle results"
237− atomically (modifyTVar' callbacks (M.delete val))
238− return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
205+ return (processResults, runReaderT (unJSM entryPoint) ctx)
239 where
206 where
240− numberForeverFromM_ :: (Monad m, Enum n) => n -> (n -> m a) -> m ()
241− numberForeverFromM_ !n f = do
242− _ <- f n
243− numberForeverFromM_ (succ n) f
244− takeResult recvMVar nBatch =
245− takeMVar recvMVar >>= \case
246− (n, _) | n < nBatch -> takeResult recvMVar nBatch
247− r -> return r
248− readBatch :: Int -> TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])
207+ readBatch :: TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])
249− readBatch nBatch chan = do
208+ readBatch chan = do
250 first <- atomically $ readTChan chan -- We want at least one command to send
209 first <- atomically $ readTChan chan -- We want at least one command to send
251 loop first ([], [])
210 loop first ([], [])
252 where
211 where
@@ -257,55 +216,28 @@ runJavaScript sendBatch entryPoint = do
257 let cmds = Right syncCmd:cmds'
216 let cmds = Right syncCmd:cmds'
258 resultMVars = resultMVar:resultMVars'
217 resultMVars = resultMVar:resultMVars'
259 atomically (tryReadTChan chan) >>= \case
218 atomically (tryReadTChan chan) >>= \case
260− Nothing -> return (Batch (reverse cmds) False nBatch, reverse resultMVars)
219+ Nothing -> return (Batch (reverse cmds) False, reverse resultMVars)
261 Just cmd -> loop cmd (cmds, resultMVars)
220 Just cmd -> loop cmd (cmds, resultMVars)
262 loop (Left asyncCmd) (cmds', resultMVars) = do
221 loop (Left asyncCmd) (cmds', resultMVars) = do
263 let cmds = Left asyncCmd:cmds'
222 let cmds = Left asyncCmd:cmds'
264 atomically (tryReadTChan chan) >>= \case
223 atomically (tryReadTChan chan) >>= \case
265− Nothing -> return (Batch (reverse cmds) False nBatch, reverse resultMVars)
224+ Nothing -> return (Batch (reverse cmds) False, reverse resultMVars)
266 Just cmd -> loop cmd (cmds, resultMVars)
225 Just cmd -> loop cmd (cmds, resultMVars)
267 -- When we have seen a SyncWithAnimationFrame command only a synchronous command should end the batch
226 -- When we have seen a SyncWithAnimationFrame command only a synchronous command should end the batch
268 loopAnimation :: Either AsyncCommand (Command, MVar Result) -> ([Either AsyncCommand Command], [MVar Result]) -> IO (Batch, [MVar Result])
227 loopAnimation :: Either AsyncCommand (Command, MVar Result) -> ([Either AsyncCommand Command], [MVar Result]) -> IO (Batch, [MVar Result])
269 loopAnimation (Right (Sync, resultMVar)) (cmds, resultMVars) =
228 loopAnimation (Right (Sync, resultMVar)) (cmds, resultMVars) =
270− return (Batch (reverse (Right Sync:cmds)) True nBatch, reverse (resultMVar:resultMVars))
229+ return (Batch (reverse (Right Sync:cmds)) True, reverse (resultMVar:resultMVars))
271 loopAnimation (Right (syncCmd, resultMVar)) (cmds, resultMVars) =
230 loopAnimation (Right (syncCmd, resultMVar)) (cmds, resultMVars) =
272 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Right syncCmd:cmds, resultMVar:resultMVars)
231 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Right syncCmd:cmds, resultMVar:resultMVars)
273 loopAnimation (Left asyncCmd) (cmds, resultMVars) =
232 loopAnimation (Left asyncCmd) (cmds, resultMVars) =
274 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Left asyncCmd:cmds, resultMVars)
233 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Left asyncCmd:cmds, resultMVars)
275
234
276−addThreadFinalizer :: ThreadId -> IO () -> IO ()
277−addThreadFinalizer t@(ThreadId t#) (IO finalizer) =
278− IO $ \s -> case mkWeak# t# t finalizer s of { (# s1, _ #) -> (# s1, () #) }
279−
280−
281wrapJSVal :: JSValueReceived -> JSM JSVal
235wrapJSVal :: JSValueReceived -> JSM JSVal
282−wrapJSVal v = do
236+wrapJSVal (JSValueReceived ref) = do
283− ctx <- JSM ask
237+ -- TODO make sure this ref has not already been wrapped (perhaps only in debug version)
284− liftIO $ wrapJSVal' ctx v
238+ let result = JSVal ref
285−
286−wrapJSVal' :: JSContextRef -> JSValueReceived -> IO JSVal
287−wrapJSVal' ctx (JSValueReceived n) = do
288− ref <- liftIO $ newIORef n
289− when (n >= 5 || n < 0) $
239+ when (ref >= 5) $ do
290−#ifdef JSADDLE_CHECK_WRAPJSVAL
240+ ctx <- JSM ask
291− do lr <- takeMVar $ liveRefs ctx
292− if n `S.member` lr
293− then do
294− putStrLn $ "JS Value Ref " <> show n <> " already wrapped"
295− putMVar (liveRefs ctx) lr
296− else putMVar (liveRefs ctx) =<< evaluate (S.insert n lr)
297−#endif
298− void . mkWeakIORef ref $ do
299− ft <- takeMVar $ finalizerThreads ctx
300− t <- myThreadId
301− let tname = T.pack $ show t
302− doSendAsyncCommand ctx $ FreeRef tname $ JSValueForSend n
241+ liftIO . addFinalizer result $ doSendAsyncCommand ctx $ FreeRef $ JSValueForSend ref
303− if tname `S.member` ft
304− then putMVar (finalizerThreads ctx) ft
305− else do
306− addThreadFinalizer t $ do
307− modifyMVar (finalizerThreads ctx) $ \s -> return (S.delete tname s, ())
308− doSendAsyncCommand ctx $ FreeRefs tname
309− putMVar (finalizerThreads ctx) =<< evaluate (S.insert tname ft)
310− return (JSVal ref)
242+ return result
311#endif
243#endif
jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+56−132
@@ -13,6 +13,7 @@
13
13
14module Language.Javascript.JSaddle.Run.Files (
14module Language.Javascript.JSaddle.Run.Files (
15 indexHtml
15 indexHtml
16+ , jsaddleJs
16 , initState
17 , initState
17 , runBatch
18 , runBatch
18 , ghcjsHelpers
19 , ghcjsHelpers
@@ -30,38 +31,25 @@ indexHtml =
30 \</head>\n\
31 \</head>\n\
31 \<body>\n\
32 \<body>\n\
32 \</body>\n\
33 \</body>\n\
33− \<script src=\"/jsaddle.js\"></script>\n\
34+ \<script src=\"jsaddle.js\"></script>\n\
34 \</html>"
35 \</html>"
35
36
36initState :: ByteString
37initState :: ByteString
37initState = "\
38initState = "\
38 \ var jsaddle_values = new Map();\n\
39 \ var jsaddle_values = new Map();\n\
39− \ var jsaddle_free = new Map();\n\
40 \ jsaddle_values.set(0, null);\n\
40 \ jsaddle_values.set(0, null);\n\
41 \ jsaddle_values.set(1, undefined);\n\
41 \ jsaddle_values.set(1, undefined);\n\
42 \ jsaddle_values.set(2, false);\n\
42 \ jsaddle_values.set(2, false);\n\
43 \ jsaddle_values.set(3, true);\n\
43 \ jsaddle_values.set(3, true);\n\
44− \ jsaddle_values.set(4, globalThis);\n\
44+ \ jsaddle_values.set(4, window);\n\
45 \ var jsaddle_index = 100;\n\
45 \ var jsaddle_index = 100;\n\
46− \ var expectedBatch = 1;\n\
47− \ var lastResults = [0, {\"tag\": \"Success\", \"contents\": [[], []]}];\n\
48− \ var inCallback = 0;\n\
49− \ var asyncBatch = null;\n\
50 \"
46 \"
51
47
52−runBatch :: (ByteString -> ByteString) -> Maybe (ByteString -> ByteString) -> ByteString
48+runBatch :: (ByteString -> ByteString) -> ByteString
53−runBatch send sendSync = "\
49+runBatch send = "\
54− \ var runBatch = function(firstBatch, initialSyncDepth) {\n\
55 \ var processBatch = function(timestamp) {\n\
50 \ var processBatch = function(timestamp) {\n\
56− \ var batch = firstBatch;\n\
57− \ var callbacksToFree = [];\n\
58− \ var results = [];\n\
51+ \ var results = [];\n\
59− \ inCallback++;\n\
60− \ try {\n\
52+ \ try {\n\
61− \ syncDepth = initialSyncDepth || 0;\n\
62− \ for(;;){\n\
63− \ if(batch[2] === expectedBatch) {\n\
64− \ expectedBatch++;\n\
65 \ var nCommandsLength = batch[0].length;\n\
53 \ var nCommandsLength = batch[0].length;\n\
66 \ for (var nCommand = 0; nCommand != nCommandsLength; nCommand++) {\n\
54 \ for (var nCommand = 0; nCommand != nCommandsLength; nCommand++) {\n\
67 \ var cmd = batch[0][nCommand];\n\
55 \ var cmd = batch[0][nCommand];\n\
@@ -69,15 +57,7 @@ runBatch send sendSync = "\
69 \ var d = cmd.Left;\n\
57 \ var d = cmd.Left;\n\
70 \ switch (d.tag) {\n\
58 \ switch (d.tag) {\n\
71 \ case \"FreeRef\":\n\
59 \ case \"FreeRef\":\n\
72− \ var refsToFree = jsaddle_free.get(d.contents[0]) || [];\n\
73− \ refsToFree.push(d.contents[1]);\n\
74− \ jsaddle_free.set(d.contents[0], refsToFree);\n\
75− \ break;\n\
76− \ case \"FreeRefs\":\n\
77− \ var refsToFree = jsaddle_free.get(d.contents) || [];\n\
78− \ for(var nRef = 0; nRef != refsToFree.length; nRef++)\n\
79− \ jsaddle_values.delete(refsToFree[nRef]);\n\
80− \ jsaddle_free.delete(d.contents);\n\
60+ \ jsaddle_values.delete(d.contents);\n\
81 \ break;\n\
61 \ break;\n\
82 \ case \"SetPropertyByName\":\n\
62 \ case \"SetPropertyByName\":\n\
83 \ jsaddle_values.get(d.contents[0])[d.contents[1]]=jsaddle_values.get(d.contents[2]);\n\
63 \ jsaddle_values.get(d.contents[0])[d.contents[1]]=jsaddle_values.get(d.contents[2]);\n\
@@ -93,10 +73,6 @@ runBatch send sendSync = "\
93 \ var n = d.contents[1];\n\
73 \ var n = d.contents[1];\n\
94 \ jsaddle_values.set(n, d.contents[0]);\n\
74 \ jsaddle_values.set(n, d.contents[0]);\n\
95 \ break;\n\
75 \ break;\n\
96− \ case \"JSONValueToValue\":\n\
97− \ var n = d.contents[1];\n\
98− \ jsaddle_values.set(n, d.contents[0]);\n\
99− \ break;\n\
100 \ case \"GetPropertyByName\":\n\
76 \ case \"GetPropertyByName\":\n\
101 \ var n = d.contents[2];\n\
77 \ var n = d.contents[2];\n\
102 \ jsaddle_values.set(n, jsaddle_values.get(d.contents[0])[d.contents[1]]);\n\
78 \ jsaddle_values.set(n, jsaddle_values.get(d.contents[0])[d.contents[1]]);\n\
@@ -113,12 +89,10 @@ runBatch send sendSync = "\
113 \ var n = d.contents;\n\
89 \ var n = d.contents;\n\
114 \ jsaddle_values.set(n, {});\n\
90 \ jsaddle_values.set(n, {});\n\
115 \ break;\n\
91 \ break;\n\
116− \ case \"NewAsyncCallback\":\n\
92+ \ case \"NewCallback\":\n\
117 \ (function() {\n\
93 \ (function() {\n\
118 \ var nFunction = d.contents;\n\
94 \ var nFunction = d.contents;\n\
119− \ var func = function() {\n\
120− \ var nFunctionInFunc = ++jsaddle_index;\n\
121− \ jsaddle_values.set(nFunctionInFunc, func);\n\
95+ \ jsaddle_values.set(nFunction, function() {\n\
122 \ var nThis = ++jsaddle_index;\n\
96 \ var nThis = ++jsaddle_index;\n\
123 \ jsaddle_values.set(nThis, this);\n\
97 \ jsaddle_values.set(nThis, this);\n\
124 \ var args = [];\n\
98 \ var args = [];\n\
@@ -127,41 +101,8 @@ runBatch send sendSync = "\
127 \ jsaddle_values.set(nArg, arguments[i]);\n\
101 \ jsaddle_values.set(nArg, arguments[i]);\n\
128 \ args[i] = nArg;\n\
102 \ args[i] = nArg;\n\
129 \ }\n\
103 \ }\n\
130− \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nFunctionInFunc, nThis, args]}" <> "\n\
104+ \ " <> send "{\"tag\": \"Callback\", \"contents\": [nFunction, nThis, args]}" <> "\n\
131− \ };\n\
105+ \ })})();\n\
132− \ jsaddle_values.set(nFunction, func);\n\
133− \ })();\n\
134− \ break;\n\
135− \ case \"NewSyncCallback\":\n\
136− \ (function() {\n\
137− \ var nFunction = d.contents;\n\
138− \ var func = function() {\n\
139− \ var nFunctionInFunc = ++jsaddle_index;\n\
140− \ jsaddle_values.set(nFunctionInFunc, func);\n\
141− \ var nThis = ++jsaddle_index;\n\
142− \ jsaddle_values.set(nThis, this);\n\
143− \ var args = [];\n\
144− \ for (var i = 0; i != arguments.length; i++) {\n\
145− \ var nArg = ++jsaddle_index;\n\
146− \ jsaddle_values.set(nArg, arguments[i]);\n\
147− \ args[i] = nArg;\n\
148− \ }\n" <> (
149− case sendSync of
150− Just s ->
151− " if(inCallback > 0) {\n\
152− \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nFunctionInFunc, nThis, args]}" <> "\n\
153− \ } else {\n\
154− \ runBatch(" <> s "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nFunctionInFunc, nThis, args]}" <> ", 1);\n\
155− \ }\n"
156− Nothing ->
157− " " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nFunctionInFunc, nThis, args]}" <> "\n"
158− ) <>
159− " };\n\
160− \ jsaddle_values.set(nFunction, func);\n\
161− \ })();\n\
162− \ break;\n\
163− \ case \"FreeCallback\":\n\
164− \ callbacksToFree.push(d.contents);\n\
165 \ break;\n\
106 \ break;\n\
166 \ case \"CallAsFunction\":\n\
107 \ case \"CallAsFunction\":\n\
167 \ var n = d.contents[3];\n\
108 \ var n = d.contents[3];\n\
@@ -204,15 +145,9 @@ runBatch send sendSync = "\
204 \ jsaddle_values.set(n, d.contents[0].map(function(v){return jsaddle_values.get(v);}));\n\
145 \ jsaddle_values.set(n, d.contents[0].map(function(v){return jsaddle_values.get(v);}));\n\
205 \ break;\n\
146 \ break;\n\
206 \ case \"SyncWithAnimationFrame\":\n\
147 \ case \"SyncWithAnimationFrame\":\n\
207− \ var n = d.contents;\n\
148+ \ var n = d.contents[0];\n\
208 \ jsaddle_values.set(n, timestamp);\n\
149 \ jsaddle_values.set(n, timestamp);\n\
209 \ break;\n\
150 \ break;\n\
210− \ case \"StartSyncBlock\":\n\
211− \ syncDepth++;\n\
212− \ break;\n\
213− \ case \"EndSyncBlock\":\n\
214− \ syncDepth--;\n\
215− \ break;\n\
216 \ default:\n\
151 \ default:\n\
217 \ " <> send "{\"tag\": \"ProtocolError\", \"contents\": e.data}" <> "\n\
152 \ " <> send "{\"tag\": \"ProtocolError\", \"contents\": e.data}" <> "\n\
218 \ return;\n\
153 \ return;\n\
@@ -247,7 +182,7 @@ runBatch send sendSync = "\
247 \ (v === true ) ? [3, \"\"] :\n\
182 \ (v === true ) ? [3, \"\"] :\n\
248 \ (typeof v === \"number\") ? [-1, v.toString()] :\n\
183 \ (typeof v === \"number\") ? [-1, v.toString()] :\n\
249 \ (typeof v === \"string\") ? [-2, v]\n\
184 \ (typeof v === \"string\") ? [-2, v]\n\
250− \ : [-3, \"\"];\n\
185+ \ : [n, \"\"];\n\
251 \ results.push({\"tag\": \"DeRefValResult\", \"contents\": c});\n\
186 \ results.push({\"tag\": \"DeRefValResult\", \"contents\": c});\n\
252 \ break;\n\
187 \ break;\n\
253 \ case \"IsNull\":\n\
188 \ case \"IsNull\":\n\
@@ -275,66 +210,55 @@ runBatch send sendSync = "\
275 \ }\n\
210 \ }\n\
276 \ }\n\
211 \ }\n\
277 \ }\n\
212 \ }\n\
278− \ if(syncDepth <= 0) {\n\
213+ \ " <> send "{\"tag\": \"Success\", \"contents\": results}" <> "\n\
279− \ lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": [callbacksToFree, results]}];\n\
214+ \ }\n\
280− \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> "\n\
215+ \ catch (err) {\n\
281− \ break;\n\
216+ \ var n = ++jsaddle_index;\n\
282− \ } else {\n" <> (
217+ \ jsaddle_values.set(n, err);\n\
283− case sendSync of
284− Just s ->
285− " lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": [callbacksToFree, results]}];\n\
286− \ batch = " <> s "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> ";\n\
287− \ results = [];\n\
288− \ callbacksToFree = [];\n"
289− Nothing ->
290− " " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Success\", \"contents\": [callbacksToFree, results]}]}" <> "\n\
291− \ break;\n"
292− ) <>
293− " }\n\
294− \ } else {\n\
295− \ if(syncDepth <= 0) {\n\
296− \ break;\n\
297− \ } else {\n" <> (
298− case sendSync of
299− Just s ->
300− " if(batch[2] === expectedBatch - 1) {\n\
301− \ batch = " <> s "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> ";\n\
302− \ } else {\n\
303− \ batch = " <> s "{\"tag\": \"Duplicate\", \"contents\": [batch[2], expectedBatch]}" <> ";\n\
304− \ }\n\
305− \ results = [];\n\
306− \ callbacksToFree = [];\n"
307− Nothing ->
308− " " <> send "{\"tag\": \"Duplicate\", \"contents\": [batch[2], expectedBatch]}" <> "\n\
218+ \ " <> send "{\"tag\": \"Failure\", \"contents\": [results,n]}" <> "\n\
309− \ break;\n"
310− ) <>
311− " }\n\
312− \ }\n\
313 \ }\n\
219 \ }\n\
314− \ }\n\
315− \ catch (err) {\n\
316− \ var n = ++jsaddle_index;\n\
317− \ jsaddle_values.set(n, err);\n\
318− \ console.log(err);\n\
319− \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Failure\", \"contents\": [callbacksToFree, results, n, String(err)]}]}" <> "\n\
320− \ }\n\
321− \ if(inCallback == 1) {\n\
322− \ while(asyncBatch !== null) {\n\
323− \ var b = asyncBatch;\n\
324− \ asyncBatch = null;\n\
325− \ if(b[2] == expectedBatch) runBatch(b);\n\
326− \ }\n\
327− \ }\n\
328− \ inCallback--;\n\
329 \ };\n\
220 \ };\n\
330− \ if(batch[1] && (initialSyncDepth || 0) === 0) {\n\
221+ \ if(batch[1]) {\n\
331 \ window.requestAnimationFrame(processBatch);\n\
222 \ window.requestAnimationFrame(processBatch);\n\
332 \ }\n\
223 \ }\n\
333 \ else {\n\
224 \ else {\n\
334 \ processBatch(window.performance ? window.performance.now() : null);\n\
225 \ processBatch(window.performance ? window.performance.now() : null);\n\
335 \ }\n\
226 \ }\n\
336− \ };\n\
227+ \"
337− \ runBatch(batch);\n\
228+
229+-- Use this to generate this string for embedding
230+-- sed -e 's|\\|\\\\|g' -e 's|^| \\|' -e 's|$|\\n\\|' -e 's|"|\\"|g' data/jsaddle.js | pbcopy
231+jsaddleJs :: ByteString
232+jsaddleJs = "\
233+ \if(typeof global !== \"undefined\") {\n\
234+ \ global.window = global;\n\
235+ \ global.WebSocket = require('ws');\n\
236+ \}\n\
237+ \\n\
238+ \var connect = function() {\n\
239+ \ var wsaddress =\n\
240+ \ typeof window.location === \"undefined\"\n\
241+ \ ? \"ws://localhost:3709/\"\n\
242+ \ : window.location.protocol.replace('http', 'ws')+\"//\"+window.location.hostname+(window.location.port?(\":\"+window.location.port):\"\");\n\
243+ \\n\
244+ \ var ws = new WebSocket(wsaddress);\n\
245+ \\n\
246+ \ ws.onopen = function(e) {\n\
247+ \ " <> initState <> "\
248+ \\n\
249+ \ ws.onmessage = function(e) {\n\
250+ \ var batch = JSON.parse(e.data);\n\
251+ \\n\
252+ \ " <> runBatch (\a -> "ws.send(JSON.stringify(" <> a <> "));") <> "\
253+ \ };\n\
254+ \ };\n\
255+ \ ws.onerror = function() {\n\
256+ \ setTimeout(connect, 1000);\n\
257+ \ };\n\
258+ \}\n\
259+ \\n\
260+ \ " <> ghcjsHelpers <> "\
261+ \connect();\n\
338 \"
262 \"
339
263
340ghcjsHelpers :: ByteString
264ghcjsHelpers :: ByteString
jsaddle/src/Language/Javascript/JSaddle/Types.hs+25−155
@@ -14,7 +14,6 @@
14#endif
14#endif
15{-# LANGUAGE ConstraintKinds #-}
15{-# LANGUAGE ConstraintKinds #-}
16{-# LANGUAGE KindSignatures #-}
16{-# LANGUAGE KindSignatures #-}
17−{-# LANGUAGE AllowAmbiguousTypes #-}
18-----------------------------------------------------------------------------
17-----------------------------------------------------------------------------
19--
18--
20-- Module : Language.Javascript.JSaddle.Types
19-- Module : Language.Javascript.JSaddle.Types
@@ -58,14 +57,8 @@ module Language.Javascript.JSaddle.Types (
58 -- * Debugging
57 -- * Debugging
59 , JSadddleHasCallStack
58 , JSadddleHasCallStack
60
59
61− -- * Sync JSM
62− , syncPoint
63− , syncAfter
64−
65−#ifndef ghcjs_HOST_OS
66− , sendCommand
67−
68 -- * JavaScript Context Commands
60 -- * JavaScript Context Commands
61+#ifndef ghcjs_HOST_OS
69 , MutabilityType(..)
62 , MutabilityType(..)
70 , Mutable
63 , Mutable
71 , Immutable
64 , Immutable
@@ -80,7 +73,6 @@ module Language.Javascript.JSaddle.Types (
80 , Command(..)
73 , Command(..)
81 , Batch(..)
74 , Batch(..)
82 , Result(..)
75 , Result(..)
83− , BatchResults(..)
84 , Results(..)
76 , Results(..)
85#endif
77#endif
86) where
78) where
@@ -95,27 +87,14 @@ import GHCJS.Nullable (Nullable(..))
95import GHCJS.Prim.Internal (JSVal(..), JSValueRef)
87import GHCJS.Prim.Internal (JSVal(..), JSValueRef)
96import Data.JSString.Internal.Type (JSString(..))
88import Data.JSString.Internal.Type (JSString(..))
97import Control.DeepSeq (NFData(..))
89import Control.DeepSeq (NFData(..))
98−import Control.Monad.Catch (MonadThrow, MonadCatch(..), MonadMask(..))
99−import Control.Monad.Trans.Cont (ContT(..))
100−import Control.Monad.Trans.Error (Error(..), ErrorT(..))
101−import Control.Monad.Trans.Except (ExceptT(..))
102−import Control.Monad.Trans.Identity (IdentityT(..))
103−import Control.Monad.Trans.List (ListT(..))
104−import Control.Monad.Trans.Maybe (MaybeT(..))
105−import Control.Monad.Trans.Reader (ReaderT(..), ask)
90+import Control.Monad.Trans.Reader (ReaderT(..))
106−import Control.Monad.Trans.RWS.Lazy as Lazy (RWST(..))
107−import Control.Monad.Trans.RWS.Strict as Strict (RWST(..))
91+import Control.Monad.Trans.State.Lazy (StateT(..))
108−import Control.Monad.Trans.State.Lazy as Lazy (StateT(..))
109−import Control.Monad.Trans.State.Strict as Strict (StateT(..))
92+import qualified Control.Monad.Trans.State.Strict as Strict
110−import Control.Monad.Trans.Writer.Lazy as Lazy (WriterT(..))
93+ (StateT(..))
111−import Control.Monad.Trans.Writer.Strict as Strict (WriterT(..))
112import Control.Monad.Trans.Class (MonadTrans(..))
94import Control.Monad.Trans.Class (MonadTrans(..))
113import Control.Monad.Fix (MonadFix)
95import Control.Monad.Fix (MonadFix)
114import Control.Monad.Ref (MonadAtomicRef(..), MonadRef(..))
96import Control.Monad.Ref (MonadAtomicRef(..), MonadRef(..))
115import Control.Concurrent.STM.TVar (TVar)
97import Control.Concurrent.STM.TVar (TVar)
116−import Control.Concurrent.MVar (MVar)
117−import Data.Int (Int64)
118−import Data.Set (Set)
119import Data.Text (Text)
98import Data.Text (Text)
120import Data.Time.Clock (UTCTime(..))
99import Data.Time.Clock (UTCTime(..))
121import Data.Typeable (Typeable)
100import Data.Typeable (Typeable)
@@ -123,8 +102,6 @@ import Data.Coerce (coerce, Coercible)
123import Data.Aeson
102import Data.Aeson
124 (defaultOptions, genericToEncoding, ToJSON(..), FromJSON(..), Value)
103 (defaultOptions, genericToEncoding, ToJSON(..), FromJSON(..), Value)
125import GHC.Generics (Generic)
104import GHC.Generics (Generic)
126−import Control.Monad.IO.Unlift (MonadUnliftIO)
127−import qualified Control.Monad.Fail as Fail
128#endif
105#endif
129
106
130#if MIN_VERSION_base(4,9,0) && defined(CHECK_UNCHECKED)
107#if MIN_VERSION_base(4,9,0) && defined(CHECK_UNCHECKED)
@@ -141,16 +118,13 @@ import GHC.Exts (Constraint)
141type JSContextRef = ()
118type JSContextRef = ()
142#else
119#else
143data JSContextRef = JSContextRef {
120data JSContextRef = JSContextRef {
144− contextId :: Int64
145− , startTime :: UTCTime
121+ startTime :: UTCTime
146− , doSendCommand :: Command -> IO Result
122+ , doSendCommand :: Command -> IO Result
147− , doSendAsyncCommand :: AsyncCommand -> IO ()
123+ , doSendAsyncCommand :: AsyncCommand -> IO ()
148− , addCallback :: Object -> JSCallAsFunction -> IO ()
124+ , addCallback :: Object -> JSCallAsFunction -> IO ()
125+ , freeCallback :: Object -> IO ()
149− , nextRef :: TVar JSValueRef
126+ , nextRef :: TVar JSValueRef
150− , doEnableLogging :: Bool -> IO ()
127+ , doEnableLogging :: Bool -> IO ()
151− , finalizerThreads :: MVar (Set Text)
152− , animationFrameHandlers :: MVar [Double -> JSM ()]
153− , liveRefs :: MVar (Set Int64)
154}
128}
155#endif
129#endif
156
130
@@ -166,56 +140,9 @@ data JSContextRef = JSContextRef {
166type JSM = IO
140type JSM = IO
167#else
141#else
168newtype JSM a = JSM { unJSM :: ReaderT JSContextRef IO a }
142newtype JSM a = JSM { unJSM :: ReaderT JSContextRef IO a }
169− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadThrow, MonadUnliftIO, Fail.MonadFail)
143+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
170−
171−instance MonadCatch JSM where
172− t `catch` c = JSM (unJSM (syncAfter t) `catch` \e -> unJSM (c e))
173−
174−instance MonadMask JSM where
175− mask a = JSM $ mask $ \unmask -> unJSM (a $ q unmask)
176− where q :: (ReaderT JSContextRef IO a -> ReaderT JSContextRef IO a) -> JSM a -> JSM a
177− q unmask (JSM b) = syncAfter . JSM $ unmask b
178− uninterruptibleMask a =
179− JSM $ uninterruptibleMask $ \unmask -> unJSM (a $ q unmask)
180− where q :: (ReaderT JSContextRef IO a -> ReaderT JSContextRef IO a) -> JSM a -> JSM a
181− q unmask (JSM b) = syncAfter . JSM $ unmask b
182−#if MIN_VERSION_exceptions(0,9,0)
183− generalBracket acquire release use =
184− JSM $ generalBracket
185− (unJSM acquire)
186− (\resource exitCase -> unJSM $ release resource exitCase)
187− (unJSM . syncAfter . use)
188−#endif
189−
190−#endif
191−
192−-- | Forces execution of pending asyncronous code
193−syncPoint :: JSM ()
194−#ifdef ghcjs_HOST_OS
195−syncPoint = return ()
196−#else
197−syncPoint = do
198− SyncResult <- sendCommand Sync
199− return ()
200#endif
144#endif
201
145
202−-- | Forces execution of pending asyncronous code after performing `f`
203−syncAfter :: JSM a -> JSM a
204−#ifdef ghcjs_HOST_OS
205−syncAfter = id
206−#else
207−syncAfter f = do
208− result <- f
209− syncPoint
210− return result
211−#endif
212−
213−#ifndef ghcjs_HOST_OS
214−sendCommand :: Command -> JSM Result
215−sendCommand cmd = do
216− s <- doSendCommand <$> JSM ask
217− liftIO $ s cmd
218−#endif
219
146
220-- | Type we can give to functions that are pure when using ghcjs, but
147-- | Type we can give to functions that are pure when using ghcjs, but
221-- live in JSM when using jsaddle.
148-- live in JSM when using jsaddle.
@@ -264,64 +191,21 @@ type MonadJSM = MonadIO
264#else
191#else
265class (Applicative m, MonadIO m) => MonadJSM m where
192class (Applicative m, MonadIO m) => MonadJSM m where
266 liftJSM' :: JSM a -> m a
193 liftJSM' :: JSM a -> m a
267−
268− default liftJSM' :: (MonadJSM m', MonadTrans t, m ~ t m') => JSM a -> m a
269− liftJSM' = lift . (liftJSM' :: MonadJSM m' => JSM a -> m' a)
270− {-# INLINE liftJSM' #-}
194+ {-# MINIMAL liftJSM' #-}
271
195
272instance MonadJSM JSM where
196instance MonadJSM JSM where
273 liftJSM' = id
197 liftJSM' = id
274 {-# INLINE liftJSM' #-}
198 {-# INLINE liftJSM' #-}
275
199
276−instance (MonadJSM m) => MonadJSM (ContT r m) where
200+instance MonadJSM m => MonadJSM (ReaderT e m) where
277− liftJSM' = lift . liftJSM'
278− {-# INLINE liftJSM' #-}
279−
280−instance (Error e, MonadJSM m) => MonadJSM (ErrorT e m) where
281− liftJSM' = lift . liftJSM'
282− {-# INLINE liftJSM' #-}
283−
284−instance (MonadJSM m) => MonadJSM (ExceptT e m) where
285− liftJSM' = lift . liftJSM'
286− {-# INLINE liftJSM' #-}
287−
288−instance (MonadJSM m) => MonadJSM (IdentityT m) where
289 liftJSM' = lift . liftJSM'
201 liftJSM' = lift . liftJSM'
290 {-# INLINE liftJSM' #-}
202 {-# INLINE liftJSM' #-}
291
203
292−instance (MonadJSM m) => MonadJSM (ListT m) where
204+instance MonadJSM m => MonadJSM (StateT r m) where
293 liftJSM' = lift . liftJSM'
205 liftJSM' = lift . liftJSM'
294 {-# INLINE liftJSM' #-}
206 {-# INLINE liftJSM' #-}
295
207
296−instance (MonadJSM m) => MonadJSM (MaybeT m) where
297− liftJSM' = lift . liftJSM'
298− {-# INLINE liftJSM' #-}
299−
300−instance (MonadJSM m) => MonadJSM (ReaderT r m) where
208+instance MonadJSM m => MonadJSM (Strict.StateT r m) where
301− liftJSM' = lift . liftJSM'
302− {-# INLINE liftJSM' #-}
303−
304−instance (Monoid w, MonadJSM m) => MonadJSM (Lazy.RWST r w s m) where
305− liftJSM' = lift . liftJSM'
306− {-# INLINE liftJSM' #-}
307−
308−instance (Monoid w, MonadJSM m) => MonadJSM (Strict.RWST r w s m) where
309− liftJSM' = lift . liftJSM'
310− {-# INLINE liftJSM' #-}
311−
312−instance (MonadJSM m) => MonadJSM (Lazy.StateT s m) where
313− liftJSM' = lift . liftJSM'
314− {-# INLINE liftJSM' #-}
315−
316−instance (MonadJSM m) => MonadJSM (Strict.StateT s m) where
317− liftJSM' = lift . liftJSM'
318− {-# INLINE liftJSM' #-}
319−
320−instance (Monoid w, MonadJSM m) => MonadJSM (Lazy.WriterT w m) where
321− liftJSM' = lift . liftJSM'
322− {-# INLINE liftJSM' #-}
323−
324−instance (Monoid w, MonadJSM m) => MonadJSM (Strict.WriterT w m) where
325 liftJSM' = lift . liftJSM'
209 liftJSM' = lift . liftJSM'
326 {-# INLINE liftJSM' #-}
210 {-# INLINE liftJSM' #-}
327
211
@@ -396,7 +280,7 @@ type MutableJSArray = SomeJSArray Mutable
396type STJSArray s = SomeJSArray (STMutable s)
280type STJSArray s = SomeJSArray (STMutable s)
397
281
398-- | See 'JavaScript.Object.Internal.Object'
282-- | See 'JavaScript.Object.Internal.Object'
399−newtype Object = Object JSVal
283+newtype Object = Object JSVal deriving(Show, ToJSON, FromJSON)
400
284
401-- | See 'GHCJS.Nullable.Nullable'
285-- | See 'GHCJS.Nullable.Nullable'
402newtype Nullable a = Nullable a
286newtype Nullable a = Nullable a
@@ -420,8 +304,7 @@ newtype JSStringForSend = JSStringForSend Text deriving(Show, ToJSON, FromJSON,
420instance NFData JSStringForSend
304instance NFData JSStringForSend
421
305
422-- | Command sent to a JavaScript context for execution asynchronously
306-- | Command sent to a JavaScript context for execution asynchronously
423−data AsyncCommand = FreeRef Text JSValueForSend
307+data AsyncCommand = FreeRef JSValueForSend
424− | FreeRefs Text
425 | SetPropertyByName JSObjectForSend JSStringForSend JSValueForSend
308 | SetPropertyByName JSObjectForSend JSStringForSend JSValueForSend
426 | SetPropertyAtIndex JSObjectForSend Int JSValueForSend
309 | SetPropertyAtIndex JSObjectForSend Int JSValueForSend
427 | StringToValue JSStringForSend JSValueForSend
310 | StringToValue JSStringForSend JSValueForSend
@@ -432,15 +315,11 @@ data AsyncCommand = FreeRef Text JSValueForSend
432 | CallAsFunction JSObjectForSend JSObjectForSend [JSValueForSend] JSValueForSend
315 | CallAsFunction JSObjectForSend JSObjectForSend [JSValueForSend] JSValueForSend
433 | CallAsConstructor JSObjectForSend [JSValueForSend] JSValueForSend
316 | CallAsConstructor JSObjectForSend [JSValueForSend] JSValueForSend
434 | NewEmptyObject JSValueForSend
317 | NewEmptyObject JSValueForSend
435− | NewAsyncCallback JSValueForSend
436− | NewSyncCallback JSValueForSend
437− | FreeCallback JSValueForSend
318+ | NewCallback JSValueForSend
438 | NewArray [JSValueForSend] JSValueForSend
319 | NewArray [JSValueForSend] JSValueForSend
439 | EvaluateScript JSStringForSend JSValueForSend
320 | EvaluateScript JSStringForSend JSValueForSend
440 | SyncWithAnimationFrame JSValueForSend
321 | SyncWithAnimationFrame JSValueForSend
441− | StartSyncBlock
442− | EndSyncBlock
443− deriving (Show, Generic)
322+ deriving (Show, Generic)
444
323
445instance ToJSON AsyncCommand where
324instance ToJSON AsyncCommand where
446 toEncoding = genericToEncoding defaultOptions
325 toEncoding = genericToEncoding defaultOptions
@@ -472,7 +351,7 @@ instance FromJSON Command
472instance NFData Command
351instance NFData Command
473
352
474-- | Batch of commands that can be sent together to the JavaScript context
353-- | Batch of commands that can be sent together to the JavaScript context
475−data Batch = Batch [Either AsyncCommand Command] Bool Int
354+data Batch = Batch [Either AsyncCommand Command] Bool
476 deriving (Show, Generic)
355 deriving (Show, Generic)
477
356
478instance ToJSON Batch where
357instance ToJSON Batch where
@@ -503,18 +382,9 @@ instance ToJSON Result where
503
382
504instance FromJSON Result
383instance FromJSON Result
505
384
506−data BatchResults = Success [JSValueReceived] [Result]
385+data Results = Success [Result]
507− | Failure [JSValueReceived] [Result] JSValueReceived String
386+ | Failure [Result] JSValueReceived
508− deriving (Show, Generic)
509−
510−instance ToJSON BatchResults where
511− toEncoding = genericToEncoding defaultOptions
512−
513−instance FromJSON BatchResults
514−
515−data Results = BatchResults Int BatchResults
516− | Duplicate Int Int
517− | Callback Int BatchResults JSValueReceived JSValueReceived JSValueReceived [JSValueReceived]
387+ | Callback JSValueReceived JSValueReceived [JSValueReceived]
518 | ProtocolError Text
388 | ProtocolError Text
519 deriving (Show, Generic)
389 deriving (Show, Generic)
520
390
jsaddle/src/Language/Javascript/JSaddle/Value.hs+3−5
@@ -618,17 +618,15 @@ foreign import javascript unsafe "$r = ($1 === undefined)?0:\
618 (typeof $1===\"string\")?4:\
618 (typeof $1===\"string\")?4:\
619 (typeof $1===\"object\")?5:-1;" jsrefGetType :: JSVal -> Int
619 (typeof $1===\"object\")?5:-1;" jsrefGetType :: JSVal -> Int
620#else
620#else
621−deRefVal value = do
621+deRefVal value = toJSVal value >>= N.deRefVal >>= \result -> return $
622− v <- toJSVal value
623− result <- N.deRefVal v
624− return $ case result of
622+ case result of
625 DeRefValResult 0 _ -> ValNull
623 DeRefValResult 0 _ -> ValNull
626 DeRefValResult 1 _ -> ValUndefined
624 DeRefValResult 1 _ -> ValUndefined
627 DeRefValResult 2 _ -> ValBool False
625 DeRefValResult 2 _ -> ValBool False
628 DeRefValResult 3 _ -> ValBool True
626 DeRefValResult 3 _ -> ValBool True
629 DeRefValResult (-1) s -> ValNumber (read (T.unpack s))
627 DeRefValResult (-1) s -> ValNumber (read (T.unpack s))
630 DeRefValResult (-2) s -> ValString s
628 DeRefValResult (-2) s -> ValString s
631− DeRefValResult (-3) _ -> ValObject (Object v)
629+ DeRefValResult ref _ -> ValObject (Object (JSVal ref))
632 _ -> error "Unexpected result dereferencing JSaddle value"
630 _ -> error "Unexpected result dereferencing JSaddle value"
633#endif
631#endif
634
632
nix/hix.nixdeleted−8
@@ -1,8 +0,0 @@
1−{pkgs, ...}:
2−{ modules = [({pkgs, ...}: {
3− packages.webkit2gtk3-javascriptcore.components.library.doHaddock = false;
4− })];
5− shell.buildInputs = [ pkgs.nodejs ];
6− shell.tools.cabal = {};
7− shell.crossPlatforms = p: [ p.ghcjs ];
8−}
travis/build.sh+12−2
@@ -6,8 +6,18 @@ GHCVER=`ghc --numeric-version`
6
6
7JSADDLE_WARP_VERSION=`head -n2 jsaddle-warp/jsaddle-warp.cabal | tail -n1 | sed 's/[^0-9.]//g'`
7JSADDLE_WARP_VERSION=`head -n2 jsaddle-warp/jsaddle-warp.cabal | tail -n1 | sed 's/[^0-9.]//g'`
8
8
9+if [[ -d .cabal && -d .ghc ]]; then
10+ cp -a .cabal .ghc /root
11+fi
12+
9cabal update
13cabal update
10−cabal new-build 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
14+cabal new-build --constraint='Cabal>=1.24.2.0' 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
11cd jsaddle-warp
15cd jsaddle-warp
12−GHC_PACKAGE_PATH=/opt/ghc/$GHCVER/lib/ghc-$GHCVER/package.conf.d:~/.cabal/store/ghc-$GHCVER/package.db:../dist-newstyle/packagedb/ghc-$GHCVER ../dist-newstyle/build/x86_64-linux/ghc-$GHCVER/jsaddle-warp-$JSADDLE_WARP_VERSION/t/test-tool/build/test-tool/test-tool ../jsaddle
16+GHC_PACKAGE_PATH=/opt/ghc/$GHCVER/lib/ghc-$GHCVER/package.conf.d:~/.cabal/store/ghc-$GHCVER/package.db:./dist-newstyle/packagedb/ghc-$GHCVER jsaddle_datadir=`pwd` ../dist-newstyle/build/jsaddle-warp-$JSADDLE_WARP_VERSION/build/test-tool/test-tool ../jsaddle-warp
17+
18+# update the cache
19+rm -rf .cabal
20+cp -a /root/.cabal ./
21+rm -rf .ghc
22+cp -a /root/.ghc ./
13
23