on f446802 help sign in
55 files changed+626−2406
since splitsnapshots
unifiedsplit
.travis.yml+22−40 README.md+14−34 cabal.project+35−4 jsaddle-clib/LICENSE−21 jsaddle-clib/Setup.lhs−6 jsaddle-clib/cbits/include/jsaddle.h−24 jsaddle-clib/jsaddle-clib.cabal−40 jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hs−157 jsaddle-clib/src-ghcjs/Language/Javascript/JSaddle/CLib.hs−7 jsaddle-clib/src/Language/Javascript/JSaddle/CLib/Internal.hsc−67 jsaddle-warp/jsaddle-warp.cabal+12−33 jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs+12−13 jsaddle-warp/src/Language/Javascript/JSaddle/Warp.hs+4−3 jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+40−287 jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets/Compat.hs−29 jsaddle-warp/tests/DocTest.hs+68−84 jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal+11−14 jsaddle-webkit2gtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+46−50 jsaddle-webkitgtk/jsaddle-webkitgtk.cabal+5−7 jsaddle-webkitgtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+4−9 jsaddle-wkwebview/cbits-cocoa/AppDelegate.m+7−71 jsaddle-wkwebview/cbits-uikit/AppDelegate.m+2−152 jsaddle-wkwebview/cbits-uikit/ViewController.m+2−2 jsaddle-wkwebview/cbits/WKWebView.m+10−79 jsaddle-wkwebview/default.nix−14 jsaddle-wkwebview/jsaddle-wkwebview.cabal+15−32 jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+5−205 jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+16−109 jsaddle/jsaddle.cabal+15−32 jsaddle/src-ghc/Data/JSString/Internal/Type.hs+1−2 jsaddle/src-ghc/GHCJS/Buffer.hs+2−2 jsaddle/src-ghc/GHCJS/Concurrent.hs−22 jsaddle/src-ghc/GHCJS/Foreign/Internal.hs+10−12 jsaddle/src-ghc/GHCJS/Marshal/Internal.hs+2−21 jsaddle/src-ghc/GHCJS/Prim/Internal.hs+3−5 jsaddle/src-ghc/GHCJS/Types.hs+3−5 jsaddle/src-ghc/JavaScript/Array.hs+2−2 jsaddle/src-ghc/JavaScript/Array/Internal.hs+2 jsaddle/src/Language/Javascript/JSaddle.hs+5−5 jsaddle/src/Language/Javascript/JSaddle/Debug.hs−48 jsaddle/src/Language/Javascript/JSaddle/Evaluate.hs+1−1 jsaddle/src/Language/Javascript/JSaddle/Monad.hs+26−3 jsaddle/src/Language/Javascript/JSaddle/Native/Internal.hs+31−55 jsaddle/src/Language/Javascript/JSaddle/Null.hs−56 jsaddle/src/Language/Javascript/JSaddle/Object.hs+19−51 jsaddle/src/Language/Javascript/JSaddle/Run.hs+71−166 jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+56−132 jsaddle/src/Language/Javascript/JSaddle/Types.hs+26−173 jsaddle/src/Language/Javascript/JSaddle/Value.hs+3−5 nix/hix.nix−8 travis/build.sh+14−3 travis/trusty.Dockerfile+1−1 travis/vivid.Dockerfile+1−1 travis/wily.Dockerfile+1−1 travis/xenial.Dockerfile+1−1
.travis.yml+22−40
@@ -1,47 +1,29 @@
1−language: nix
2−nix: 2.2.2
1+sudo: required
2+services:
3+ - docker
34
4−jobs:
5− include:
6− - stage: Build
7− env: OS=linux GHCVER=ghc865
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
5+cache:
6+ directories:
7+ - .cabal
8+ - .ghc
249
25−install:
26−- sudo mkdir -p /etc/nix
27−- echo "substituters = https://cache.nixos.org https://hydra.iohk.io" | sudo tee -a /etc/nix/nix.conf > /dev/null
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
10+env:
11+# - CABALVER=1.24 GHCVER=7.10.3 OSVER=xenial
12+ - CABALVER=1.24 GHCVER=8.0.1 OSVER=xenial
13+ - CABALVER=1.24 GHCVER=7.10.3 OSVER=trusty
14+ - CABALVER=1.24 GHCVER=8.0.1 OSVER=trusty
2915
30−script:
31−- nix-build --argstr haskellCompiler $GHCVER -A hsPkgs.jsaddle.components.library
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
16+before_install:
17+ - docker build -t ghc -f travis/$OSVER.Dockerfile travis
4318
19+script:
20+ - docker run -v `pwd`:/build ghc
21+ sh -x -c "
22+ apt-get install -y cabal-install-$CABALVER ghc-$GHCVER &&
23+ 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 &&
24+ ./travis/build.sh"
4425notifications:
4526 irc:
4627 channels: "irc.freenode.net#ghcjs"
47− email: true
28+ skip_join: true
29+ email: true
README.md+14−34
@@ -13,8 +13,7 @@ import Control.Monad.IO.Class (MonadIO(..))
1313import Control.Concurrent.MVar (takeMVar, putMVar, newEmptyMVar)
1414import Control.Lens ((^.))
1515import Language.Javascript.JSaddle
16− (jsg, js, js1, jss, fun, valToNumber, syncPoint)
17−import Language.Javascript.JSaddle.Warp (run)
16+ (run, jsg, js, js1, jss, fun, valToNumber, syncPoint)
1817
1918main = run 3709 $ do
2019 doc <- jsg "document"
@@ -80,7 +79,7 @@ main = run 3708 $ do
8079 appendChild newParagraph text
8180 appendChild body (Just newParagraph)
8281 return ()
83−
82+
8483 -- Make an exit button
8584 exitMVar <- liftIO newEmptyMVar
8685 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
103102
104103## How does it work on GHC
105104
106−There are a number of different JSaddle runners to choose from
107−
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.
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
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.
119107
120−Haskell callbacks from JavaScript are supported by sending JSON back from the command interpreter. These can be synchronous (blocking the
121−JavaScript thread until the Haskell callback completes) or asynchronous.
108+### Why use WebSockets?
122109
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.
127−The general advice for people migrating from WebKit1 to WebKit2 seems to be to use JavaScript.
128−
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.
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).
112+The general advice for people migrating from WebKit1 to WebKit2 seems to be to use JavaScript. There is an interface in WebKit2
113+to send JavaScript code from the host application, but it does not seem to provide any advantages over the WebSockets approach.
131114
132115### JSVal
133116
@@ -137,8 +120,8 @@ The first five keys `[0..4]` are
137120these values. Both the Haskell code and `jsaddle.js` can allocate new `JSVal` keys. The Haskell code allocates negative
138121keys and `jsaddle.js` allocates positive keys to avoid clashing.
139122
140−The haskell code will not know the value 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
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
124+that are sort of "hey call this function and and put the result in the map with this key"). This allows for the
142125lazy execution of the JSaddle code.
143126
144127A 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
146129
147130### JSStrings
148131
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`.
150133
151134### Lazy Execution
152135
@@ -178,7 +161,7 @@ force all the pending asynchronous commands to be executed.
178161It 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.
179162
180163For 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`.
182165
183166For instance here is `getElementById` from the `ghcjs-dom-jsffi` (used by `ghcjs-dom` when compiled with GHCJS)
184167
@@ -213,10 +196,7 @@ JSaddle does not support exceptions well. When compiled with GHCJS an exception
213196at the point the exception is thrown.
214197
215198When 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.
217200When it reaches the synchronous command a haskell type JSException will be thrown (this may not be the thread that initiated the
218201command that caused the exception). You can use `syncPoint` and `syncAfter` to force the exception to be thrown.
219202There 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+
13packages:
24 jsaddle/
35 jsaddle-warp/
46 jsaddle-wkwebview/
7+-- jsaddle-webkitgtk/
58 jsaddle-webkit2gtk/
6− jsaddle-clib/
7−
8−constraints: haskell-gi-overloading == 0.0
99
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
jsaddle-clib/LICENSEdeleted−21
@@ -1,21 +0,0 @@
1−Copyright (c) 2010-2012 Hamish Mackenzie, Victor Nazarov, Luite Stegeman
2−
3−Permission is hereby granted, free of charge, to any person obtaining
4−a copy of this software and associated documentation files (the
5−"Software"), to deal in the Software without restriction, including
6−without limitation the rights to use, copy, modify, merge, publish,
7−distribute, sublicense, and/or sell copies of the Software, and to
8−permit persons to whom the Software is furnished to do so, subject to
9−the following conditions:
10−
11−The above copyright notice and this permission notice shall be
12−included in all copies or substantial portions of the Software.
13−
14−THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15−EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16−MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17−NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18−LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19−OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20−WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21−
jsaddle-clib/Setup.lhsdeleted−6
@@ -1,6 +0,0 @@
1−#!/usr/bin/runhaskell
2−> module Main where
3−> import Distribution.Simple
4−> main :: IO ()
5−> main = defaultMain
6−
jsaddle-clib/cbits/include/jsaddle.hdeleted−24
@@ -1,24 +0,0 @@
1−#ifndef JSADDLE_H
2−#define JSADDLE_H
3−
4−typedef struct native_callbacks {
5− void (* jsaddleStart) ();
6− void (* jsaddleResult) (char *);
7− char * (* jsaddleSyncResult) (char *);
8− char * jsaddleJsData;
9− char * jsaddleHtmlData;
10−} native_callbacks;
11−
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
jsaddle-clib/jsaddle-clib.cabaldeleted−40
@@ -1,40 +0,0 @@
1−name: jsaddle-clib
2−version: 0.9.8.0
3−cabal-version: >=1.10
4−build-type: Simple
5−license: MIT
6−license-file: LICENSE
7−maintainer: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
8−synopsis: Interface for JavaScript that works with GHCJS and GHC
9−description:
10− This package provides an EDSL for calling JavaScript that
11− can be used both from GHCJS and GHC.
12−category: Web, Javascript
13−author: Hamish Mackenzie
14−
15−source-repository head
16− type: git
17− location: https://github.com/ghcjs/jsaddle
18−
19−library
20− exposed-modules:
21− Language.Javascript.JSaddle.CLib
22− Language.Javascript.JSaddle.CLib.Internal
23− build-depends:
24− aeson >=0.8.0.2 && <1.6,
25− base <5,
26− base-compat >=0.9.0 && <0.12,
27− bytestring >=0.10.6.0 && <0.11,
28− jsaddle >= 0.9.0.0 && <0.10,
29− data-default,
30− text
31− default-language: Haskell2010
32− hs-source-dirs: src
33− Include-dirs: cbits/include
34− Includes: jsaddle.h
35− Install-includes: jsaddle.h
36− ghc-options: -ferror-spans -Wall
37− if impl(ghcjs)
38− hs-source-dirs: src-ghcjs
39− else
40− hs-source-dirs: src-ghc
jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hsdeleted−157
@@ -1,157 +0,0 @@
1−{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
2−module Language.Javascript.JSaddle.CLib
3− ( jsaddleInit
4− , NativeCallbacks (..)
5− , AppCallbacks (..)
6− , AppConfig (..)
7− , pokeAppConfig
8− , appConfigToAppCallbacks
9− ) where
10−
11−import Prelude ()
12−import Prelude.Compat
13−
14−import Control.Monad (void)
15−import Control.Concurrent (forkIO)
16−
17−import Data.Aeson (encode, decode)
18−import Data.ByteString (useAsCString, packCString)
19−import Data.ByteString.Char8 (unpack)
20−import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
21−import Data.Default (def, Default)
22−import Data.Text (Text)
23−import qualified Data.Text.Encoding as T
24−
25−import Foreign.C.String (CString, newCString)
26−import Foreign.Ptr (FunPtr, Ptr)
27−import Foreign.Storable (poke)
28−import Foreign.Marshal.Utils (new)
29−
30−import Language.Javascript.JSaddle (JSM)
31−import Language.Javascript.JSaddle.Run (runJavaScript)
32−import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
33−
34−import Language.Javascript.JSaddle.CLib.Internal
35−
36−foreign import ccall safe "dynamic"
37− mkCallback :: FunPtr (CString -> IO ()) -> CString -> IO ()
38−
39−foreign import ccall safe "wrapper"
40− wrapStartCallback :: IO () -> IO (FunPtr (IO ()))
41−
42−foreign import ccall safe "wrapper"
43− wrapMessageCallback :: (CString -> IO ()) -> IO (FunPtr (CString -> IO ()))
44−
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−
51−jsaddleInit :: JSM () -> FunPtr (CString -> IO ()) -> IO (Ptr NativeCallbacks)
52−jsaddleInit jsm evaluateJavascriptAsyncPtr = do
53− let evaluateJavascriptAsync = mkCallback evaluateJavascriptAsyncPtr
54− (processResult, processSyncResult, start) <- runJavaScript (\batch ->
55− useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");")
56− evaluateJavascriptAsync) jsm
57− jsaddleStartPtr <- wrapStartCallback $ void $ forkIO start
58− jsaddleResultPtr <- wrapMessageCallback $ \s -> do
59− result <- decode . fromStrict <$> packCString s
60− case result of
61− Nothing -> error $ "jsaddle message decode failed: " <> show result
62− 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
69− jsaddleHtmlPtr <- newCString $ unpack $ toStrict indexHtml
70− new NativeCallbacks
71− { _nativeCallbacks_jsaddleStart = jsaddleStartPtr
72− , _nativeCallbacks_jsaddleResult = jsaddleResultPtr
73− , _nativeCallbacks_jsaddleSyncResult = jsaddleSyncResultPtr
74− , _nativeCallbacks_jsaddleJsData = jsaddleJsPtr
75− , _nativeCallbacks_jsaddleHtmlData = jsaddleHtmlPtr
76− }
77−
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−
137−jsaddleJs :: ByteString
138−jsaddleJs = ghcjsHelpers <> "\
139− \runJSaddleBatch = (function() {\n\
140− \ " <> initState <> "\n\
141− \ return function(batch) {\n\
142− \ " <> runBatch (\a -> "jsaddle.postMessage(JSON.stringify(" <> a <> "));")
143− (Just (\a -> "JSON.parse(jsaddle.syncMessage(JSON.stringify(" <> a <> ")))")) <> "\
144− \ };\n\
145− \})();\n\
146− \jsaddle.postReady();\n"
147−
148−indexHtml :: ByteString
149−indexHtml =
150− "<!DOCTYPE html>\n\
151− \<html>\n\
152− \<head>\n\
153− \<title>JSaddle</title>\n\
154− \</head>\n\
155− \<body>\n\
156− \</body>\n\
157− \</html>"
jsaddle-clib/src-ghcjs/Language/Javascript/JSaddle/CLib.hsdeleted−7
@@ -1,7 +0,0 @@
1−module Language.Javascript.JSaddle.CLib
2− ( run
3− ) where
4−
5−run :: IO () -> IO ()
6−run = id
7−{-# INLINE run #-}
jsaddle-clib/src/Language/Javascript/JSaddle/CLib/Internal.hscdeleted−67
@@ -1,67 +0,0 @@
1−{-# LANGUAGE ForeignFunctionInterface #-}
2−module Language.Javascript.JSaddle.CLib.Internal where
3−
4−import Foreign
5−import Foreign.C
6−
7−#include "jsaddle.h"
8−
9−data NativeCallbacks = NativeCallbacks
10− { _nativeCallbacks_jsaddleStart :: !(FunPtr (IO ())) -- void (jsaddleStart *)()
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
14− , _nativeCallbacks_jsaddleHtmlData :: !CString -- char * jsaddleHtmlData
15− }
16−
17−instance Storable NativeCallbacks where
18− sizeOf _ = #{size native_callbacks}
19− alignment _ = #{alignment native_callbacks}
20− poke p nc = do
21− #{poke native_callbacks, jsaddleStart} p $ _nativeCallbacks_jsaddleStart nc
22− #{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
25− #{poke native_callbacks, jsaddleHtmlData} p $ _nativeCallbacks_jsaddleHtmlData nc
26− peek p = NativeCallbacks
27− <$> #{peek native_callbacks, jsaddleStart} p
28− <*> #{peek native_callbacks, jsaddleResult} p
29− <*> #{peek native_callbacks, jsaddleSyncResult} p
30− <*> #{peek native_callbacks, jsaddleJsData} p
31− <*> #{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/jsaddle-warp.cabal+12−33
@@ -1,5 +1,5 @@
11name: jsaddle-warp
2−version: 0.9.8.0
2+version: 0.8.2.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -10,7 +10,7 @@ description:
1010 This package provides an EDSL for calling JavaScript that
1111 can be used both from GHCJS and GHC. When using GHC
1212 the application is run using Warp and WebSockets to
13− drive a small JavaScript helper.
13+ drive a small JavaScipt helper.
1414category: Web, Javascript
1515author: Hamish Mackenzie
1616
@@ -24,23 +24,19 @@ library
2424 if !impl(ghcjs -any)
2525 exposed-modules:
2626 Language.Javascript.JSaddle.WebSockets
27− other-modules:
28− Language.Javascript.JSaddle.WebSockets.Compat
2927 build-depends:
30− aeson >=0.8.0.2 && <1.6,
31− bytestring >=0.10.6.0 && <0.11,
32− containers >=0.5.6.2 && <0.7,
33− foreign-store >=0.2 && <0.3,
34− http-types >=0.8.6 && <0.13,
35− jsaddle >=0.9.4.0 && <0.10,
36− stm >=2.4.4 && <2.6,
28+ aeson >=0.8.0.2 && <1.1,
29+ containers >=0.5.6.2 && <0.6,
30+ http-types >=0.8.6 && <0.10,
31+ jsaddle >=0.8.0.0 && <0.9,
32+ stm >=2.4.4 && <2.5,
3733 text >=1.2.1.3 && <1.3,
38− time >=1.5.0.1 && <1.11,
34+ time >=1.5.0.1 && <1.7,
3935 transformers >=0.4.2.0 && <0.6,
4036 wai >=3.0.3.0 && <3.3,
4137 wai-websockets >=3.0.0.6 && <3.1,
42− warp >=3.1.2 && <3.4,
43− websockets >=0.9.5.0 && <0.13
38+ warp >=3.1.2 && <3.3,
39+ websockets >=0.9.5.0 && <0.10
4440 exposed-modules:
4541 Language.Javascript.JSaddle.Warp
4642 build-depends:
@@ -55,34 +51,17 @@ test-suite test-tool
5551
5652 build-depends:
5753 QuickCheck -any,
58− aeson -any,
5954 bytestring -any,
60− containers -any,
6155 deepseq -any,
62− doctest >=0.10.1 && <0.19,
63− exceptions -any,
56+ doctest >=0.10.1 && <0.12,
6457 filepath >=1.4.0.0 && <1.5,
65− foreign-store -any,
6658 ghc-prim -any,
67− http-types -any,
6859 jsaddle -any,
6960 jsaddle-warp -any,
7061 lens -any,
71− network -any,
7262 ref-tf -any,
7363 primitive -any,
74− process >=1.2.3.0 && <1.7,
75− random -any,
76− stm -any,
77− text -any,
78− time -any,
79− transformers -any,
80− unliftio-core -any,
81− wai -any,
82− wai-websockets -any,
83− warp -any,
84− webdriver >=0.8.4 && <0.10,
85− websockets -any
64+ process >=1.2.3.0 && <1.5
8665 type: exitcode-stdio-1.0
8766 main-is: DocTest.hs
8867 build-depends:
jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs+12−13
@@ -17,22 +17,21 @@ module Language.Javascript.JSaddle.Test (
1717 , listWindowProperties
1818) where
1919
20−import Prelude hiding((!!), catch)
2120import Control.Applicative
22−import Control.Concurrent (threadDelay, forkIO, ThreadId)
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(..))
21+import Prelude hiding((!!), catch)
2822import 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 ((^.))
2928import 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)
3033import Data.Text (Text)
31−import qualified Data.Text as T
32−import Language.Javascript.JSaddle
3334import Language.Javascript.JSaddle.Warp (run)
34−import System.IO.Unsafe (unsafePerformIO)
35−import System.Timeout (timeout)
3635
3736context :: MVar JSContextRef
3837context = unsafePerformIO newEmptyMVar
@@ -53,12 +52,12 @@ startServer =
5352
5453-- >>> testJSaddle $ ((global ^. js "console" . js "log") # ["Hello"])
5554testJSaddle :: ToJSVal val => JSM val -> IO ()
56−testJSaddle f = timeout 10000000 (do
55+testJSaddle f = do
5756 startServer
5857 c <- takeMVar context
5958 runReaderT (unJSM $ (f >>= valToText >>= liftIO . putStrLn . T.unpack)
6059 `catch` \ (JSException e) -> valToText e >>= liftIO . putStrLn . T.unpack) c
61− putMVar context c) >>= maybe (putStrLn "testJSaddle timed out") return
60+ putMVar context c
6261
6362debugLog :: String -> IO ()
6463debugLog _ = return ()
jsaddle-warp/src/Language/Javascript/JSaddle/Warp.hs+4−3
@@ -17,7 +17,8 @@ module Language.Javascript.JSaddle.Warp (
1717 -- * Running JSM over WebSockets
1818 run
1919#ifndef ghcjs_HOST_OS
20− , module Language.Javascript.JSaddle.WebSockets
20+ , jsaddleOr
21+ , jsaddleApp
2122#endif
2223) where
2324
@@ -28,7 +29,7 @@ import Network.WebSockets (defaultConnectionOptions)
2829
2930import Language.Javascript.JSaddle.Types (JSM)
3031import Language.Javascript.JSaddle.Run (syncPoint)
31−import Language.Javascript.JSaddle.WebSockets
32+import Language.Javascript.JSaddle.WebSockets (jsaddleOr, jsaddleApp)
3233#endif
3334
3435-- | Run the given 'JSM' action as the main entry point. Either directly
@@ -39,6 +40,6 @@ run _port = id
3940#else
4041run :: Int -> JSM () -> IO ()
4142run port f =
42− runSettings (setPort port (setTimeout 3600 defaultSettings)) =<<
43+ runSettings (setPort port (setTimeout 3600 defaultSettings)) $
4344 jsaddleOr defaultConnectionOptions (f >> syncPoint) jsaddleApp
4445#endif
jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+40−287
@@ -1,7 +1,5 @@
11{-# LANGUAGE LambdaCase #-}
22{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE RecursiveDo #-}
53-----------------------------------------------------------------------------
64--
75-- Module : Language.Javascript.JSaddle.WebSockets
@@ -18,302 +16,57 @@ module Language.Javascript.JSaddle.WebSockets (
1816 -- * Running JSM over WebSockets
1917 jsaddleOr
2018 , jsaddleApp
21− , jsaddleWithAppOr
22− , jsaddleAppWithJs
23− , jsaddleAppWithJsOr
24− , jsaddleAppPartial
25− , jsaddleJs
26− , jsaddleJs'
27− , debug
28− , debugOr
29− , debugWrapper
3019) where
3120
32−import Control.Monad (when, void, forever)
33−import Control.Concurrent (killThread, forkIO, threadDelay)
34−import Control.Exception (handle, AsyncException, throwIO, fromException, finally)
21+import Control.Monad (forever)
22+import Control.Concurrent (forkIO)
3523
3624import Data.Monoid ((<>))
3725import Data.Aeson (encode, decode)
3826
39−import Network.Wai
40− (Middleware, lazyRequestBody, Application, Request, Response,
41− ResponseReceived)
27+import Network.Wai (Application)
4228import Network.WebSockets
43− (defaultConnectionOptions, ConnectionOptions(..), sendTextData,
44− receiveDataMessage, acceptRequest, ServerApp, sendPing)
29+ (ConnectionOptions(..), sendTextData, receiveDataMessage,
30+ acceptRequest, ServerApp)
31+import qualified Network.WebSockets as WS (DataMessage(..))
4532import Network.Wai.Handler.WebSockets (websocketsOr)
46−import Network.HTTP.Types (Status(..))
4733
48−import Language.Javascript.JSaddle.Types (JSM(..), JSContextRef(..))
34+import Language.Javascript.JSaddle.Types (JSM(..))
4935import qualified Network.Wai as W
50− (responseLBS, requestMethod, pathInfo, modifyResponse, responseStatus)
51−import qualified Data.Text as T (pack)
36+ (responseLBS, requestMethod, Application, pathInfo)
37+import Data.Text (Text)
5238import qualified Network.HTTP.Types as H
53− (status403, status200)
54−import Language.Javascript.JSaddle.Run (syncPoint, runJavaScript)
55−import Language.Javascript.JSaddle.Run.Files (indexHtml, runBatch, ghcjsHelpers, initState)
56−import Language.Javascript.JSaddle.Debug
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
78−jsaddleOr opts entryPoint otherApp = do
79− syncHandlers <- newIORef M.empty
80− asyncHandlers <- newIORef M.empty
81− let wsApp :: ServerApp
82− wsApp pending_conn = do
83− 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
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 $
100− receiveDataMessage conn >>= \msg -> case getTextMessageByteString msg of
101− Just t ->
102− case decode t of
103− Nothing -> error $ "jsaddle Results decode failed : " <> show t
104− Just r -> processResult r
105− _ -> error "jsaddle WebSocket unexpected binary data"
39+ (status403, status200, status405)
40+import Language.Javascript.JSaddle.Run (runJavaScript)
41+import Language.Javascript.JSaddle.Run.Files (indexHtml, jsaddleJs)
42+
43+jsaddleOr :: ConnectionOptions -> JSM () -> Application -> Application
44+jsaddleOr opts entryPoint = websocketsOr opts wsApp
45+ where
46+ wsApp :: ServerApp
47+ wsApp pending_conn = do
48+ conn <- acceptRequest pending_conn
49+ (processResult, start) <- runJavaScript (sendTextData conn . encode) entryPoint
50+ _ <- forkIO . forever $
51+ receiveDataMessage conn >>= \case
52+ (WS.Text t) ->
53+ case decode t of
54+ Nothing -> error $ "jsaddle WebSocket decode failed : " <> show t
55+ Just r -> processResult r
10656 _ -> error "jsaddle WebSocket unexpected binary data"
107−
108− -- Based on Network.WebSocket.forkPingThread
109− waitTillClosed conn = ignore `handle` go 1
110− where
111− go :: Int -> IO ()
112− go i = do
113− threadDelay (10 * 1000 * 1000)
114− sendPing conn (T.pack $ show i)
115− go (i + 1)
116−
117− ignore e = case fromException e of
118− Just async -> throwIO (async :: AsyncException)
119− 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−
57+ start
14058
14159jsaddleApp :: Application
142−jsaddleApp = jsaddleAppWithJs $ jsaddleJs False
143−
144−jsaddleAppWithJs :: ByteString -> Application
145−jsaddleAppWithJs js req sendResponse =
146− jsaddleAppWithJsOr js
147− (\_ _ -> sendResponse $ W.responseLBS H.status403 [("Content-Type", "text/plain")] "Forbidden")
148− req sendResponse
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
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
170− _ -> Nothing
171−
172−jsaddleJs :: Bool -> ByteString
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
60+jsaddleApp req = jsaddleAppPieces (W.pathInfo req) req
61+
62+jsaddleAppPieces :: [Text] -> W.Application
63+jsaddleAppPieces _ req sendResponse
64+ | W.requestMethod req `notElem` ["GET", "HEAD"] = sendResponse $ W.responseLBS
65+ H.status405
66+ [("Content-Type", "text/plain")]
67+ "Only GET or HEAD is supported"
68+jsaddleAppPieces [] _req sendResponse = sendResponse $ W.responseLBS H.status200 [("Content-Type", "text/html")] indexHtml
69+jsaddleAppPieces ["jsaddle.js"] _req sendResponse = sendResponse $ W.responseLBS H.status200 [("Content-Type", "application/javascript")] jsaddleJs
70+jsaddleAppPieces _rawPieces _req sendResponse = sendResponse $ W.responseLBS H.status403
71+ [ ("Content-Type", "text/plain")
72+ ] "Forbidden"
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+68−84
@@ -11,17 +11,15 @@ import System.IO (stderr, BufferMode(..), stdout, hSetBuffering)
1111import System.FilePath ((</>))
1212import System.Exit (exitFailure, exitWith, ExitCode(..))
1313import System.Process (readProcess, system)
14−import Control.Concurrent
15− (takeMVar, newEmptyMVar, forkIO, threadDelay, putMVar)
14+import Control.Concurrent (forkIO)
1615import Control.Monad (void)
17−import Control.Monad.IO.Class (MonadIO(..))
1816import Data.Monoid ((<>))
1917import System.Environment (getArgs)
20−import Test.WebDriver (runSession, defaultConfig, openPage, closeSession)
18+import Language.Javascript.JSaddle.Run.Files (jsaddleJs)
19+import qualified Data.ByteString.Lazy.Char8 as BS (unpack)
2120
2221main :: IO ()
2322main = do
24− putStrLn "Testing JSaddle"
2523 jsaddlePath <- getArgs >>= \case
2624 [arg] -> return arg
2725 _ -> do
@@ -29,84 +27,70 @@ main = do
2927 exitFailure
3028 hSetBuffering stdout LineBuffering
3129 hSetBuffering stderr LineBuffering
32− putStrLn "Checking for phantomjs"
33− node <- system "phantomjs --version" >>= \case
34− ExitSuccess -> return ()
30+ node <- system "nodejs --version" >>= \case
31+ ExitSuccess -> return "nodejs"
32+ _ -> return "node"
33+ system (node <> " --version") >>= \case
34+ ExitSuccess -> putStrLn "Node.js found"
3535 e -> do
36− putStrLn "phantomjs not found"
36+ putStrLn "Node.js not found"
3737 exitWith e
38− putStrLn "Starting phantomjs"
39− forkIO . void $ readProcess "phantomjs" ["--webdriver=4444"] "" >>= putStr
40− threadDelay 5000000
41− putStrLn "Running Tests"
42− done <- newEmptyMVar
43− forkIO $ do
44− liftIO $ doctest [
45− "-hide-all-packages",
46− "-package=base-" ++ VERSION_base,
47− "-package=lens-" ++ VERSION_lens,
48− "-package=text-" ++ VERSION_text,
49− "-package=bytestring-" ++ VERSION_bytestring,
50− "-package=transformers-" ++ VERSION_transformers,
51− "-package=websockets-" ++ VERSION_websockets,
52− "-package=primitive-" ++ VERSION_primitive,
53− "-package=aeson-" ++ VERSION_aeson,
54− "-package=websockets-" ++ VERSION_websockets,
55− "-package=wai-" ++ VERSION_wai,
56− "-package=wai-websockets-" ++ VERSION_wai_websockets,
57− "-package=warp-" ++ VERSION_warp,
58− "-package=http-types-" ++ VERSION_http_types,
59− "-package=stm-" ++ VERSION_stm,
60− "-package=time-" ++ VERSION_time,
61− "-package=containers-" ++ VERSION_containers,
62− "-package=process-" ++ VERSION_process,
63− "-package=filepath-" ++ VERSION_filepath,
64− "-package=ref-tf-" ++ VERSION_ref_tf,
65− "-package=deepseq-" ++ VERSION_deepseq,
66− "-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",
72− "-i" <> "src-ghc",
73− "src/Language/Javascript/JSaddle/Test.hs",
74− "-i" <> jsaddlePath,
75− jsaddlePath </> "src-ghc/Data/JSString/Text.hs",
76− jsaddlePath </> "src-ghc/Data/JSString/Internal/Type.hs",
77− jsaddlePath </> "src-ghc/GHCJS/Foreign.hs",
78− jsaddlePath </> "src-ghc/GHCJS/Foreign/Internal.hs",
79− jsaddlePath </> "src-ghc/GHCJS/Internal/Types.hs",
80− jsaddlePath </> "src-ghc/GHCJS/Marshal.hs",
81− jsaddlePath </> "src-ghc/GHCJS/Marshal/Pure.hs",
82− jsaddlePath </> "src-ghc/GHCJS/Marshal/Internal.hs",
83− jsaddlePath </> "src-ghc/GHCJS/Prim.hs",
84− jsaddlePath </> "src-ghc/GHCJS/Prim/Internal.hs",
85− jsaddlePath </> "src-ghc/GHCJS/Types.hs",
86− jsaddlePath </> "src-ghc/JavaScript/Array.hs",
87− jsaddlePath </> "src-ghc/JavaScript/Array/Internal.hs",
88− jsaddlePath </> "src-ghc/JavaScript/Object/Internal.hs",
89− jsaddlePath </> "src/Language/Javascript/JSaddle.hs",
90− jsaddlePath </> "src/Language/Javascript/JSaddle/Arguments.hs",
91− jsaddlePath </> "src/Language/Javascript/JSaddle/Classes.hs",
92− jsaddlePath </> "src/Language/Javascript/JSaddle/Classes/Internal.hs",
93− jsaddlePath </> "src/Language/Javascript/JSaddle/Debug.hs",
94− jsaddlePath </> "src/Language/Javascript/JSaddle/Evaluate.hs",
95− jsaddlePath </> "src/Language/Javascript/JSaddle/Exception.hs",
96− jsaddlePath </> "src/Language/Javascript/JSaddle/Monad.hs",
97− jsaddlePath </> "src/Language/Javascript/JSaddle/Marshal/String.hs",
98− jsaddlePath </> "src/Language/Javascript/JSaddle/Native.hs",
99− jsaddlePath </> "src/Language/Javascript/JSaddle/Native/Internal.hs",
100− jsaddlePath </> "src/Language/Javascript/JSaddle/Object.hs",
101− jsaddlePath </> "src/Language/Javascript/JSaddle/Properties.hs",
102− jsaddlePath </> "src/Language/Javascript/JSaddle/Run.hs",
103− jsaddlePath </> "src/Language/Javascript/JSaddle/Run/Files.hs",
104− jsaddlePath </> "src/Language/Javascript/JSaddle/String.hs",
105− jsaddlePath </> "src/Language/Javascript/JSaddle/Types.hs",
106− 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
38+ forkIO . void $ readProcess node ["--harmony"] (BS.unpack jsaddleJs) >>= putStr
39+ doctest [
40+ "-hide-all-packages",
41+ "-package=base-" ++ VERSION_base,
42+ "-package=lens-" ++ VERSION_lens,
43+ "-package=text-" ++ VERSION_text,
44+ "-package=bytestring-" ++ VERSION_bytestring,
45+ "-package=transformers-" ++ VERSION_transformers,
46+ "-package=websockets-" ++ VERSION_websockets,
47+ "-package=primitive-" ++ VERSION_primitive,
48+ "-package=aeson-" ++ VERSION_aeson,
49+ "-package=websockets-" ++ VERSION_websockets,
50+ "-package=wai-" ++ VERSION_wai,
51+ "-package=wai-websockets-" ++ VERSION_wai_websockets,
52+ "-package=warp-" ++ VERSION_warp,
53+ "-package=http-types-" ++ VERSION_http_types,
54+ "-package=stm-" ++ VERSION_stm,
55+ "-package=time-" ++ VERSION_time,
56+ "-package=containers-" ++ VERSION_containers,
57+ "-package=process-" ++ VERSION_process,
58+ "-package=filepath-" ++ VERSION_filepath,
59+ "-package=ref-tf-" ++ VERSION_ref_tf,
60+ "-package=deepseq-" ++ VERSION_deepseq,
61+ "-package=ghc-prim-" ++ VERSION_ghc_prim,
62+ "-i" <> jsaddlePath </> "jsaddle-warp/src",
63+ "-i" <> jsaddlePath </> "jsaddle-warp/src-ghc",
64+ jsaddlePath </> "jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs",
65+ "-i" <> jsaddlePath </> "jsaddle",
66+ jsaddlePath </> "jsaddle/src-ghc/Data/JSString/Text.hs",
67+ jsaddlePath </> "jsaddle/src-ghc/Data/JSString/Internal/Type.hs",
68+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Foreign.hs",
69+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Foreign/Internal.hs",
70+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Internal/Types.hs",
71+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Marshal.hs",
72+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Marshal/Pure.hs",
73+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Marshal/Internal.hs",
74+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Prim.hs",
75+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Prim/Internal.hs",
76+ jsaddlePath </> "jsaddle/src-ghc/GHCJS/Types.hs",
77+ jsaddlePath </> "jsaddle/src-ghc/JavaScript/Array.hs",
78+ jsaddlePath </> "jsaddle/src-ghc/JavaScript/Array/Internal.hs",
79+ jsaddlePath </> "jsaddle/src-ghc/JavaScript/Object/Internal.hs",
80+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle.hs",
81+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Arguments.hs",
82+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Classes.hs",
83+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Classes/Internal.hs",
84+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Evaluate.hs",
85+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Exception.hs",
86+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Monad.hs",
87+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Marshal/String.hs",
88+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Native.hs",
89+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Native/Internal.hs",
90+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Object.hs",
91+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Properties.hs",
92+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Run.hs",
93+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs",
94+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/String.hs",
95+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Types.hs",
96+ jsaddlePath </> "jsaddle/src/Language/Javascript/JSaddle/Value.hs" ]
jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal+11−14
@@ -1,5 +1,5 @@
11name: jsaddle-webkit2gtk
2−version: 0.9.8.0
2+version: 0.8.2.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -26,23 +26,20 @@ library
2626 base <5
2727 if !impl(ghcjs -any)
2828 build-depends:
29− aeson >=0.8.0.2 && <1.6,
29+ aeson >=0.8.0.2 && <1.1,
3030 base <5,
3131 bytestring >=0.10.6.0 && <0.11,
32− directory >=1.0.0.2 && <1.4,
33− gi-glib >=2.0.14 && <2.1,
34− gi-gio >=2.0.14 && <2.1,
35− gi-gtk >=3.0.17 && <3.1,
36− gi-webkit2 >=4.0.14 && <4.1,
37− gi-javascriptcore >=4.0.14 && <4.1,
38− haskell-gi-base >=0.20 && <0.26,
39− haskell-gi-overloading >=0.0 && < 2.0,
40− jsaddle >=0.9.4.0 && <0.10,
32+ directory >=1.0.0.2 && <1.3,
33+ gi-glib >=2.0.6 && <2.1,
34+ gi-gio >=2.0.6 && <2.1,
35+ gi-gtk >=3.0.6 && <3.1,
36+ gi-webkit2 >=4.0.6 && <4.1,
37+ gi-javascriptcore >=4.0.6 && <4.1,
38+ haskell-gi-base >=0.20 && <0.21,
39+ jsaddle >=0.8.0.0 && <0.9,
4140 text >=1.2.1.3 && <1.3,
41+ unix >=2.3.1.0 && <2.8,
4242 webkit2gtk3-javascriptcore >=0.14.0.0 && <0.15
43− if !os(windows)
44− build-depends:
45− unix >=2.3.1.0 && <2.8
4643 default-language: Haskell2010
4744 hs-source-dirs: src
4845 ghc-options: -ferror-spans -Wall
jsaddle-webkit2gtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+46−50
@@ -34,75 +34,87 @@ import Control.Monad (void)
3434import Control.Monad.IO.Class (MonadIO(..), MonadIO)
3535import Control.Concurrent (forkIO, yield)
3636
37−#ifndef mingw32_HOST_OS
3837import System.Posix.Signals (installHandler, keyboardSignal, Handler(Catch))
39−#endif
4038import System.Directory (getCurrentDirectory)
4139
4240import Data.Monoid ((<>))
4341import Data.ByteString.Lazy (toStrict, fromStrict)
4442import qualified Data.ByteString.Lazy as LB (ByteString)
4543import Data.Aeson (encode, decode)
44+import Data.Text (Text)
4645import qualified Data.Text as T (pack)
46+import Data.Text.Foreign (fromPtr)
4747import Data.Text.Encoding (decodeUtf8, encodeUtf8)
4848
49+import Foreign.Ptr (castPtr, nullPtr)
50+
4951import Data.GI.Base.BasicTypes (GObject)
5052import Data.GI.Base.Signals
5153 (connectSignalFunPtr, SignalConnectMode(..), SignalConnectMode,
5254 SignalHandlerId)
55+import Data.GI.Base.ManagedPtr (withManagedPtr)
5356
5457import GI.GLib (timeoutAdd, idleAdd, pattern PRIORITY_HIGH, pattern PRIORITY_DEFAULT)
5558import qualified GI.Gtk as Gtk (main, init)
5659import GI.Gtk
5760 (windowSetPosition, windowSetDefaultSize, windowNew,
58− scrolledWindowNew, Adjustment, containerAdd,
61+ scrolledWindowNew, noAdjustment, containerAdd,
5962 WindowType(..), WindowPosition(..), widgetDestroy,
6063 widgetGetToplevel, widgetShowAll, onWidgetDestroy,
6164 mainQuit)
62−import GI.Gio (Cancellable)
63−import GI.JavaScriptCore (valueToString)
65+import GI.Gio (noCancellable)
66+import GI.JavaScriptCore (Value(..), GlobalContext(..))
6467import GI.WebKit2
65− (scriptDialogPromptSetText, scriptDialogPromptGetDefaultText,
66− scriptDialogGetMessage, scriptDialogGetDialogType,
67− onWebViewScriptDialog, WebView,
68− setSettingsEnableWriteConsoleMessagesToStdout,
69− setSettingsEnableJavascript, webViewNewWithUserContentManager,
70− userContentManagerNew,
68+ (WebView, setSettingsEnableWriteConsoleMessagesToStdout,
69+ setSettingsEnableJavascript,
70+ webViewNewWithUserContentManager, userContentManagerNew,
7171 userContentManagerRegisterScriptMessageHandler,
72− javascriptResultGetJsValue,
72+ javascriptResultGetValue, javascriptResultGetGlobalContext,
7373 webViewGetUserContentManager,
7474 mk_UserContentManagerScriptMessageReceivedCallback,
7575 wrap_UserContentManagerScriptMessageReceivedCallback,
7676 webViewRunJavascript, LoadEvent(..),
7777 UserContentManagerScriptMessageReceivedCallback, webViewLoadHtml,
7878 onWebViewLoadChanged, setSettingsEnableDeveloperExtras,
79− webViewSetSettings, webViewGetSettings, ScriptDialogType(..))
79+ webViewSetSettings, webViewGetSettings)
80+
81+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
82+ (JSValueRef, JSContextRef)
83+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
84+ (jsstringgetcharactersptr, jsstringgetlength, jsstringrelease)
85+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
86+ (jsvaluetostringcopy)
8087
81−import Language.Javascript.JSaddle (JSM, Results, Batch)
88+import Language.Javascript.JSaddle (JSM, Results)
8289import Language.Javascript.JSaddle.Run (runJavaScript)
8390import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
8491
85−noAdjustment :: Maybe Adjustment
86−noAdjustment = Nothing
87−
88−noCancellable :: Maybe Cancellable
89−noCancellable = Nothing
90−
9192quitWebView :: WebView -> IO ()
9293quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
9394 widgetDestroy w
9495
9596installQuitHandler :: WebView -> IO ()
96−#ifdef mingw32_HOST_OS
97−installQuitHandler wv = return () -- TODO: Maybe figure something out here for Windows users.
98−#else
9997installQuitHandler wv = void $ installHandler keyboardSignal (Catch (quitWebView wv)) Nothing
100−#endif
10198
10299postGUIAsync :: IO () -> IO ()
103100postGUIAsync action =
104101 void . idleAdd PRIORITY_DEFAULT $ action >> return False
105102
103+withJSContextRef :: GlobalContext -> (JSContextRef -> IO a) -> IO a
104+withJSContextRef (GlobalContext ctx) f = withManagedPtr ctx (f . castPtr)
105+
106+withJSValueRef :: Value -> (JSValueRef -> IO a) -> IO a
107+withJSValueRef (Value ptr) f = withManagedPtr ptr (f . castPtr)
108+
109+valueToText :: JSContextRef -> JSValueRef -> IO Text
110+valueToText ctxRef s = do
111+ jsstring <- jsvaluetostringcopy ctxRef s nullPtr
112+ l <- jsstringgetlength jsstring
113+ p <- jsstringgetcharactersptr jsstring
114+ result <- fromPtr (castPtr p) (fromIntegral l)
115+ jsstringrelease jsstring
116+ return result
117+
106118run :: JSM () -> IO ()
107119run main = do
108120 _ <- Gtk.init Nothing
@@ -127,17 +139,17 @@ run main = do
127139 void . onWebViewLoadChanged webView $ \case
128140 LoadEventFinished -> runInWebView main webView
129141 _ -> return ()
130− webViewLoadHtml webView "" . Just $ "file://" <> T.pack pwd <> "/index.html"
142+ webViewLoadHtml webView "" . Just $ "file://" <> T.pack pwd <> "/"
131143 installQuitHandler webView
132144 Gtk.main
133145
134146runInWebView :: JSM () -> WebView -> IO ()
135147runInWebView f webView = do
136− (processResults, syncResults, start) <- runJavaScript (\batch -> postGUIAsync $
148+ (processResults, start) <- runJavaScript (\batch -> postGUIAsync $
137149 webViewRunJavascript webView (decodeUtf8 . toStrict $ "runJSaddleBatch(" <> encode batch <> ");") noCancellable Nothing)
138150 f
139151
140− addJSaddleHandler webView processResults syncResults
152+ addJSaddleHandler webView processResults
141153 webViewRunJavascript webView (decodeUtf8 $ toStrict jsaddleJs) noCancellable . Just $
142154 \_obj _asyncResult ->
143155 void $ forkIO start
@@ -151,32 +163,17 @@ connectUserContentManagerScriptMessageReceived obj cb after = liftIO $ do
151163 let cb' = wrap_UserContentManagerScriptMessageReceivedCallback cb
152164 cb'' <- mk_UserContentManagerScriptMessageReceivedCallback cb'
153165 connectSignalFunPtr obj "script-message-received::jsaddle" cb'' after
154−#if MIN_VERSION_haskell_gi_base(0,23,0)
155− Nothing
156−#endif
157166
158−addJSaddleHandler :: WebView -> (Results -> IO ()) -> (Results -> IO Batch) -> IO ()
159−addJSaddleHandler webView processResult syncResults = do
167+addJSaddleHandler :: WebView -> (Results -> IO ()) -> IO ()
168+addJSaddleHandler webView processResult = do
160169 manager <- webViewGetUserContentManager webView
161170 _ <- onUserContentManagerScriptMessageReceived manager $ \result -> do
162− arg <- javascriptResultGetJsValue result
163− bs <- encodeUtf8 <$> valueToString arg
171+ ctx <- javascriptResultGetGlobalContext result
172+ arg <- javascriptResultGetValue result
173+ bs <- withJSContextRef ctx $ \ctxRef ->
174+ withJSValueRef arg $ \argRef ->
175+ encodeUtf8 <$> valueToText ctxRef argRef
164176 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−
180177 void $ userContentManagerRegisterScriptMessageHandler manager "jsaddle"
181178
182179jsaddleJs :: LB.ByteString
@@ -185,7 +182,6 @@ jsaddleJs = ghcjsHelpers <> mconcat
185182 , initState
186183 , "\nreturn function(batch) {\n"
187184 , runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));\n")
188− (Just (\a -> "JSON.parse(window.prompt(\"JSaddleSync\", JSON.stringify(" <> a <> ")))"))
189185 , "};\n"
190186 , "})()"
191187 ]
jsaddle-webkitgtk/jsaddle-webkitgtk.cabal+5−7
@@ -1,5 +1,5 @@
11name: jsaddle-webkitgtk
2−version: 0.9.0.0
2+version: 0.8.2.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -23,21 +23,19 @@ library
2323 exposed-modules:
2424 Language.Javascript.JSaddle.WebKitGTK
2525 build-depends:
26− aeson >=0.8.0.2 && <1.3,
26+ aeson >=0.8.0.2 && <1.1,
2727 base <5,
2828 bytestring >=0.10.6.0 && <0.11,
29− directory >=1.0.0.2 && <1.4,
29+ directory >=1.0.0.2 && <1.3,
3030 gi-glib >=2.0.6 && <2.1,
3131 gi-gtk >=3.0.6 && <3.1,
3232 gi-webkit >=3.0.6 && <3.1,
3333 gi-javascriptcore >=3.0.6 && <3.1,
3434 haskell-gi-base >=0.20 && <0.21,
35− jsaddle >=0.9.0.0 && <0.10,
35+ jsaddle >=0.8.0.0 && <0.9,
3636 text >=1.2.1.3 && <1.3,
37+ unix >=2.3.1.0 && <2.8,
3738 webkitgtk3-javascriptcore >=0.14.0.0 && <0.15
38− if !os(windows)
39− build-depends:
40− unix >=2.3.1.0 && <2.8
4139 default-language: Haskell2010
4240 hs-source-dirs: src
4341 ghc-options: -ferror-spans -Wall
jsaddle-webkitgtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+4−9
@@ -35,9 +35,8 @@ import qualified Control.Exception as E (catch)
3535import Control.Exception (SomeException(..))
3636import Control.Concurrent (forkIO, yield)
3737
38−#ifndef mingw32_HOST_OS
39−import System.Posix.Signals (installHandler, keyboardSignal, Handler(Catch))
40−#endif
38+import System.Posix.Signals
39+ (Handler(..), keyboardSignal, installHandler)
4140import System.Directory (getCurrentDirectory)
4241
4342import Data.Monoid ((<>))
@@ -96,11 +95,7 @@ quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't t
9695 widgetDestroy w
9796
9897installQuitHandler :: WebView -> IO ()
99−#ifdef mingw32_HOST_OS
100−installQuitHandler wv = return () -- TODO: Maybe figure something out here for Windows users.
101−#else
10298installQuitHandler wv = void $ installHandler keyboardSignal (Catch (quitWebView wv)) Nothing
103−#endif
10499
105100postGUIAsync :: IO () -> IO ()
106101postGUIAsync action =
@@ -185,7 +180,7 @@ runInWebView f webView = do
185180 jsvalueprotect ctxRef result
186181 return result
187182
188− (processResults, _, start) <- runJavaScript (\batch -> postGUIAsync $
183+ (processResults, start) <- runJavaScript (\batch -> postGUIAsync $
189184 withJSContextRef ctx $ \ctxRef ->
190185 withJSString (decodeUtf8 . toStrict $ encode batch) $ \script -> do
191186 val <- jsvaluemakestring ctxRef script
@@ -222,7 +217,7 @@ jsaddleJs = ghcjsHelpers <> mconcat
222217 , initState
223218 , "\nreturn function(batchJSON) {\n"
224219 , " var batch = JSON.parse(batchJSON);\n"
225− , runBatch (\a -> "window.jsaddleCallback(JSON.stringify(" <> a <> "));\n") Nothing
220+ , runBatch (\a -> "window.jsaddleCallback(JSON.stringify(" <> a <> "));\n")
226221 , "};\n"
227222 , "})()\n"
228223 ]
jsaddle-wkwebview/cbits-cocoa/AppDelegate.m+7−71
@@ -2,10 +2,7 @@
22#import <Cocoa/Cocoa.h>
33#import <WebKit/WebKit.h>
44
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);
96
107@interface AppDelegate : NSObject <NSApplicationDelegate>
118@property (nonatomic, assign) IBOutlet NSWindow *window;
@@ -14,12 +11,6 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
1411- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
1512@end
1613
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−
2314@implementation AppDelegate
2415
2516- (void)applicationWillTerminate:(NSNotification *)aNotification {
@@ -31,8 +22,7 @@ uint64_t global_developerExtrasEnabled = 1;
3122 if (self) {
3223 _handler = handler;
3324 NSRect contentSize = NSMakeRect(0.0, 500.0, 1000.0, 700.0);
34− // TODO replace with once nix has newer Cocoa NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
35− NSUInteger windowStyleMask = NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
25+ NSUInteger windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
3626 _window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES];
3727 _window.backgroundColor = [NSColor whiteColor];
3828 _window.title = progName;
@@ -40,39 +30,18 @@ uint64_t global_developerExtrasEnabled = 1;
4030 return self;
4131}
4232
43−-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
44− bool b = callWithCStringReturningBool([filename UTF8String], global_applicationOpenFile);
45− return b ? YES : NO;
46−}
47−
4833-(void)applicationWillFinishLaunching:(NSNotification *)notification {
4934 WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
50− if (global_developerExtrasEnabled) {
51− [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
52− }
53− WKWebView *webView = [[WKWebView alloc] initWithFrame: [_window.contentView frame] configuration:theConfiguration];
35+ [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
36+ WKWebView *webView = [[WKWebView alloc] initWithFrame:_window.contentView.frame configuration:theConfiguration];
5437 [_window setContentView:webView];
55− callWithWebView(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);
38+ withWebView(webView, _handler);
6939}
7040
7141-(void)applicationDidFinishLaunching:(NSNotification *)notification {
7242 [_window orderFrontRegardless];
7343 [_window center];
7444 [NSApp activateIgnoringOtherApps:YES];
75− callIO(global_didFinishLaunchingWithOptions);
7645}
7746
7847-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
@@ -81,46 +50,13 @@ uint64_t global_developerExtrasEnabled = 1;
8150
8251@end
8352
84−void runInWKWebView(HsStablePtr handler,
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) {
53+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
10554 @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;
11155 NSApplication *application = [NSApplication sharedApplication];
11256 AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
11357 [application setDelegate:appDelegate];
11458 const char * _Nonnull argv [] = {"", 0};
115− [application setActivationPolicy:NSApplicationActivationPolicyRegular];
116− [application
117− performSelectorOnMainThread:@selector(run)
118− withObject:nil
119− waitUntilDone:YES];
59+ NSApplicationMain(0, argv);
12060 }
12161}
12262
123−BOOL openApp(NSURL * url) {
124− NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
125− return [workspace openURL:url];
126−}
jsaddle-wkwebview/cbits-uikit/AppDelegate.m+2−152
@@ -1,207 +1,57 @@
11#import "AppDelegate.h"
22#import "ViewController.h"
3−#import <UserNotifications/UserNotifications.h>
4−#import <stdint.h>
5−
6−extern void callIO(HsStablePtr);
7−extern void callWithCString(const char * _Nonnull, HsStablePtr);
8−extern void callWithCIntCString(int n, const char * _Nonnull, HsStablePtr);
93
104@interface AppDelegate ()
115
6+
127@end
138
149HsStablePtr globalHandler = 0;
15−HsStablePtr global_willFinishLaunchingWithOptions = 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;
3310
3411@implementation AppDelegate
3512
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−
4213- (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.
4414 self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
4515 // Override point for customization after application launch.
4616 self.window.rootViewController = [[ViewController alloc] initWithHandler:globalHandler];
4717 self.window.backgroundColor = [UIColor whiteColor];
4818 [self.window makeKeyAndVisible];
49− 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)
66− completionHandler:^(BOOL granted, NSError * _Nullable error) {
67− // Handler used to alter application behavior based on types of notifications authorized
68− }];
69− if (global_registerForRemoteNotifications) {
70− [application registerForRemoteNotifications];
71− }
72− }
73− callIO(global_didFinishLaunchingWithOptions);
7419 return YES;
7520}
7621
7722- (void)applicationWillResignActive:(UIApplication *)application {
7823 // 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.
7924 // 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);
8125}
8226
8327
8428- (void)applicationDidEnterBackground:(UIApplication *)application {
8529 // 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.
8630 // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
87− callIO(global_applicationDidEnterBackground);
8831}
8932
9033
9134- (void)applicationWillEnterForeground:(UIApplication *)application {
9235 // 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);
9436}
9537
9638
9739- (void)applicationDidBecomeActive:(UIApplication *)application {
9840 // 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);
10041}
10142
10243
10344- (void)applicationWillTerminate:(UIApplication *)application {
10445 // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
105− callIO(global_applicationWillTerminate);
106−}
107−
108−- (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];
111− callWithCString([deviceTokenString UTF8String], global_didRegisterForRemoteNotificationsWithDeviceToken);
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−}
130−
131−- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
132− // Sent to the delegate when Apple Push Notification service cannot successfully complete the registration process.
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;
15046}
15147
15248@end
15349
154−void runInWKWebView(HsStablePtr handler,
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) {
50+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
17551 @autoreleasepool {
17652 globalHandler = handler;
177− global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
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;
19553 const char * _Nonnull argv [] = {"", 0};
19654 UIApplicationMain(0, argv, nil, NSStringFromClass([AppDelegate class]));
19755 }
19856}
19957
200−BOOL openApp(NSURL * url) {
201− UIApplication *app = [UIApplication sharedApplication];
202− if ([app canOpenURL:url]) {
203− [app openURL:url];
204− return true;
205− }
206− return false;
207−}
jsaddle-wkwebview/cbits-uikit/ViewController.m+2−2
@@ -1,6 +1,6 @@
11#import "ViewController.h"
22
3−extern void callWithWebView(WKWebView *, HsStablePtr);
3+extern void withWebView(WKWebView *, HsStablePtr);
44
55@interface ViewController ()
66
@@ -27,7 +27,7 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
2727
2828- (void)viewDidLoad {
2929 [super viewDidLoad];
30− callWithWebView(_webView, _handler);
30+ withWebView(_webView, _handler);
3131}
3232
3333
jsaddle-wkwebview/cbits/WKWebView.m+10−79
@@ -2,32 +2,26 @@
22#import <Foundation/Foundation.h>
33#import <WebKit/WebKit.h>
44
5−BOOL openApp(NSURL * url);
5+extern void jsaddleStart(HsStablePtr);
6+extern void jsaddleResult(HsStablePtr, const char * _Nonnull result);
67
7−@interface JSaddleHandler : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
8+@interface JSaddleHandler : NSObject<WKNavigationDelegate, WKScriptMessageHandler>
89
910@property (nonatomic, assign) HsStablePtr startHandler;
1011@property (nonatomic, assign) HsStablePtr resultHandler;
11−@property (nonatomic, assign) HsStablePtr syncHandler;
12−@property (nonatomic, assign) void (^completionHandler)(NSString *result);
1312
14−- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler syncHandler:(HsStablePtr)syncHandler;
13+- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler;
1514
1615@end
1716
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−
2217@implementation JSaddleHandler
2318
24−- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler syncHandler:(HsStablePtr)syncHandler
19+- (instancetype)initHandler:(HsStablePtr)startHandler resultHandler:(HsStablePtr)resultHandler
2520{
2621 self = [super init];
2722 if (self) {
2823 _startHandler = startHandler;
2924 _resultHandler = resultHandler;
30− _syncHandler = syncHandler;
3125 }
3226 return self;
3327}
@@ -43,82 +37,19 @@ extern void jsaddleSyncResult(HsStablePtr, JSaddleHandler *, const char * _Nonnu
4337 jsaddleResult(self.resultHandler, [s UTF8String]);
4438}
4539
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
59−{
60− NSURL *url = navigationAction.request.URL;
61−
62− if(!navigationAction.targetFrame && ![url.scheme isEqualToString:@"file"]) {
63− if(openApp(url))
64− decisionHandler(WKNavigationActionPolicyCancel);
65− else
66− decisionHandler(WKNavigationActionPolicyAllow);
67− }
68− else
69− decisionHandler(WKNavigationActionPolicyAllow);
70−}
71−
7240@end
7341
74−void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler, HsStablePtr syncHandler) {
75− JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler syncHandler:syncHandler];
42+void addJSaddleHandler(WKWebView *webView, HsStablePtr startHandler, HsStablePtr resultHandler) {
43+ JSaddleHandler * handler = [[JSaddleHandler alloc] initHandler:startHandler resultHandler:resultHandler];
7644 [[[webView configuration] userContentController] addScriptMessageHandler:handler name:@"jsaddle"];
77−#ifdef USE_UIKIT
78− webView.scrollView.bounces = NO;
79−#endif
8045 webView.navigationDelegate = handler;
81− webView.UIDelegate = handler;
8246}
8347
8448void evaluateJavaScript(WKWebView *webView, const char * _Nonnull js) {
85− NSString *jsString = [[NSString alloc] initWithCString:js encoding:NSUTF8StringEncoding];
86− dispatch_async(dispatch_get_main_queue(), ^{
87− [webView evaluateJavaScript:jsString completionHandler:NULL];
88− [jsString release];
89− });
90−}
91−
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) {
99− 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(), ^{
102− [webView loadHTMLString:htmlString baseURL:baseURL];
103− });
49+ [webView evaluateJavaScript:[NSString stringWithCString:js encoding:NSUTF8StringEncoding] completionHandler:NULL];
10450}
10551
106−const char * mainBundleResourcePathC() {
107− NSBundle *main = [NSBundle mainBundle];
108− if(!main) return NULL;
109− NSString *path = main.resourcePath;
110− if(!path) return NULL;
111− return [path UTF8String];
52+void loadHTMLString(WKWebView *webView, const char * _Nonnull html) {
53+ [webView loadHTMLString:[NSString stringWithCString:html encoding:NSUTF8StringEncoding] baseURL:NULL];
11254}
11355
114−void loadBundleFile(WKWebView *webView, const char * _Nonnull file, const char * _Nonnull allowing) {
115− NSBundle *main = [NSBundle mainBundle];
116− if(main) {
117− NSString *path = main.resourcePath;
118− if(path) {
119− NSURL *fileUrl = [NSURL fileURLWithPath:[NSString stringWithFormat: @"%@/%@", path, [NSString stringWithCString:file encoding:NSUTF8StringEncoding]]];
120− NSURL *allowingUrl = [NSURL fileURLWithPath:[NSString stringWithFormat: @"%@/%@", path, [NSString stringWithCString:allowing encoding:NSUTF8StringEncoding]]];
121− [webView loadFileURL:fileUrl allowingReadAccessToURL:allowingUrl];
122− }
123− }
124−}
jsaddle-wkwebview/default.nixdeleted−14
@@ -1,14 +0,0 @@
1−{ filterSource ? (x: x) }:
2−self: super:
3−let
4− pkgs = self.callPackage ({pkgs}: pkgs) {};
5− inherit (pkgs) lib;
6−in
7−with pkgs.haskell.lib;
8−overrideCabal (self.callCabal2nix "jsaddle-wkwebview" (filterSource ./.) {}) (drv: {
9− libraryFrameworkDepends = (drv.libraryFrameworkDepends or []) ++
10− (if pkgs.stdenv.hostPlatform.useiOSPrebuilt then [
11− "${pkgs.buildPackages.darwin.xcode}/Contents/Developer/Platforms/${pkgs.stdenv.hostPlatform.xcodePlatform}.platform/Developer/SDKs/${pkgs.stdenv.hostPlatform.xcodePlatform}.sdk/System"
12− ] else (with pkgs.buildPackages.darwin.apple_sdk.frameworks; [ Cocoa WebKit ]));
13− buildDepends = lib.optional (!pkgs.stdenv.hostPlatform.useiOSPrebuilt) [ pkgs.buildPackages.darwin.cf-private ];
14−})
jsaddle-wkwebview/jsaddle-wkwebview.cabal+15−32
@@ -1,5 +1,5 @@
11name: jsaddle-wkwebview
2−version: 0.9.8.0
2+version: 0.8.2.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -14,10 +14,6 @@ description:
1414category: Web, Javascript
1515author: Hamish Mackenzie
1616
17−flag include-app-delegate
18− description: Include default AppDelegate C sources.
19− default: True
20−
2117source-repository head
2218 type: git
2319 location: https://github.com/ghcjs/jsaddle
@@ -25,41 +21,28 @@ source-repository head
2521library
2622 exposed-modules:
2723 Language.Javascript.JSaddle.WKWebView
24+ Language.Javascript.JSaddle.WKWebView.Internal
2825 build-depends:
29− base <5
26+ aeson >=0.8.0.2 && <1.1,
27+ base <5,
28+ bytestring >=0.10.6.0 && <0.11,
29+ jsaddle >= 0.8.0.0 && <0.9
3030 default-language: Haskell2010
3131 hs-source-dirs: src
32+ frameworks: Foundation, WebKit
3233 ghc-options: -ferror-spans -Wall
33− if os(linux)
34− buildable: False
34+ c-sources:
35+ cbits/WKWebView.m
3536 if impl(ghcjs)
3637 hs-source-dirs: src-ghcjs
3738 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
4939 hs-source-dirs: src-ghc
50− c-sources:
51− cbits/WKWebView.m
52− cc-options: -Wno-everything
5340 if os(ios)
54− frameworks: UIKit, UserNotifications
55− if flag(include-app-delegate)
56− c-sources:
57− cbits-uikit/AppDelegate.m
58− cbits-uikit/ViewController.m
59− cpp-options: -DUSE_UIKIT
41+ frameworks: UIKit
42+ c-sources:
43+ cbits-uikit/AppDelegate.m
44+ cbits-uikit/ViewController.m
6045 else
6146 frameworks: Cocoa
62− if flag(include-app-delegate)
63− c-sources:
64− cbits-cocoa/AppDelegate.m
65− cpp-options: -DUSE_COCOA
47+ c-sources:
48+ cbits-cocoa/AppDelegate.m
jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+5−205
@@ -1,219 +1,19 @@
11module Language.Javascript.JSaddle.WKWebView
22 ( jsaddleMain
3− , jsaddleMainHTMLWithBaseURL
4− , jsaddleMainFile
53 , WKWebView(..)
64 , run
7− , run'
8− , runWithAppConfig
9− , runHTMLWithBaseURL
10− , runFile
11− , mainBundleResourcePath
12− , ApplicationState (..)
13− , AppDelegateConfig (..)
14− , AppDelegateNotificationConfig (..)
15− , AuthorizationOption (..)
165 ) where
176
18−import 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)
7+import Language.Javascript.JSaddle.WKWebView.Internal (jsaddleMain, WKWebView(..))
268import System.Environment (getProgName)
279import Foreign.C.String (CString, withCString)
28−import Foreign.C.Types (CBool, CInt)
2910import Foreign.StablePtr (StablePtr, newStablePtr)
3011import Language.Javascript.JSaddle (JSM)
3112
32−foreign import ccall runInWKWebView
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 ()
13+foreign import ccall runInWKWebView :: StablePtr (WKWebView -> IO ()) -> CString -> IO ()
5614
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−
133−-- | Run JSaddle in a WKWebView
13415run :: JSM () -> IO ()
135−run f = run' def (jsaddleMain f)
136−
137−-- | Run JSaddle in a WKWebView
138−runWithAppConfig :: AppDelegateConfig -> JSM () -> IO ()
139−runWithAppConfig cfg = run' cfg . jsaddleMain
140−
141−-- | Run JSaddle in a WKWebView first loading the specified file
142−-- from the mainBundle (relative to the resourcePath).
143−runFile
144− :: ByteString -- ^ The file to navigate to.
145− -> ByteString -- ^ The path to allow read access to.
146− -> AppDelegateConfig
147− -> JSM ()
148− -> IO ()
149−runFile url allowing cfg = run' cfg . jsaddleMainFile url allowing
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
16+run f = do
17+ handler <- newStablePtr (jsaddleMain f)
16718 progName <- getProgName
168−
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
19+ withCString progName $ runInWKWebView handler
jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+16−109
@@ -1,101 +1,47 @@
11{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
22module Language.Javascript.JSaddle.WKWebView.Internal
33 ( jsaddleMain
4− , jsaddleMainHTMLWithBaseURL
5− , jsaddleMainFile
64 , WKWebView(..)
7− , mainBundleResourcePath
85 ) where
96
107import Control.Monad (void, join)
11−import Control.Concurrent (forkIO, forkOS)
12−import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
8+import Control.Concurrent (forkIO)
139
1410import Data.Monoid ((<>))
1511import Data.ByteString (useAsCString, packCString)
16−import qualified Data.ByteString as BS (ByteString)
1712import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
1813import Data.Aeson (encode, decode)
19−import qualified Data.Text as T (pack)
20−import Data.Text.Encoding (encodeUtf8)
2114
2215import Foreign.C.String (CString)
23−import Foreign.C.Types (CBool(..), CInt(..))
24−import Foreign.Ptr (Ptr, nullPtr)
16+import Foreign.Ptr (Ptr)
2517import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
2618
27−import Language.Javascript.JSaddle (Results, Batch, JSM)
19+import Language.Javascript.JSaddle (Results, JSM)
2820import Language.Javascript.JSaddle.Run (runJavaScript)
2921import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
3022
31−import System.Directory (getCurrentDirectory)
32−
3323newtype WKWebView = WKWebView (Ptr WKWebView)
34−newtype JSaddleHandler = JSaddleHandler (Ptr JSaddleHandler)
3524
3625foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO ()
3726foreign 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 ()
40−foreign export ccall callWithCIntCString :: CInt -> CString -> StablePtr (CInt -> CString -> IO ()) -> 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 ()
45−foreign import ccall loadHTMLStringWithBaseURL :: WKWebView -> CString -> CString -> IO ()
46−foreign import ccall loadBundleFile :: WKWebView -> CString -> CString -> IO ()
27+foreign export ccall withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
28+foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> IO ()
29+foreign import ccall loadHTMLString :: WKWebView -> CString -> IO ()
4730foreign import ccall evaluateJavaScript :: WKWebView -> CString -> IO ()
48−foreign import ccall completeSync :: JSaddleHandler -> CString -> IO ()
49−foreign import ccall mainBundleResourcePathC :: IO CString
5031
51−-- | Run JSaddle in WKWebView
5232jsaddleMain :: JSM () -> WKWebView -> IO ()
5333jsaddleMain 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 $
67− useAsCString baseURL $ \url ->
68− useAsCString initialHTML $ \html ->
69− loadHTMLStringWithBaseURL webView html url
70−
71−-- | Run JSaddle in a WKWebView first loading the specified file
72−-- from the mainBundle (relative to the resourcePath).
73−jsaddleMainFile :: BS.ByteString -- ^ The file to navigate to.
74− -> BS.ByteString -- ^ The path to allow read access to.
75− -> JSM () -> WKWebView -> IO ()
76−jsaddleMainFile url allowing f webView =
77− jsaddleMain' f webView $
78− useAsCString url $ \u ->
79− useAsCString allowing $ \a ->
80− loadBundleFile webView u a
81−
82−jsaddleMain' :: JSM () -> WKWebView -> IO () -> IO ()
83−jsaddleMain' f webView loadHtml = do
84− ready <- newEmptyMVar
85−
86− (processResult, syncResult, start) <- runJavaScript (\batch ->
34+ (processResult, start) <- runJavaScript (\batch ->
8735 useAsCString (toStrict $ "runJSaddleBatch(" <> encode batch <> ");") $
8836 evaluateJavaScript webView)
8937 f
9038
91− startHandler <- newStablePtr (putMVar ready ())
39+ startHandler <- newStablePtr (void $ forkIO start)
9240 resultHandler <- newStablePtr processResult
93− syncResultHandler <- newStablePtr syncResult
94− addJSaddleHandler webView startHandler resultHandler syncResultHandler
95− loadHtml
96− void . forkOS $ do
97− takeMVar ready
98− useAsCString (toStrict jsaddleJs) (evaluateJavaScript webView) >> void (forkIO start)
41+ addJSaddleHandler webView startHandler resultHandler
42+
43+ useAsCString (toStrict indexHtml) $ loadHTMLString webView
44+ useAsCString (toStrict jsaddleJs) $ evaluateJavaScript webView
9945
10046jsaddleStart :: StablePtr (IO ()) -> IO ()
10147jsaddleStart ptrHandler = join (deRefStablePtr ptrHandler)
@@ -105,52 +51,20 @@ jsaddleResult ptrHandler s = do
10551 processResult <- deRefStablePtr ptrHandler
10652 result <- packCString s
10753 case decode (fromStrict result) of
108− Nothing -> error $ "jsaddle Results decode failed : " <> show result
54+ Nothing -> error $ "jsaddle WebSocket decode failed : " <> show result
10955 Just r -> processResult r
11056
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 ()
123−callWithWebView webView ptrF = do
57+withWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
58+withWebView webView ptrF = do
12459 f <- deRefStablePtr ptrF
12560 f webView
12661
127−callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
128−callWithCString c fptr = do
129− f <- deRefStablePtr fptr
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
140− f c
141−
142−callIO :: StablePtr (IO ()) -> IO ()
143−callIO ptr = do
144− f <- deRefStablePtr ptr
145− f
146−
14762jsaddleJs :: ByteString
14863jsaddleJs = ghcjsHelpers <> "\
14964 \runJSaddleBatch = (function() {\n\
15065 \ " <> initState <> "\n\
15166 \ return function(batch) {\n\
152− \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));\n")
153− (Just (\a -> "JSON.parse(window.prompt(\"JSaddleSync\", JSON.stringify(" <> a <> ")))")) <> "\
67+ \ " <> runBatch (\a -> "window.webkit.messageHandlers.jsaddle.postMessage(JSON.stringify(" <> a <> "));") <> "\
15468 \ };\n\
15569 \})();\n\
15670 \"
@@ -165,10 +79,3 @@ indexHtml =
16579 \<body>\n\
16680 \</body>\n\
16781 \</html>"
168−
169−mainBundleResourcePath :: IO (Maybe BS.ByteString)
170−mainBundleResourcePath = do
171− bs <- mainBundleResourcePathC
172− if bs == nullPtr
173− then return Nothing
174− else Just <$> packCString bs
jsaddle/jsaddle.cabal+15−32
@@ -1,5 +1,5 @@
11name: jsaddle
2−version: 0.9.8.0
2+version: 0.8.0.1
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -10,7 +10,7 @@ description:
1010 This package provides an EDSL for calling JavaScript that
1111 can be used both from GHCJS and GHC. When using GHC
1212 the application is run using Warp and WebSockets to
13− drive a small JavaScript helper.
13+ drive a small JavaScipt helper.
1414category: Web, Javascript
1515author: Hamish Mackenzie
1616
@@ -19,14 +19,6 @@ source-repository head
1919 location: https://github.com/ghcjs/jsaddle
2020 subdir: jsaddle
2121
22−flag call-stacks
23− description: Include HasCallStack constraint on calls unchecked calls
24− default: False
25−
26−flag check-unchecked
27− description: Fail unchecked calls when they are called (rather than when the result is evaluated)
28− default: False
29−
3022library
3123
3224 if impl(ghcjs -any)
@@ -36,20 +28,18 @@ library
3628 else
3729 build-depends:
3830 attoparsec >=0.11 && <0.14,
39− containers >=0.5.6.2 && <0.7,
31+ containers >=0.5.6.2 && <0.6,
4032 deepseq >=1.3 && < 1.5,
4133 filepath >=1.4.0.0 && <1.5,
4234 ghc-prim,
43− http-types >=0.8.6 && <0.13,
44− process >=1.2.3.0 && <1.7,
45− random >= 1.1 && < 1.3,
35+ http-types >=0.8.6 && <0.10,
36+ process >=1.2.3.0 && <1.5,
4637 ref-tf >=0.4.0.1 && <0.5,
4738 scientific >=0.3 && <0.4,
48− stm >=2.4.4 && <2.6,
49− time >=1.5.0.1 && <1.11,
39+ stm >=2.4.4 && <2.5,
40+ time >=1.5.0.1 && <1.7,
5041 unordered-containers >=0.2 && <0.3,
51− vector >=0.10 && <0.13,
52− unliftio-core >=0.1 && < 0.3
42+ vector >=0.10 && <0.12
5343 exposed-modules:
5444 Data.JSString
5545 Data.JSString.Internal
@@ -63,7 +53,6 @@ library
6353 GHCJS.Prim
6454 GHCJS.Prim.Internal
6555 GHCJS.Types
66− GHCJS.Concurrent
6756 GHCJS.Foreign
6857 GHCJS.Foreign.Internal
6958 GHCJS.Internal.Types
@@ -84,10 +73,8 @@ library
8473 JavaScript.Array.Internal
8574 JavaScript.Object
8675 JavaScript.Object.Internal
87− Language.Javascript.JSaddle.Debug
8876 Language.Javascript.JSaddle.Native
8977 Language.Javascript.JSaddle.Native.Internal
90− Language.Javascript.JSaddle.Null
9178 hs-source-dirs: src-ghc
9279 exposed-modules:
9380 Language.Javascript.JSaddle
@@ -107,20 +94,16 @@ library
10794 Language.Javascript.JSaddle.Value
10895 Language.Javascript.JSaddle.Types
10996 build-depends:
110− aeson >=0.11.3.0 && <1.6,
111− base >=4.9 && <5,
112− base-compat >=0.9.0 && <0.12,
113− base64-bytestring >=1.0.0.1 && <1.2,
97+ aeson >=0.8.0.2 && <1.1,
98+ base <5,
99+ base64-bytestring >=1.0.0.1 && <1.1,
114100 bytestring >=0.10.6.0 && <0.11,
115− exceptions >=0.8 && <0.11,
116− lens >=3.8.5 && <5.1,
117− primitive >=0.6.1.0 && <0.8,
101+ lens >=3.8.5 && <4.16,
102+ primitive >=0.6.1.0 && <0.7,
118103 text >=1.2.1.3 && <1.3,
119104 transformers >=0.4.2.0 && <0.6
120105 default-language: Haskell2010
121106 hs-source-dirs: src
122107 ghc-options: -ferror-spans -Wall
123− if flag(check-unchecked)
124− cpp-options: -DCHECK_UNCHECKED
125− if flag(call-stacks) || flag(check-unchecked)
126− cpp-options: -DJSADDLE_HAS_CALL_STACK
108+
109+
jsaddle/src-ghc/Data/JSString/Internal/Type.hs+1−2
@@ -38,11 +38,10 @@ import Data.Aeson (ToJSON(..), FromJSON(..))
3838import Data.Data (Data)
3939-- import Data.Text.Internal.Unsafe.Char (ord)
4040import Data.Typeable (Typeable)
41−import Data.Semigroup (Semigroup)
4241import GHC.Exts (Char(..), ord#, andI#, (/=#), isTrue#)
4342
4443-- | 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)
4645
4746instance NFData JSString where rnf !_ = ()
4847
jsaddle/src-ghc/GHCJS/Buffer.hs+2−2
@@ -52,7 +52,7 @@ getInt32Array (SomeBuffer buf) = GHCJSPure $ I.SomeTypedArray <$> buf ^. js "i3"
5252{-# INLINE getInt32Array #-}
5353
5454getUint8Array :: 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"
5656{-# INLINE getUint8Array #-}
5757
5858getUint16Array :: SomeBuffer any -> GHCJSPure (I.SomeUint16Array any)
@@ -85,7 +85,7 @@ clone = js_clone
8585
8686fromByteString :: ByteString -> GHCJSPure (Buffer, Int, Int)
8787fromByteString bs = GHCJSPure $ do
88− buffer <- SomeBuffer <$> jsg1 "h$newByteArrayFromBase64String" (decodeUtf8 $ B64.encode bs)
88+ buffer <- SomeBuffer <$> jsg1 "h$newByteArrayBase64String" (decodeUtf8 $ B64.encode bs)
8989 return (buffer, 0, BS.length bs)
9090{-# INLINE fromByteString #-}
9191
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
2727 (valueToBool)
2828import Data.Typeable (Typeable)
2929import GHCJS.Prim (isNull, isUndefined)
30−import System.IO.Unsafe (unsafePerformIO)
31−import Data.IORef (newIORef)
3230
3331jsTrue :: JSVal
34−jsTrue = JSVal . unsafePerformIO $ newIORef 3
35−{-# NOINLINE jsTrue #-}
32+jsTrue = JSVal 3
33+{-# INLINE jsTrue #-}
3634
3735jsFalse :: JSVal
38−jsFalse = JSVal . unsafePerformIO $ newIORef 2
39−{-# NOINLINE jsFalse #-}
36+jsFalse = JSVal 2
37+{-# INLINE jsFalse #-}
4038
4139jsNull :: JSVal
42−jsNull = JSVal . unsafePerformIO $ newIORef 0
43−{-# NOINLINE jsNull #-}
40+jsNull = JSVal 0
41+{-# INLINE jsNull #-}
4442
4543toJSBool :: Bool -> JSVal
46−toJSBool b = JSVal . unsafePerformIO . newIORef $ if b then 3 else 2
47−{-# NOINLINE toJSBool #-}
44+toJSBool b = JSVal $ if b then 3 else 2
45+{-# INLINE toJSBool #-}
4846
4947jsUndefined :: JSVal
50−jsUndefined = JSVal . unsafePerformIO $ newIORef 1
51−{-# NOINLINE jsUndefined #-}
48+jsUndefined = JSVal 1
49+{-# INLINE jsUndefined #-}
5250
5351isTruthy :: JSVal -> GHCJSPure Bool
5452isTruthy = GHCJSPure . valueToBool
jsaddle/src-ghc/GHCJS/Marshal/Internal.hs+2−21
@@ -1,4 +1,4 @@
1−{-# LANGUAGE CPP, ScopedTypeVariables, DeriveDataTypeable, DefaultSignatures,
1+{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, DefaultSignatures,
22 TypeOperators, TupleSections, FlexibleContexts, FlexibleInstances,
33 LambdaCase
44 #-}
@@ -34,7 +34,7 @@ import qualified Data.JSString.Internal.Type as JSS
3434import qualified JavaScript.Object.Internal as OI (Object(..), create, setProp, getProp)
3535import qualified JavaScript.Array.Internal as AI (SomeJSArray(..), create, push, read, fromListIO, toListIO)
3636
37−import Language.Javascript.JSaddle.Types (JSM, MutableJSArray, GHCJSPure(..), ghcjsPure, ghcjsPureMap, JSadddleHasCallStack)
37+import Language.Javascript.JSaddle.Types (JSM, MutableJSArray, GHCJSPure(..), ghcjsPure, ghcjsPureMap)
3838import Language.Javascript.JSaddle.String (textToStr)
3939
4040data Purity = PureShared -- ^ conversion is pure even if the original value is shared
@@ -61,36 +61,17 @@ class ToJSVal a where
6161 default toJSVal :: (Generic a, GToJSVal (Rep a ())) => a -> JSM JSVal
6262 toJSVal = toJSVal_generic id
6363
64−fromJustWithStack :: JSadddleHasCallStack => Maybe a -> a
65−fromJustWithStack Nothing = error "fromJSValUnchecked: fromJSVal result was Nothing"
66−fromJustWithStack (Just x) = x
67−
6864class FromJSVal a where
6965 fromJSVal :: JSVal -> JSM (Maybe a)
7066
71−#if MIN_VERSION_base(4,9,0) && defined(JSADDLE_HAS_CALL_STACK)
72− fromJSValUnchecked :: JSadddleHasCallStack => JSVal -> JSM a
73−#ifdef CHECK_UNCHECKED
74− fromJSValUnchecked v = fromJSVal v >>= \case
75− Nothing -> error "fromJSValUnchecked: fromJSVal result was Nothing"
76− Just x -> return x
77−#else
78− fromJSValUnchecked = fmap fromJustWithStack . fromJSVal
79−#endif
80−#else
8167 fromJSValUnchecked :: JSVal -> JSM a
8268 fromJSValUnchecked = fmap fromJust . fromJSVal
83−#endif
8469 {-# INLINE fromJSValUnchecked #-}
8570
8671 fromJSValListOf :: JSVal -> JSM (Maybe [a])
8772 fromJSValListOf = fmap sequence . (mapM fromJSVal <=< AI.toListIO . coerce) -- fixme should check that it's an array
8873
89−#if MIN_VERSION_base(4,9,0) && defined(JSADDLE_HAS_CALL_STACK)
90− fromJSValUncheckedListOf :: JSadddleHasCallStack => JSVal -> JSM [a]
91−#else
9274 fromJSValUncheckedListOf :: JSVal -> JSM [a]
93−#endif
9475 fromJSValUncheckedListOf = mapM fromJSValUnchecked <=< AI.toListIO . coerce
9576
9677 -- default fromJSVal :: PFromJSVal a => JSVal a -> JSM (Maybe a)
jsaddle/src-ghc/GHCJS/Prim/Internal.hs+3−5
@@ -17,8 +17,6 @@ import Unsafe.Coerce (unsafeCoerce)
1717import Data.Aeson (ToJSON(..), FromJSON(..))
1818
1919import qualified GHC.Exception as Ex
20−import Data.IORef (newIORef, IORef)
21−import System.IO.Unsafe (unsafePerformIO)
2220
2321-- A reference to a particular JavaScript value inside the JavaScript context
2422type JSValueRef = Int64
@@ -27,7 +25,7 @@ type JSValueRef = Int64
2725 JSVal is a boxed type that can be used as FFI
2826 argument or result.
2927-}
30−newtype JSVal = JSVal (IORef JSValueRef)
28+newtype JSVal = JSVal JSValueRef deriving(Show, ToJSON, FromJSON)
3129
3230instance NFData JSVal where
3331 rnf x = x `seq` ()
@@ -51,8 +49,8 @@ mkJSException ref =
5149 return (JSException (unsafeCoerce ref) "")
5250
5351jsNull :: JSVal
54−jsNull = JSVal . unsafePerformIO $ newIORef 0
55−{-# NOINLINE jsNull #-}
52+jsNull = JSVal 0
53+{-# INLINE jsNull #-}
5654
5755{- | If a synchronous thread tries to do something that can only
5856 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
2727import GHC.Types
2828import GHC.Prim
2929import GHC.Ptr
30−import GHC.IORef
31−import GHC.IO.Unsafe (unsafePerformIO)
3230
3331import Control.DeepSeq
3432import Unsafe.Coerce
3533
36−type Ref# = IORef Int64
34+type Ref# = Int64
3735
3836mkRef :: Ref# -> JSVal
3937mkRef = JSVal
4038{-# INLINE mkRef #-}
4139
4240nullRef :: JSVal
43−nullRef = JSVal . unsafePerformIO $ newIORef 0
44−{-# NOINLINE nullRef #-}
41+nullRef = JSVal 0
42+{-# INLINE nullRef #-}
4543
4644--toPtr :: JSVal -> Ptr a
4745--toPtr (JSVal x) = unsafeCoerce (Ptr' x 0#)
jsaddle/src-ghc/JavaScript/Array.hs+2−2
@@ -48,7 +48,7 @@ import Language.Javascript.JSaddle.Types
4848 (JSM, JSVal, SomeJSArray(..), JSArray,
4949 MutableJSArray, GHCJSPure(..))
5050import Control.Lens.Operators ((^.))
51−import Language.Javascript.JSaddle.Object (js2, js0, (<##), js1, js)
51+import Language.Javascript.JSaddle.Object (js2, js0, (##), js1, js)
5252import Language.Javascript.JSaddle.Value (valToNumber)
5353import JavaScript.Array.Internal (create, fromList, fromListIO, toList, toListIO, index, read, push)
5454
@@ -69,7 +69,7 @@ append (SomeJSArray x) (SomeJSArray y) = SomeJSArray <$> x ^. js1 "concat" y
6969{-# INLINE append #-}
7070
7171write :: Int -> JSVal -> MutableJSArray -> JSM ()
72−write n e (SomeJSArray x) = void $ (x <## n) e
72+write n e (SomeJSArray x) = void $ (x ## n) e
7373{-# INLINE write #-}
7474
7575pop :: MutableJSArray -> JSM JSVal
jsaddle/src-ghc/JavaScript/Array/Internal.hs+2
@@ -21,6 +21,8 @@ import Data.JSString.Internal.Type (JSString(..))
2121import Language.Javascript.JSaddle.Types (JSM, SomeJSArray(..), JSArray, MutableJSArray, STJSArray, Object(..), GHCJSPure(..))
2222import Language.Javascript.JSaddle.Native.Internal
2323 (newArray, getPropertyByName, getPropertyAtIndex, callAsFunction, valueToNumber)
24+import Language.Javascript.JSaddle.Run
25+ (Command(..), Result(..), AsyncCommand(..), sendCommand, sendLazyCommand)
2426
2527create :: JSM MutableJSArray
2628create = SomeJSArray <$> newArray []
jsaddle/src/Language/Javascript/JSaddle.hs+5−5
@@ -9,7 +9,7 @@
99-- | This package provides an EDSL for calling JavaScript that
1010-- can be used both from GHCJS and GHC. When using GHC
1111-- the application is run using Warp and WebSockets to
12−-- drive a small JavaScript helper.
12+-- drive a small JavaScipt helper.
1313-----------------------------------------------------------------------------
1414
1515module Language.Javascript.JSaddle (
@@ -35,10 +35,10 @@ module Language.Javascript.JSaddle (
3535 --
3636 -- Where it makes sense code examples are given in two forms. One
3737 -- 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.
3939
4040 -- * 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
4242 -- convert a Haskell function in the JSM monad into a javascript
4343 -- value.
4444
@@ -52,8 +52,8 @@ module Language.Javascript.JSaddle (
5252 -- and the server will run the Haskell code. The JSaddle parts will
5353 -- be executed by sending commands back to the browser.
5454
55− -- Although the JavaScript code is executed in the strict order
56− -- set out by the EDSL it is done asynchronously to the Haskell code.
55+ -- Although the code JavaScript code is executed in the the strict order
56+ -- set out by the EDSL it done asynchonously to the Haskell code.
5757 -- This improves the performance by reducing the number of round trips
5858 -- needed between the Haskell and JavaScript code.
5959
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)
5050 -> JSM JSVal
5151#ifdef ghcjs_HOST_OS
5252eval script = liftIO $ js_eval (toJSString script)
53−foreign import javascript safe "$r = eval($1);"
53+foreign import javascript unsafe "$r = eval($1);"
5454 js_eval :: JSString -> IO JSVal
5555#else
5656eval = evaluateScript . toJSString
jsaddle/src/Language/Javascript/JSaddle/Monad.hs+26−3
@@ -38,12 +38,13 @@ module Language.Javascript.JSaddle.Monad (
3838 , bracket
3939) where
4040
41+import Prelude hiding (read)
4142#ifndef ghcjs_HOST_OS
42−import Control.Monad.Trans.Reader (runReaderT, ask)
43+import Control.Monad.Trans.Reader (runReaderT, ask, ReaderT(..))
4344#endif
4445import Control.Monad.IO.Class (MonadIO(..))
45−import Control.Monad.Catch (catch, bracket)
46−import Language.Javascript.JSaddle.Types (JSM(..), MonadJSM, liftJSM, JSContextRef)
46+import qualified Control.Exception as E (Exception, catch, bracket)
47+import Language.Javascript.JSaddle.Types (JSM(..), MonadJSM, liftJSM, JSContextRef(..))
4748import Language.Javascript.JSaddle.Run (syncPoint, syncAfter, waitForAnimationFrame, nextAnimationFrame)
4849
4950-- | Gets the JavaScript context from the monad
@@ -65,3 +66,25 @@ runJSM f = liftIO . runReaderT (unJSM f)
6566-- | Alternative version of 'runJSM'
6667runJSaddle :: MonadIO m => JSContextRef -> JSM a -> m a
6768runJSaddle = 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 #-}
41-----------------------------------------------------------------------------
52--
63-- Module : Language.Javascript.JSaddle.Native
@@ -30,8 +27,7 @@ module Language.Javascript.JSaddle.Native.Internal (
3027 , callAsFunction
3128 , callAsConstructor
3229 , newEmptyObject
33− , newAsyncCallback
34− , newSyncCallback
30+ , newCallback
3531 , newArray
3632 , evaluateScript
3733 , deRefVal
@@ -48,6 +44,7 @@ module Language.Javascript.JSaddle.Native.Internal (
4844) where
4945
5046import Control.Monad.IO.Class (MonadIO(..))
47+import Control.Monad.Primitive (touch)
5148
5249import Data.Aeson (Value)
5350
@@ -57,36 +54,32 @@ import Language.Javascript.JSaddle.Types
5754 JSStringReceived(..), JSStringForSend(..), JSObjectForSend(..))
5855import Language.Javascript.JSaddle.Monad (askJSM)
5956import Language.Javascript.JSaddle.Run
60− (Command(..), Result(..), sendCommand,
57+ (Command(..), AsyncCommand(..), Result(..), sendCommand,
6158 sendAsyncCommand, sendLazyCommand, wrapJSVal)
62−import GHC.IORef (IORef(..), readIORef)
63−import GHC.STRef (STRef(..))
64−import GHC.IO (IO(..))
65−import GHC.Base (touch#)
6659
6760wrapJSString :: MonadIO m => JSStringReceived -> m JSString
6861wrapJSString (JSStringReceived ref) = return $ JSString ref
6962
70−touchIORef :: IORef a -> IO ()
71−touchIORef (IORef (STRef r#)) = IO $ \s -> case touch# r# s of s' -> (# s', () #)
72−
7363withJSVal :: MonadIO m => JSVal -> (JSValueForSend -> m a) -> m a
74−withJSVal (JSVal ref) f = do
75− result <- (f . JSValueForSend) =<< liftIO (readIORef ref)
76− liftIO $ touchIORef ref
64+withJSVal v@(JSVal ref) f =
65+ do result <- f (JSValueForSend ref)
66+ liftIO $ touch v
7767 return result
7868
7969withJSVals :: MonadIO m => [JSVal] -> ([JSValueForSend] -> m a) -> m a
8070withJSVals v f =
81− do result <- f =<< mapM (\(JSVal ref) -> liftIO $ JSValueForSend <$> readIORef ref) v
82− liftIO $ mapM_ (\(JSVal ref) -> touchIORef ref) v
71+ do result <- f (map (\(JSVal ref) -> JSValueForSend ref) v)
72+ liftIO $ mapM_ touch v
8373 return result
8474
8575withObject :: MonadIO m => Object -> (JSObjectForSend -> m a) -> m a
8676withObject (Object o) f = withJSVal o (f . JSObjectForSend)
8777
8878withJSString :: 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
9083
9184setPropertyByName :: JSString -> JSVal -> Object -> JSM ()
9285setPropertyByName name val this =
@@ -143,21 +136,13 @@ newEmptyObject :: JSM Object
143136newEmptyObject = Object <$> sendLazyCommand NewEmptyObject
144137{-# INLINE newEmptyObject #-}
145138
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
155−newSyncCallback f = do
156− object <- Object <$> sendLazyCommand NewSyncCallback
139+newCallback :: JSCallAsFunction -> JSM Object
140+newCallback f = do
141+ object <- Object <$> sendLazyCommand NewCallback
157142 add <- addCallback <$> askJSM
158143 liftIO $ add object f
159144 return object
160−{-# INLINE newSyncCallback #-}
145+{-# INLINE newCallback #-}
161146
162147newArray :: [JSVal] -> JSM JSVal
163148newArray xs = withJSVals xs $ \xs' -> sendLazyCommand (NewArray xs')
@@ -172,14 +157,13 @@ deRefVal value = withJSVal value $ sendCommand . DeRefVal
172157{-# INLINE deRefVal #-}
173158
174159valueToBool :: JSVal -> JSM Bool
175−valueToBool v@(JSVal ref) = liftIO (readIORef ref) >>= \case
176− 0 -> return False -- null
177− 1 -> return False -- undefined
178− 2 -> return False -- false
179− 3 -> return True -- true
180− _ -> withJSVal v $ \rval -> do
181− ~(ValueToBoolResult result) <- sendCommand (ValueToBool rval)
182− return result
160+valueToBool (JSVal 0) = return False -- null
161+valueToBool (JSVal 1) = return False -- undefined
162+valueToBool (JSVal 2) = return False -- false
163+valueToBool (JSVal 3) = return True -- true
164+valueToBool v = withJSVal v $ \rval -> do
165+ ~(ValueToBoolResult result) <- sendCommand (ValueToBool rval)
166+ return result
183167{-# INLINE valueToBool #-}
184168
185169valueToNumber :: JSVal -> JSM Double
@@ -208,25 +192,17 @@ valueToJSONValue value = withJSVal value $ \rval -> do
208192{-# INLINE valueToJSONValue #-}
209193
210194isNull :: JSVal -> JSM Bool
211−isNull v@(JSVal ref) = liftIO (readIORef ref) >>= \case
212− 0 -> return True -- null
213− 1 -> return False -- undefined
214− 2 -> return False -- false
215− 3 -> return False -- true
216− _ -> withJSVal v $ \rval -> do
217− ~(IsNullResult result) <- sendCommand $ IsNull rval
218− return result
195+isNull (JSVal 0) = return True
196+isNull v = withJSVal v $ \rval -> do
197+ ~(IsNullResult result) <- sendCommand $ IsNull rval
198+ return result
219199{-# INLINE isNull #-}
220200
221201isUndefined :: JSVal -> JSM Bool
222−isUndefined v@(JSVal ref) = liftIO (readIORef ref) >>= \case
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
228− ~(IsUndefinedResult result) <- sendCommand $ IsUndefined rval
229− return result
202+isUndefined (JSVal 1) = return True
203+isUndefined v = withJSVal v $ \rval -> do
204+ ~(IsUndefinedResult result) <- sendCommand $ IsUndefined rval
205+ return result
230206{-# INLINE isUndefined #-}
231207
232208strictEqual :: 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 (
6666 -- * Calling Haskell From JavaScript
6767 , Function(..)
6868 , function
69− , asyncFunction
7069 , freeFunction
7170 , fun
7271 , JSCallAsFunction
@@ -103,7 +102,7 @@ import Data.Coerce (coerce)
103102#ifdef ghcjs_HOST_OS
104103import GHCJS.Types (nullRef)
105104import GHCJS.Foreign.Callback
106− (releaseCallback, syncCallback2, asyncCallback2, OnBlocked(..), Callback)
105+ (releaseCallback, syncCallback2, OnBlocked(..), Callback)
107106import GHCJS.Marshal (ToJSVal(..))
108107import JavaScript.Array (MutableJSArray)
109108import qualified JavaScript.Array as Array (toListIO, fromListIO)
@@ -116,13 +115,12 @@ import Language.Javascript.JSaddle.Types
116115#else
117116import GHCJS.Marshal.Internal (ToJSVal(..))
118117import Language.Javascript.JSaddle.Native
119− (newAsyncCallback, newSyncCallback, callAsFunction, callAsConstructor)
118+ (newCallback, callAsFunction, callAsConstructor)
120119import Language.Javascript.JSaddle.Monad (askJSM, JSM)
121120import Language.Javascript.JSaddle.Types
122− (JSValueForSend(..), AsyncCommand(..), JSString, Object(..),
123− SomeJSArray(..), JSVal(..), JSCallAsFunction, JSContextRef(..))
121+ (JSString, Object(..), SomeJSArray(..),
122+ JSVal(..), JSCallAsFunction, JSContextRef(..))
124123import JavaScript.Object.Internal (create, listProps)
125−import Language.Javascript.JSaddle.Run (sendAsyncCommand)
126124#endif
127125import JavaScript.Array.Internal (fromListIO)
128126import Language.Javascript.JSaddle.Value (valToObject)
@@ -132,8 +130,6 @@ import Language.Javascript.JSaddle.Arguments (MakeArgs(..))
132130import Control.Monad.IO.Class (MonadIO(..))
133131import Language.Javascript.JSaddle.Properties
134132import Control.Lens (IndexPreservingGetter, to)
135−import Data.IORef (newIORef, readIORef)
136−import System.IO.Unsafe (unsafePerformIO)
137133
138134-- $setup
139135-- >>> import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
@@ -439,16 +435,9 @@ newtype Function = Function {functionObject :: Object}
439435#endif
440436
441437
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−
447438-- ^ 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`).
450439function :: 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
452441 -- call the Haskell one when it is called
453442#ifdef ghcjs_HOST_OS
454443function f = do
@@ -456,26 +445,11 @@ function f = do
456445 rargs <- Array.toListIO (coerce args)
457446 f this this rargs -- TODO pass function object through
458447 Function callback <$> makeFunctionWithCallback callback
448+foreign import javascript unsafe "$r = function () { $1(this, arguments); }"
449+ makeFunctionWithCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO Object
459450#else
460451function f = do
461− object <- newSyncCallback 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
452+ object <- newCallback f
479453 return $ Function object
480454#endif
481455
@@ -484,16 +458,9 @@ freeFunction :: Function -> JSM ()
484458freeFunction (Function callback _) = liftIO $
485459 releaseCallback callback
486460#else
487−freeFunction (Function (Object (JSVal objectRef))) = do
488− -- By now the callback should ideally have been removed from whatever
489− -- events it was added to.
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))
461+freeFunction (Function object) = do
462+ free <- freeCallback <$> askJSM
463+ liftIO $ free object
497464#endif
498465
499466instance ToJSVal Function where
@@ -526,12 +493,13 @@ array args = do
526493 Object . coerce <$> fromListIO rargs
527494
528495-- | JavaScript's global object
496+global :: Object
529497#ifdef ghcjs_HOST_OS
530−foreign import javascript unsafe "$r = globalThis"
531− global :: Object
498+global = js_window
499+foreign import javascript unsafe "$r = window"
500+ js_window :: Object
532501#else
533−global :: Object
534−global = Object . JSVal . unsafePerformIO $ newIORef 4
502+global = Object (JSVal 4)
535503#endif
536504
537505-- | Get a list containing the property names present on a given object
@@ -556,7 +524,7 @@ objCallAsFunction :: MakeArgs args
556524objCallAsFunction f this args = do
557525 rargs <- makeArgs args >>= liftIO . Array.fromListIO
558526 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)"
560528 js_apply :: Object -> Object -> MutableJSArray -> IO JSVal
561529#else
562530objCallAsFunction f this args = do
@@ -576,7 +544,7 @@ objCallAsConstructor :: MakeArgs args
576544objCallAsConstructor f args = do
577545 rargs <- makeArgs args >>= liftIO . Array.fromListIO
578546 liftIO $ js_new f rargs
579−foreign import javascript safe "\
547+foreign import javascript unsafe "\
580548 switch($2.length) {\
581549 case 0 : $r = new $1(); break;\
582550 case 1 : $r = new $1($2[0]); break;\
@@ -612,5 +580,5 @@ nullObject :: Object
612580#ifdef ghcjs_HOST_OS
613581nullObject = Object nullRef
614582#else
615−nullObject = Object . JSVal . unsafePerformIO $ newIORef 0
583+nullObject = Object (JSVal 0)
616584#endif
jsaddle/src/Language/Javascript/JSaddle/Run.hs+71−166
@@ -1,9 +1,6 @@
1−{-# LANGUAGE BangPatterns #-}
21{-# LANGUAGE CPP #-}
32{-# LANGUAGE LambdaCase #-}
43{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE MagicHash #-}
6−{-# LANGUAGE UnboxedTuples #-}
74-----------------------------------------------------------------------------
85--
96-- Module : Language.Javascript.JSaddle.Run
@@ -22,7 +19,6 @@ module Language.Javascript.JSaddle.Run (
2219 , syncAfter
2320 , waitForAnimationFrame
2421 , nextAnimationFrame
25− , enableLogging
2622#ifndef ghcjs_HOST_OS
2723 -- * Functions used to implement JSaddle using JSON messaging
2824 , runJavaScript
@@ -37,67 +33,58 @@ module Language.Javascript.JSaddle.Run (
3733) where
3834
3935#ifdef ghcjs_HOST_OS
40−import Language.Javascript.JSaddle.Types (JSM, syncPoint, syncAfter)
36+import Language.Javascript.JSaddle.Types (JSM)
4137import qualified JavaScript.Web.AnimationFrame as GHCJS
4238 (waitForAnimationFrame)
4339#else
44−import Control.Exception (throwIO, evaluate)
45−import Control.Monad (void, when, zipWithM_)
40+import Control.Exception (throwIO)
41+import Control.Monad (void, when, forever, zipWithM_)
4642import Control.Monad.IO.Class (MonadIO(..))
4743import Control.Monad.Trans.Reader (ask, runReaderT)
4844import Control.Monad.STM (atomically)
49−import Control.Concurrent (forkIO, myThreadId)
45+import Control.Concurrent (forkIO)
5046import Control.Concurrent.STM.TChan
5147 (tryReadTChan, TChan, readTChan, writeTChan, newTChanIO)
5248import Control.Concurrent.STM.TVar
53− (writeTVar, readTVar, readTVarIO, modifyTVar', newTVarIO)
49+ (writeTVar, readTVar, readTVarIO, modifyTVar, newTVarIO)
5450import Control.Concurrent.MVar
55− (tryTakeMVar, MVar, putMVar, takeMVar, newMVar, newEmptyMVar, readMVar, modifyMVar)
51+ (MVar, MVar, putMVar, takeMVar, newEmptyMVar)
5652
5753import System.IO.Unsafe (unsafeInterleaveIO)
5854import System.Mem.Weak (addFinalizer)
59−import System.Random
6055
61−import GHC.Base (IO(..), mkWeak#)
62−import GHC.Conc (ThreadId(..))
6356import Data.Monoid ((<>))
64−import qualified Data.Text as T (unpack, pack)
65−import qualified Data.Map as M (lookup, delete, insert, empty, size)
66−import qualified Data.Set as S (empty, member, insert, delete)
57+import qualified Data.Text as T (unpack)
58+import qualified Data.Map as M (lookup, delete, insert, empty)
6759import Data.Time.Clock (getCurrentTime,diffUTCTime)
68−import Data.IORef
69− (mkWeakIORef, newIORef, atomicWriteIORef, readIORef)
7060
7161import Language.Javascript.JSaddle.Types
72− (Command(..), AsyncCommand(..), Result(..), BatchResults(..), Results(..), JSContextRef(..), JSVal(..),
73− Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..), syncPoint, syncAfter, sendCommand)
62+ (Command(..), AsyncCommand(..), Result(..), Results(..), JSContextRef(..), JSVal(..),
63+ Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..))
7464import Language.Javascript.JSaddle.Exception (JSException(..))
75−import Control.DeepSeq (force, deepseq)
76−#if MIN_VERSION_base(4,11,0)
77−import GHC.Stats (getRTSStatsEnabled, getRTSStats, RTSStats(..), gcdetails_live_bytes, gc)
78−#else
79−import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
80−#endif
81−import Data.Foldable (forM_)
65+-- import Language.Javascript.JSaddle.Native.Internal (wrapJSVal)
66+import Control.DeepSeq (deepseq)
8267#endif
8368
84−#ifndef ghcjs_HOST_OS
85−#if MIN_VERSION_base(4,11,0)
86−currentBytesUsed = gcdetails_live_bytes . gc
69+-- | Forces execution of pending asyncronous code
70+syncPoint :: JSM ()
71+#ifdef ghcjs_HOST_OS
72+syncPoint = return ()
8773#else
88−getRTSStatsEnabled = getGCStatsEnabled
89−getRTSStats = getGCStats
90−#endif
74+syncPoint = do
75+ SyncResult <- sendCommand Sync
76+ return ()
9177#endif
9278
93−-- | Enable (or disable) JSaddle logging
94−enableLogging :: Bool -> JSM ()
79+-- | Forces execution of pending asyncronous code after performing `f`
80+syncAfter :: JSM a -> JSM a
9581#ifdef ghcjs_HOST_OS
96−enableLogging _ = return ()
82+syncAfter = id
9783#else
98−enableLogging v = do
99− f <- doEnableLogging <$> JSM ask
100− liftIO $ f v
84+syncAfter f = do
85+ result <- f
86+ syncPoint
87+ return result
10188#endif
10289
10390-- | On GHCJS this is 'JavaScript.Web.AnimationFrame.waitForAnimationFrame'.
@@ -125,12 +112,17 @@ nextAnimationFrame f = do
125112 syncAfter (f t)
126113
127114#ifndef ghcjs_HOST_OS
115+sendCommand :: Command -> JSM Result
116+sendCommand cmd = do
117+ s <- doSendCommand <$> JSM ask
118+ liftIO $ s cmd
119+
128120sendLazyCommand :: (JSValueForSend -> AsyncCommand) -> JSM JSVal
129121sendLazyCommand cmd = do
130122 nextRefTVar <- nextRef <$> JSM ask
131123 n <- liftIO . atomically $ do
132124 n <- subtract 1 <$> readTVar nextRefTVar
133− writeTVar nextRefTVar $! n
125+ writeTVar nextRefTVar n
134126 return n
135127 s <- doSendAsyncCommand <$> JSM ask
136128 liftIO $ s (cmd $ JSValueForSend n)
@@ -141,112 +133,52 @@ sendAsyncCommand cmd = do
141133 s <- doSendAsyncCommand <$> JSM ask
142134 liftIO $ s cmd
143135
144−runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), Results -> IO Batch, IO ())
136+runJavaScript :: (Batch -> IO ()) -> JSM () -> IO (Results -> IO (), IO ())
145137runJavaScript sendBatch entryPoint = do
146− contextId' <- randomIO
147138 startTime' <- getCurrentTime
148− recvMVar <- newEmptyMVar
149− lastAsyncBatch <- newEmptyMVar
139+ recvChan <- newTChanIO
150140 commandChan <- newTChanIO
151141 callbacks <- newTVarIO M.empty
152142 nextRef' <- newTVarIO 0
153− finalizerThreads' <- newMVar S.empty
154− animationFrameHandlers' <- newMVar []
155− loggingEnabled <- newIORef False
156− liveRefs' <- newMVar S.empty
157143 let ctx = JSContextRef {
158− contextId = contextId'
159− , startTime = startTime'
160− , doSendCommand = \cmd -> cmd `deepseq` do
161− result <- newEmptyMVar
162− atomically $ writeTChan commandChan (Right (cmd, result))
163− unsafeInterleaveIO $
164− takeMVar result >>= \case
165− (ThrowJSValue v) -> do
166− jsval <- wrapJSVal' ctx v
167− throwIO $ JSException jsval
168− r -> return r
169− , 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)
173− , nextRef = nextRef'
174− , doEnableLogging = atomicWriteIORef loggingEnabled
175− , finalizerThreads = finalizerThreads'
176− , animationFrameHandlers = animationFrameHandlers'
177− , liveRefs = liveRefs'
178− }
179− processResults :: Bool -> Results -> IO ()
180− processResults syncCallbacks = \case
144+ startTime = startTime'
145+ , doSendCommand = \cmd -> cmd `deepseq` do
146+ result <- newEmptyMVar
147+ atomically $ writeTChan commandChan (Right (cmd, result))
148+ unsafeInterleaveIO $
149+ takeMVar result >>= \case
150+ (ThrowJSValue (JSValueReceived v)) -> throwIO $ JSException (JSVal v)
151+ r -> return r
152+ , doSendAsyncCommand = \cmd -> cmd `deepseq` atomically (writeTChan commandChan $ Left cmd)
153+ , addCallback = \(Object (JSVal val)) cb -> atomically $ modifyTVar callbacks (M.insert val cb)
154+ , freeCallback = \(Object (JSVal val)) -> atomically $ modifyTVar callbacks (M.delete val)
155+ , nextRef = nextRef'
156+ }
157+ let processResults = \case
181158 (ProtocolError err) -> error $ "Protocol error : " <> T.unpack err
182− (Callback n br (JSValueReceived fNumber) f this a) -> do
183− putMVar recvMVar (n, br)
184− f' <- runReaderT (unJSM $ wrapJSVal f) ctx
159+ (Callback f this a) -> do
160+ f'@(JSVal fNumber) <- runReaderT (unJSM $ wrapJSVal f) ctx
185161 this' <- runReaderT (unJSM $ wrapJSVal this) ctx
186162 args <- runReaderT (unJSM $ mapM wrapJSVal a) ctx
187− logInfo (("Call " <> show fNumber <> " ") <>)
188163 (M.lookup fNumber <$> liftIO (readTVarIO callbacks)) >>= \case
189164 Nothing -> liftIO $ putStrLn "Callback called after it was freed"
190− Just cb -> void . forkIO $ do
191− 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)
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 =
207− readIORef loggingEnabled >>= \case
208− True -> do
209− currentBytesUsedStr <- getRTSStatsEnabled >>= \case
210− True -> show . currentBytesUsed <$> getRTSStats
211− False -> return "??"
212− cbCount <- M.size <$> readTVarIO callbacks
213− putStrLn . s $ "M " <> currentBytesUsedStr <> " CB " <> show cbCount <> " "
214− False -> return ()
215− _ <- forkIO . numberForeverFromM_ 1 $ \nBatch ->
216− readBatch nBatch commandChan >>= \case
217− (batch@(Batch cmds _ _), resultMVars) -> do
218− logInfo (\x -> "Sync " <> x <> show (length cmds, last cmds))
219− _ <- tryTakeMVar lastAsyncBatch
220− putMVar lastAsyncBatch batch
165+ Just cb -> void . forkIO $ runReaderT (unJSM $ cb f' this' args) ctx
166+ m -> atomically $ writeTChan recvChan m
167+ _ <- forkIO . forever $ readBatch commandChan >>= \case
168+ (batch, resultMVars) -> do
221169 sendBatch batch
222− takeResult recvMVar nBatch >>= \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"
226− | otherwise -> do
227− zipWithM_ putMVar resultMVars results
228− forM_ callbacksToFree $ \(JSValueReceived val) ->
229− atomically (modifyTVar' callbacks (M.delete val))
230− (_, Failure callbacksToFree results exception err) -> do
170+ atomically (readTChan recvChan) >>= \case
171+ Success results -> zipWithM_ putMVar resultMVars results
172+ Failure results exception -> do
231173 -- The exception will only be rethrown in Haskell if/when one of the
232174 -- missing results (if any) is evaluated.
233175 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
234− putStrLn err
235176 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
236− forM_ callbacksToFree $ \(JSValueReceived val) ->
237− atomically (modifyTVar' callbacks (M.delete val))
238− return (asyncResults, syncResults, runReaderT (unJSM entryPoint) ctx)
177+ _ -> error "Unexpected jsaddle results"
178+ return (processResults, runReaderT (unJSM entryPoint) ctx)
239179 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])
249− readBatch nBatch chan = do
180+ readBatch :: TChan (Either AsyncCommand (Command, MVar Result)) -> IO (Batch, [MVar Result])
181+ readBatch chan = do
250182 first <- atomically $ readTChan chan -- We want at least one command to send
251183 loop first ([], [])
252184 where
@@ -257,55 +189,28 @@ runJavaScript sendBatch entryPoint = do
257189 let cmds = Right syncCmd:cmds'
258190 resultMVars = resultMVar:resultMVars'
259191 atomically (tryReadTChan chan) >>= \case
260− Nothing -> return (Batch (reverse cmds) False nBatch, reverse resultMVars)
192+ Nothing -> return (Batch (reverse cmds) False, reverse resultMVars)
261193 Just cmd -> loop cmd (cmds, resultMVars)
262194 loop (Left asyncCmd) (cmds', resultMVars) = do
263195 let cmds = Left asyncCmd:cmds'
264196 atomically (tryReadTChan chan) >>= \case
265− Nothing -> return (Batch (reverse cmds) False nBatch, reverse resultMVars)
197+ Nothing -> return (Batch (reverse cmds) False, reverse resultMVars)
266198 Just cmd -> loop cmd (cmds, resultMVars)
267199 -- When we have seen a SyncWithAnimationFrame command only a synchronous command should end the batch
268200 loopAnimation :: Either AsyncCommand (Command, MVar Result) -> ([Either AsyncCommand Command], [MVar Result]) -> IO (Batch, [MVar Result])
269201 loopAnimation (Right (Sync, resultMVar)) (cmds, resultMVars) =
270− return (Batch (reverse (Right Sync:cmds)) True nBatch, reverse (resultMVar:resultMVars))
202+ return (Batch (reverse (Right Sync:cmds)) True, reverse (resultMVar:resultMVars))
271203 loopAnimation (Right (syncCmd, resultMVar)) (cmds, resultMVars) =
272204 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Right syncCmd:cmds, resultMVar:resultMVars)
273205 loopAnimation (Left asyncCmd) (cmds, resultMVars) =
274206 atomically (readTChan chan) >>= \cmd -> loopAnimation cmd (Left asyncCmd:cmds, resultMVars)
275207
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−
281208wrapJSVal :: JSValueReceived -> JSM JSVal
282−wrapJSVal v = do
283− ctx <- JSM ask
284− liftIO $ wrapJSVal' ctx v
285−
286−wrapJSVal' :: JSContextRef -> JSValueReceived -> IO JSVal
287−wrapJSVal' ctx (JSValueReceived n) = do
288− ref <- liftIO $ newIORef n
289− when (n >= 5 || n < 0) $
290−#ifdef JSADDLE_CHECK_WRAPJSVAL
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
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)
209+wrapJSVal (JSValueReceived ref) = do
210+ -- TODO make sure this ref has not already been wrapped (perhaps only in debug version)
211+ let result = JSVal ref
212+ when (ref >= 5) $ do
213+ ctx <- JSM ask
214+ liftIO . addFinalizer result $ doSendAsyncCommand ctx $ FreeRef $ JSValueForSend ref
215+ return result
311216#endif
jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+56−132
@@ -13,6 +13,7 @@
1313
1414module Language.Javascript.JSaddle.Run.Files (
1515 indexHtml
16+ , jsaddleJs
1617 , initState
1718 , runBatch
1819 , ghcjsHelpers
@@ -30,38 +31,25 @@ indexHtml =
3031 \</head>\n\
3132 \<body>\n\
3233 \</body>\n\
33− \<script src=\"/jsaddle.js\"></script>\n\
34+ \<script src=\"jsaddle.js\"></script>\n\
3435 \</html>"
3536
3637initState :: ByteString
3738initState = "\
3839 \ var jsaddle_values = new Map();\n\
39− \ var jsaddle_free = new Map();\n\
4040 \ jsaddle_values.set(0, null);\n\
4141 \ jsaddle_values.set(1, undefined);\n\
4242 \ jsaddle_values.set(2, false);\n\
4343 \ jsaddle_values.set(3, true);\n\
44− \ jsaddle_values.set(4, globalThis);\n\
44+ \ jsaddle_values.set(4, window);\n\
4545 \ 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\
5046 \"
5147
52−runBatch :: (ByteString -> ByteString) -> Maybe (ByteString -> ByteString) -> ByteString
53−runBatch send sendSync = "\
54− \ var runBatch = function(firstBatch, initialSyncDepth) {\n\
48+runBatch :: (ByteString -> ByteString) -> ByteString
49+runBatch send = "\
5550 \ var processBatch = function(timestamp) {\n\
56− \ var batch = firstBatch;\n\
57− \ var callbacksToFree = [];\n\
58− \ var results = [];\n\
59− \ inCallback++;\n\
60− \ try {\n\
61− \ syncDepth = initialSyncDepth || 0;\n\
62− \ for(;;){\n\
63− \ if(batch[2] === expectedBatch) {\n\
64− \ expectedBatch++;\n\
51+ \ var results = [];\n\
52+ \ try {\n\
6553 \ var nCommandsLength = batch[0].length;\n\
6654 \ for (var nCommand = 0; nCommand != nCommandsLength; nCommand++) {\n\
6755 \ var cmd = batch[0][nCommand];\n\
@@ -69,15 +57,7 @@ runBatch send sendSync = "\
6957 \ var d = cmd.Left;\n\
7058 \ switch (d.tag) {\n\
7159 \ 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\
8161 \ break;\n\
8262 \ case \"SetPropertyByName\":\n\
8363 \ jsaddle_values.get(d.contents[0])[d.contents[1]]=jsaddle_values.get(d.contents[2]);\n\
@@ -93,10 +73,6 @@ runBatch send sendSync = "\
9373 \ var n = d.contents[1];\n\
9474 \ jsaddle_values.set(n, d.contents[0]);\n\
9575 \ 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\
10076 \ case \"GetPropertyByName\":\n\
10177 \ var n = d.contents[2];\n\
10278 \ jsaddle_values.set(n, jsaddle_values.get(d.contents[0])[d.contents[1]]);\n\
@@ -113,12 +89,10 @@ runBatch send sendSync = "\
11389 \ var n = d.contents;\n\
11490 \ jsaddle_values.set(n, {});\n\
11591 \ break;\n\
116− \ case \"NewAsyncCallback\":\n\
92+ \ case \"NewCallback\":\n\
11793 \ (function() {\n\
11894 \ 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\
12296 \ var nThis = ++jsaddle_index;\n\
12397 \ jsaddle_values.set(nThis, this);\n\
12498 \ var args = [];\n\
@@ -127,41 +101,8 @@ runBatch send sendSync = "\
127101 \ jsaddle_values.set(nArg, arguments[i]);\n\
128102 \ args[i] = nArg;\n\
129103 \ }\n\
130− \ " <> send "{\"tag\": \"Callback\", \"contents\": [lastResults[0], lastResults[1], nFunction, nFunctionInFunc, nThis, args]}" <> "\n\
131− \ };\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\
104+ \ " <> send "{\"tag\": \"Callback\", \"contents\": [nFunction, nThis, args]}" <> "\n\
105+ \ })})();\n\
165106 \ break;\n\
166107 \ case \"CallAsFunction\":\n\
167108 \ var n = d.contents[3];\n\
@@ -204,15 +145,9 @@ runBatch send sendSync = "\
204145 \ jsaddle_values.set(n, d.contents[0].map(function(v){return jsaddle_values.get(v);}));\n\
205146 \ break;\n\
206147 \ case \"SyncWithAnimationFrame\":\n\
207− \ var n = d.contents;\n\
148+ \ var n = d.contents[0];\n\
208149 \ jsaddle_values.set(n, timestamp);\n\
209150 \ break;\n\
210− \ case \"StartSyncBlock\":\n\
211− \ syncDepth++;\n\
212− \ break;\n\
213− \ case \"EndSyncBlock\":\n\
214− \ syncDepth--;\n\
215− \ break;\n\
216151 \ default:\n\
217152 \ " <> send "{\"tag\": \"ProtocolError\", \"contents\": e.data}" <> "\n\
218153 \ return;\n\
@@ -247,7 +182,7 @@ runBatch send sendSync = "\
247182 \ (v === true ) ? [3, \"\"] :\n\
248183 \ (typeof v === \"number\") ? [-1, v.toString()] :\n\
249184 \ (typeof v === \"string\") ? [-2, v]\n\
250− \ : [-3, \"\"];\n\
185+ \ : [n, \"\"];\n\
251186 \ results.push({\"tag\": \"DeRefValResult\", \"contents\": c});\n\
252187 \ break;\n\
253188 \ case \"IsNull\":\n\
@@ -275,66 +210,55 @@ runBatch send sendSync = "\
275210 \ }\n\
276211 \ }\n\
277212 \ }\n\
278− \ if(syncDepth <= 0) {\n\
279− \ lastResults = [batch[2], {\"tag\": \"Success\", \"contents\": [callbacksToFree, results]}];\n\
280− \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [lastResults[0], lastResults[1]]}" <> "\n\
281− \ break;\n\
282− \ } else {\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\
309− \ break;\n"
310− ) <>
311− " }\n\
312− \ }\n\
213+ \ " <> send "{\"tag\": \"Success\", \"contents\": results}" <> "\n\
214+ \ }\n\
215+ \ catch (err) {\n\
216+ \ var n = ++jsaddle_index;\n\
217+ \ jsaddle_values.set(n, err);\n\
218+ \ " <> send "{\"tag\": \"Failure\", \"contents\": [results,n]}" <> "\n\
313219 \ }\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\
329220 \ };\n\
330− \ if(batch[1] && (initialSyncDepth || 0) === 0) {\n\
221+ \ if(batch[1]) {\n\
331222 \ window.requestAnimationFrame(processBatch);\n\
332223 \ }\n\
333224 \ else {\n\
334225 \ processBatch(window.performance ? window.performance.now() : null);\n\
335226 \ }\n\
336− \ };\n\
337− \ runBatch(batch);\n\
227+ \"
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\
338262 \"
339263
340264ghcjsHelpers :: ByteString
jsaddle/src/Language/Javascript/JSaddle/Types.hs+26−173
@@ -1,5 +1,6 @@
11{-# LANGUAGE CPP #-}
22#ifdef ghcjs_HOST_OS
3+{-# LANGUAGE ConstraintKinds #-}
34{-# OPTIONS_GHC -Wno-dodgy-exports #-}
45#else
56{-# LANGUAGE TypeFamilies #-}
@@ -7,14 +8,11 @@
78{-# LANGUAGE GeneralizedNewtypeDeriving #-}
89{-# LANGUAGE DeriveGeneric #-}
910{-# LANGUAGE DataKinds #-}
11+{-# LANGUAGE KindSignatures #-}
1012{-# LANGUAGE PolyKinds #-}
1113{-# LANGUAGE FlexibleContexts #-}
1214{-# LANGUAGE DefaultSignatures #-}
13−{-# LANGUAGE ImplicitParams #-}
1415#endif
15−{-# LANGUAGE ConstraintKinds #-}
16−{-# LANGUAGE KindSignatures #-}
17−{-# LANGUAGE AllowAmbiguousTypes #-}
1816-----------------------------------------------------------------------------
1917--
2018-- Module : Language.Javascript.JSaddle.Types
@@ -55,17 +53,8 @@ module Language.Javascript.JSaddle.Types (
5553 , Nullable(..)
5654 , JSCallAsFunction
5755
58− -- * Debugging
59− , JSadddleHasCallStack
60−
61− -- * Sync JSM
62− , syncPoint
63− , syncAfter
64−
65−#ifndef ghcjs_HOST_OS
66− , sendCommand
67−
6856 -- * JavaScript Context Commands
57+#ifndef ghcjs_HOST_OS
6958 , MutabilityType(..)
7059 , Mutable
7160 , Immutable
@@ -80,7 +69,6 @@ module Language.Javascript.JSaddle.Types (
8069 , Command(..)
8170 , Batch(..)
8271 , Result(..)
83− , BatchResults(..)
8472 , Results(..)
8573#endif
8674) where
@@ -95,27 +83,14 @@ import GHCJS.Nullable (Nullable(..))
9583import GHCJS.Prim.Internal (JSVal(..), JSValueRef)
9684import Data.JSString.Internal.Type (JSString(..))
9785import 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)
106−import Control.Monad.Trans.RWS.Lazy as Lazy (RWST(..))
107−import Control.Monad.Trans.RWS.Strict as Strict (RWST(..))
108−import Control.Monad.Trans.State.Lazy as Lazy (StateT(..))
109−import Control.Monad.Trans.State.Strict as Strict (StateT(..))
110−import Control.Monad.Trans.Writer.Lazy as Lazy (WriterT(..))
111−import Control.Monad.Trans.Writer.Strict as Strict (WriterT(..))
86+import Control.Monad.Trans.Reader (ReaderT(..))
87+import Control.Monad.Trans.State.Lazy (StateT(..))
88+import qualified Control.Monad.Trans.State.Strict as Strict
89+ (StateT(..))
11290import Control.Monad.Trans.Class (MonadTrans(..))
11391import Control.Monad.Fix (MonadFix)
11492import Control.Monad.Ref (MonadAtomicRef(..), MonadRef(..))
11593import Control.Concurrent.STM.TVar (TVar)
116−import Control.Concurrent.MVar (MVar)
117−import Data.Int (Int64)
118−import Data.Set (Set)
11994import Data.Text (Text)
12095import Data.Time.Clock (UTCTime(..))
12196import Data.Typeable (Typeable)
@@ -123,14 +98,6 @@ import Data.Coerce (coerce, Coercible)
12398import Data.Aeson
12499 (defaultOptions, genericToEncoding, ToJSON(..), FromJSON(..), Value)
125100import GHC.Generics (Generic)
126−import Control.Monad.IO.Unlift (MonadUnliftIO)
127−import qualified Control.Monad.Fail as Fail
128−#endif
129−
130−#if MIN_VERSION_base(4,9,0) && defined(CHECK_UNCHECKED)
131−import GHC.Stack (HasCallStack)
132−#else
133−import GHC.Exts (Constraint)
134101#endif
135102
136103-- | Identifies a JavaScript execution context.
@@ -141,16 +108,12 @@ import GHC.Exts (Constraint)
141108type JSContextRef = ()
142109#else
143110data JSContextRef = JSContextRef {
144− contextId :: Int64
145− , startTime :: UTCTime
146− , doSendCommand :: Command -> IO Result
147− , doSendAsyncCommand :: AsyncCommand -> IO ()
148− , addCallback :: Object -> JSCallAsFunction -> IO ()
149− , nextRef :: TVar JSValueRef
150− , doEnableLogging :: Bool -> IO ()
151− , finalizerThreads :: MVar (Set Text)
152− , animationFrameHandlers :: MVar [Double -> JSM ()]
153− , liveRefs :: MVar (Set Int64)
111+ startTime :: UTCTime
112+ , doSendCommand :: Command -> IO Result
113+ , doSendAsyncCommand :: AsyncCommand -> IO ()
114+ , addCallback :: Object -> JSCallAsFunction -> IO ()
115+ , freeCallback :: Object -> IO ()
116+ , nextRef :: TVar JSValueRef
154117}
155118#endif
156119
@@ -166,56 +129,9 @@ data JSContextRef = JSContextRef {
166129type JSM = IO
167130#else
168131newtype JSM a = JSM { unJSM :: ReaderT JSContextRef IO a }
169− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadThrow, MonadUnliftIO, Fail.MonadFail)
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
201−
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
132+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
211133#endif
212134
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
219135
220136-- | Type we can give to functions that are pure when using ghcjs, but
221137-- live in JSM when using jsaddle.
@@ -264,64 +180,21 @@ type MonadJSM = MonadIO
264180#else
265181class (Applicative m, MonadIO m) => MonadJSM m where
266182 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' #-}
183+ {-# MINIMAL liftJSM' #-}
271184
272185instance MonadJSM JSM where
273186 liftJSM' = id
274187 {-# INLINE liftJSM' #-}
275188
276−instance (MonadJSM m) => MonadJSM (ContT r 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
189+instance MonadJSM m => MonadJSM (ReaderT e m) where
285190 liftJSM' = lift . liftJSM'
286191 {-# INLINE liftJSM' #-}
287192
288−instance (MonadJSM m) => MonadJSM (IdentityT m) where
193+instance MonadJSM m => MonadJSM (StateT r m) where
289194 liftJSM' = lift . liftJSM'
290195 {-# INLINE liftJSM' #-}
291196
292−instance (MonadJSM m) => MonadJSM (ListT m) where
293− liftJSM' = lift . liftJSM'
294− {-# INLINE liftJSM' #-}
295−
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
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
197+instance MonadJSM m => MonadJSM (Strict.StateT r m) where
325198 liftJSM' = lift . liftJSM'
326199 {-# INLINE liftJSM' #-}
327200
@@ -396,7 +269,7 @@ type MutableJSArray = SomeJSArray Mutable
396269type STJSArray s = SomeJSArray (STMutable s)
397270
398271-- | See 'JavaScript.Object.Internal.Object'
399−newtype Object = Object JSVal
272+newtype Object = Object JSVal deriving(Show, ToJSON, FromJSON)
400273
401274-- | See 'GHCJS.Nullable.Nullable'
402275newtype Nullable a = Nullable a
@@ -420,8 +293,7 @@ newtype JSStringForSend = JSStringForSend Text deriving(Show, ToJSON, FromJSON,
420293instance NFData JSStringForSend
421294
422295-- | Command sent to a JavaScript context for execution asynchronously
423−data AsyncCommand = FreeRef Text JSValueForSend
424− | FreeRefs Text
296+data AsyncCommand = FreeRef JSValueForSend
425297 | SetPropertyByName JSObjectForSend JSStringForSend JSValueForSend
426298 | SetPropertyAtIndex JSObjectForSend Int JSValueForSend
427299 | StringToValue JSStringForSend JSValueForSend
@@ -432,15 +304,11 @@ data AsyncCommand = FreeRef Text JSValueForSend
432304 | CallAsFunction JSObjectForSend JSObjectForSend [JSValueForSend] JSValueForSend
433305 | CallAsConstructor JSObjectForSend [JSValueForSend] JSValueForSend
434306 | NewEmptyObject JSValueForSend
435− | NewAsyncCallback JSValueForSend
436− | NewSyncCallback JSValueForSend
437− | FreeCallback JSValueForSend
307+ | NewCallback JSValueForSend
438308 | NewArray [JSValueForSend] JSValueForSend
439309 | EvaluateScript JSStringForSend JSValueForSend
440310 | SyncWithAnimationFrame JSValueForSend
441− | StartSyncBlock
442− | EndSyncBlock
443− deriving (Show, Generic)
311+ deriving (Show, Generic)
444312
445313instance ToJSON AsyncCommand where
446314 toEncoding = genericToEncoding defaultOptions
@@ -472,7 +340,7 @@ instance FromJSON Command
472340instance NFData Command
473341
474342-- | Batch of commands that can be sent together to the JavaScript context
475−data Batch = Batch [Either AsyncCommand Command] Bool Int
343+data Batch = Batch [Either AsyncCommand Command] Bool
476344 deriving (Show, Generic)
477345
478346instance ToJSON Batch where
@@ -503,18 +371,9 @@ instance ToJSON Result where
503371
504372instance FromJSON Result
505373
506−data BatchResults = Success [JSValueReceived] [Result]
507− | Failure [JSValueReceived] [Result] JSValueReceived String
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]
374+data Results = Success [Result]
375+ | Failure [Result] JSValueReceived
376+ | Callback JSValueReceived JSValueReceived [JSValueReceived]
518377 | ProtocolError Text
519378 deriving (Show, Generic)
520379
@@ -524,11 +383,5 @@ instance ToJSON Results where
524383instance FromJSON Results
525384#endif
526385
527−-- | Like HasCallStack, but only when jsaddle cabal flag check-unchecked is set
528−#if MIN_VERSION_base(4,9,0) && defined(CHECK_UNCHECKED)
529−type JSadddleHasCallStack = HasCallStack
530−#else
531−type JSadddleHasCallStack = (() :: Constraint)
532−#endif
533386
534387
jsaddle/src/Language/Javascript/JSaddle/Value.hs+3−5
@@ -618,17 +618,15 @@ foreign import javascript unsafe "$r = ($1 === undefined)?0:\
618618 (typeof $1===\"string\")?4:\
619619 (typeof $1===\"object\")?5:-1;" jsrefGetType :: JSVal -> Int
620620#else
621−deRefVal value = do
622− v <- toJSVal value
623− result <- N.deRefVal v
624− return $ case result of
621+deRefVal value = toJSVal value >>= N.deRefVal >>= \result -> return $
622+ case result of
625623 DeRefValResult 0 _ -> ValNull
626624 DeRefValResult 1 _ -> ValUndefined
627625 DeRefValResult 2 _ -> ValBool False
628626 DeRefValResult 3 _ -> ValBool True
629627 DeRefValResult (-1) s -> ValNumber (read (T.unpack s))
630628 DeRefValResult (-2) s -> ValString s
631− DeRefValResult (-3) _ -> ValObject (Object v)
629+ DeRefValResult ref _ -> ValObject (Object (JSVal ref))
632630 _ -> error "Unexpected result dereferencing JSaddle value"
633631#endif
634632
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+14−3
@@ -6,8 +6,19 @@ GHCVER=`ghc --numeric-version`
66
77JSADDLE_WARP_VERSION=`head -n2 jsaddle-warp/jsaddle-warp.cabal | tail -n1 | sed 's/[^0-9.]//g'`
88
9+if [[ -d .cabal && -d .ghc ]]; then
10+ cp -a .cabal .ghc /root
11+fi
12+
13+npm install ws
14+
915cabal update
10−cabal new-build 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
11−cd 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+cabal new-build --constraint='Cabal>=1.24.2.0' 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
17+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 .
18+
19+# update the cache
20+rm -rf .cabal
21+cp -a /root/.cabal ./
22+rm -rf .ghc
23+cp -a /root/.ghc ./
1324
travis/trusty.Dockerfile+1−1
@@ -4,7 +4,7 @@ RUN apt-get update && \
44 apt-get -y install software-properties-common && \
55 add-apt-repository -y ppa:hvr/ghc && \
66 apt-get update && \
7− apt-get -y install happy-1.19.5 alex-3.1.7 phantomjs
7+ apt-get -y install happy-1.19.5 alex-3.1.7 nodejs npm
88
99RUN mkdir /build
1010WORKDIR /build
travis/vivid.Dockerfile+1−1
@@ -4,7 +4,7 @@ RUN apt-get update && \
44 apt-get -y install software-properties-common && \
55 add-apt-repository -y ppa:hvr/ghc && \
66 apt-get update && \
7− apt-get -y install happy-1.19.5 alex-3.1.7 phantomjs
7+ apt-get -y install happy-1.19.5 alex-3.1.7 nodejs npm
88
99RUN mkdir /build
1010WORKDIR /build
travis/wily.Dockerfile+1−1
@@ -4,7 +4,7 @@ RUN apt-get update && \
44 apt-get -y install software-properties-common && \
55 add-apt-repository -y ppa:hvr/ghc && \
66 apt-get update && \
7− apt-get -y install happy-1.19.5 alex-3.1.7 phantomjs
7+ apt-get -y install happy-1.19.5 alex-3.1.7 nodejs npm
88
99RUN mkdir /build
1010WORKDIR /build
travis/xenial.Dockerfile+1−1
@@ -4,7 +4,7 @@ RUN apt-get update && \
44 apt-get -y install software-properties-common && \
55 add-apt-repository -y ppa:hvr/ghc && \
66 apt-get update && \
7− apt-get -y install happy-1.19.5 alex-3.1.7 phantomjs
7+ apt-get -y install happy-1.19.5 alex-3.1.7 nodejs npm
88
99RUN mkdir /build
1010WORKDIR /build