on master help sign in
33 files changed+386−377
since splitsnapshots
unifiedsplit
.travis.yml+40−24 README.md+9−9 cabal.project+3−35 default.nix−8 jsaddle-clib/default.nix−9 jsaddle-clib/jsaddle-clib.cabal+4−3 jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hs+3−1 jsaddle-warp/default.nix−25 jsaddle-warp/jsaddle-warp.cabal+15−9 jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs+13−12 jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+63−26 jsaddle-warp/tests/DocTest.hs+20−7 jsaddle-webkit2gtk/default.nix−16 jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal+4−4 jsaddle-webkit2gtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+15−36 jsaddle-webkitgtk/default.nix−16 jsaddle-wkwebview/cbits-cocoa/AppDelegate.m+57−2 jsaddle-wkwebview/cbits-uikit/AppDelegate.m+14−1 jsaddle-wkwebview/default.nix+13−26 jsaddle-wkwebview/jsaddle-wkwebview.cabal+5−3 jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+43 jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+13 jsaddle/default.nix−23 jsaddle/jsaddle.cabal+14−15 jsaddle/src-ghc/JavaScript/Array.hs+2−2 jsaddle/src/Language/Javascript/JSaddle.hs+5−5 jsaddle/src/Language/Javascript/JSaddle/Evaluate.hs+1−1 jsaddle/src/Language/Javascript/JSaddle/Object.hs+5−6 jsaddle/src/Language/Javascript/JSaddle/Run.hs+2−1 jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+3−38 jsaddle/src/Language/Javascript/JSaddle/Types.hs+10−2 nix/hix.nix+8 travis/build.sh+2−12
.travis.yml+40−24
@@ -1,31 +1,47 @@
1−sudo: required
2−services:
3− - docker
1+language: nix
2+nix: 2.2.2
43
5−cache:
6− directories:
7− - .cabal
8− - .ghc
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
924
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=8.0.2 OSVER=xenial
14− - CABALVER=1.24 GHCVER=7.10.3 OSVER=trusty
15− - CABALVER=1.24 GHCVER=8.0.1 OSVER=trusty
16− - CABALVER=1.24 GHCVER=8.0.2 OSVER=trusty
17−
18−before_install:
19− - docker build -t ghc -f travis/$OSVER.Dockerfile travis
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
2029
2130script:
22− - docker run -v `pwd`:/build ghc
23− sh -x -c "
24− apt-get install -y cabal-install-$CABALVER ghc-$GHCVER &&
25− export PATH=\$HOME/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:/opt/alex/3.1.7/bin:/opt/happy/1.19.5/bin:\$PATH &&
26− ./travis/build.sh"
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
43+
2744notifications:
2845 irc:
2946 channels: "irc.freenode.net#ghcjs"
30− skip_join: true
31− email: true
47+ email: true
README.md+9−9
@@ -80,7 +80,7 @@ main = run 3708 $ do
8080 appendChild newParagraph text
8181 appendChild body (Just newParagraph)
8282 return ()
83−
83+
8484 -- Make an exit button
8585 exitMVar <- liftIO newEmptyMVar
8686 Just exit <- createElement doc (Just "span")
@@ -111,8 +111,8 @@ There are a number of different JSaddle runners to choose from
111111* [jsaddle-clib](https://hackage.haskell.org/package/jsaddle-clib) - C interface used to run JSaddle on Android using JNI.
112112
113113In all of these cases a web control or browser of some sort is used. An
114−[HTML file](https://github.com/ghcjs/jsaddle/blob/master/data/index.html)
115−and a [small JavaScript command interpreter](https://github.com/ghcjs/jsaddle/blob/master/data/jsaddle.js)
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)
116116are loaded into it. Then the native GHC compiled JSaddle code runs. In order to interact with the browser
117117it sends commands to the JavaScript command interpreter. The mechanism for sending the commands differs
118118for the different runners, but they are always combined into batches and encoded in JSON.
@@ -123,7 +123,7 @@ JavaScript thread until the Haskell callback completes) or asynchronous.
123123### Why use a JavaScript command interpreter?
124124
125125Older 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 WekKit2 interface this level of access is only available to WebKit Extensions.
126+WebKitGTK. With the newer WebKit2 interface this level of access is only available to WebKit Extensions.
127127The general advice for people migrating from WebKit1 to WebKit2 seems to be to use JavaScript.
128128
129129As a bonus we have been able to support a number of different platforms with the JavaScript command interpreter and it should
@@ -137,8 +137,8 @@ The first five keys `[0..4]` are
137137these values. Both the Haskell code and `jsaddle.js` can allocate new `JSVal` keys. The Haskell code allocates negative
138138keys and `jsaddle.js` allocates positive keys to avoid clashing.
139139
140−The haskell code will not know the value of the to go with the key, but it will include it in commands to the server
141−that are sort of "hey call this function and and put the result in the map with this key"). This allows for the
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
142142lazy execution of the JSaddle code.
143143
144144A finalizer is added on the Haskell code and an asynchronous `FreeJSVal` command is automatically sent to `jsaddle.js`
@@ -146,7 +146,7 @@ when the JSVal is no longer reachable. The `jsaddle.js` code then deletes the k
146146
147147### JSStrings
148148
149−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`.
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`.
150150
151151### Lazy Execution
152152
@@ -178,7 +178,7 @@ force all the pending asynchronous commands to be executed.
178178It 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.
179179
180180For 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`.
181+for your JavaScript code. This is the approach taken by `ghcjs-dom`.
182182
183183For instance here is `getElementById` from the `ghcjs-dom-jsffi` (used by `ghcjs-dom` when compiled with GHCJS)
184184
@@ -213,7 +213,7 @@ JSaddle does not support exceptions well. When compiled with GHCJS an exception
213213at the point the exception is thrown.
214214
215215When 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 the next synchronous command is executed.
216+(because the lazy execution of the JS code). The exception will be thrown when the next synchronous command is executed.
217217When it reaches the synchronous command a haskell type JSException will be thrown (this may not be the thread that initiated the
218218command that caused the exception). You can use `syncPoint` and `syncAfter` to force the exception to be thrown.
219219There are `JSM` versions of `catch` and `bracket` that also include a `syncPoint` call.
cabal.project+3−35
@@ -1,42 +1,10 @@
1−flags: -overloaded-methods -overloaded-signals -overloaded-properties
2−
31packages:
42 jsaddle/
53 jsaddle-warp/
64 jsaddle-wkwebview/
7−-- jsaddle-webkitgtk/
85 jsaddle-webkit2gtk/
96 jsaddle-clib/
107
11−package gi-atk
12− flags: -overloaded-methods -overloaded-signals -overloaded-properties
13−package gi-cairo
14− flags: -overloaded-methods -overloaded-signals -overloaded-properties
15−package gi-gdk
16− flags: -overloaded-methods -overloaded-signals -overloaded-properties
17−package gi-gdkpixbuf
18− flags: -overloaded-methods -overloaded-signals -overloaded-properties
19−package gi-gio
20− flags: -overloaded-methods -overloaded-signals -overloaded-properties
21−package gi-glib
22− flags: -overloaded-methods -overloaded-signals -overloaded-properties
23−package gi-gobject
24− flags: -overloaded-methods -overloaded-signals -overloaded-properties
25−package gi-gtk
26− flags: -overloaded-methods -overloaded-signals -overloaded-properties
27−package gi-gtk-hs
28− flags: -overloaded-methods -overloaded-signals -overloaded-properties
29−package gi-gtkosxapplication
30− flags: -overloaded-methods -overloaded-signals -overloaded-properties
31−package gi-gtksource
32− flags: -overloaded-methods -overloaded-signals -overloaded-properties
33−package gi-javascriptcore
34− flags: -overloaded-methods -overloaded-signals -overloaded-properties
35−package gi-pango
36− flags: -overloaded-methods -overloaded-signals -overloaded-properties
37−package gi-soup
38− flags: -overloaded-methods -overloaded-signals -overloaded-properties
39−package gi-webkit
40− flags: -overloaded-methods -overloaded-signals -overloaded-properties
41−package gi-webkit2
42− flags: -overloaded-methods -overloaded-signals -overloaded-properties
8+constraints: haskell-gi-overloading == 0.0
9+
10+allow-newer: websockets:random, splitmix:time, *:time, *:unix, *:process, stm:base
default.nixdeleted−8
@@ -1,8 +0,0 @@
1−haskellPackages: {
2− jsaddle = haskellPackages.callPackage ./jsaddle {};
3− jsaddle-warp = haskellPackages.callPackage ./jsaddle-warp {};
4− jsaddle-wkwebview = haskellPackages.callPackage ./jsaddle-wkwebview {};
5− jsaddle-webkit2gtk = haskellPackages.callPackage ./jsaddle-webkit2gtk {};
6− jsaddle-webkitgtk = haskellPackages.callPackage ./jsaddle-webkitgtk {};
7− jsaddle-clib = haskellPackages.callPackage ./jsaddle-clib {};
8−}
jsaddle-clib/default.nixdeleted−9
@@ -1,9 +0,0 @@
1−{ mkDerivation, aeson, base, bytestring, data-default, jsaddle, stdenv, text }:
2−mkDerivation {
3− pname = "jsaddle-clib";
4− version = "0.9.0.0";
5− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
6− libraryHaskellDepends = [ aeson base bytestring data-default jsaddle text ];
7− description = "Interface for JavaScript that works with GHCJS and GHC";
8− license = stdenv.lib.licenses.mit;
9−}
jsaddle-clib/jsaddle-clib.cabal+4−3
@@ -1,5 +1,5 @@
11name: jsaddle-clib
2−version: 0.9.0.0
2+version: 0.9.8.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -21,8 +21,9 @@ library
2121 Language.Javascript.JSaddle.CLib
2222 Language.Javascript.JSaddle.CLib.Internal
2323 build-depends:
24− aeson >=0.8.0.2 && <1.3,
24+ aeson >=0.8.0.2 && <1.6,
2525 base <5,
26+ base-compat >=0.9.0 && <0.12,
2627 bytestring >=0.10.6.0 && <0.11,
2728 jsaddle >= 0.9.0.0 && <0.10,
2829 data-default,
@@ -32,7 +33,7 @@ library
3233 Include-dirs: cbits/include
3334 Includes: jsaddle.h
3435 Install-includes: jsaddle.h
35− ghc-options: -ferror-spans -Wall -Werror
36+ ghc-options: -ferror-spans -Wall
3637 if impl(ghcjs)
3738 hs-source-dirs: src-ghcjs
3839 else
jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hs+3−1
@@ -8,6 +8,9 @@ module Language.Javascript.JSaddle.CLib
88 , appConfigToAppCallbacks
99 ) where
1010
11+import Prelude ()
12+import Prelude.Compat
13+
1114import Control.Monad (void)
1215import Control.Concurrent (forkIO)
1316
@@ -16,7 +19,6 @@ import Data.ByteString (useAsCString, packCString)
1619import Data.ByteString.Char8 (unpack)
1720import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
1821import Data.Default (def, Default)
19−import Data.Monoid ((<>))
2022import Data.Text (Text)
2123import qualified Data.Text.Encoding as T
2224
jsaddle-warp/default.nixdeleted−25
@@ -1,25 +0,0 @@
1−{ mkDerivation, aeson, base, bytestring, containers, deepseq
2−, doctest, filepath, foreign-store, ghc-prim, http-types, jsaddle
3−, lens, network, primitive, process, QuickCheck, ref-tf, stdenv
4−, stm, text, time, transformers, uuid, uuid-types, wai
5−, wai-websockets, warp, webdriver, websockets
6−}:
7−mkDerivation {
8− pname = "jsaddle-warp";
9− version = "0.9.4.0";
10− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
11− libraryHaskellDepends = [
12− aeson base bytestring containers foreign-store http-types jsaddle
13− stm text time transformers uuid uuid-types wai wai-websockets warp
14− websockets
15− ];
16− testHaskellDepends = [
17− aeson base bytestring containers deepseq doctest filepath ghc-prim
18− http-types jsaddle lens network primitive process QuickCheck ref-tf
19− stm text time transformers wai wai-websockets warp webdriver
20− websockets
21− ];
22− testTarget = "--test-option=${jsaddle.src}";
23− description = "Interface for JavaScript that works with GHCJS and GHC";
24− license = stdenv.lib.licenses.mit;
25−}
jsaddle-warp/jsaddle-warp.cabal+15−9
@@ -1,5 +1,5 @@
11name: jsaddle-warp
2−version: 0.9.5.0
2+version: 0.9.8.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 JavaScipt helper.
13+ drive a small JavaScript helper.
1414category: Web, Javascript
1515author: Hamish Mackenzie
1616
@@ -24,20 +24,22 @@ library
2424 if !impl(ghcjs -any)
2525 exposed-modules:
2626 Language.Javascript.JSaddle.WebSockets
27+ other-modules:
28+ Language.Javascript.JSaddle.WebSockets.Compat
2729 build-depends:
28− aeson >=0.8.0.2 && <1.3,
30+ aeson >=0.8.0.2 && <1.6,
2931 bytestring >=0.10.6.0 && <0.11,
30− containers >=0.5.6.2 && <0.6,
32+ containers >=0.5.6.2 && <0.7,
3133 foreign-store >=0.2 && <0.3,
3234 http-types >=0.8.6 && <0.13,
3335 jsaddle >=0.9.4.0 && <0.10,
34− stm >=2.4.4 && <2.5,
36+ stm >=2.4.4 && <2.6,
3537 text >=1.2.1.3 && <1.3,
36− time >=1.5.0.1 && <1.9,
38+ time >=1.5.0.1 && <1.11,
3739 transformers >=0.4.2.0 && <0.6,
3840 wai >=3.0.3.0 && <3.3,
3941 wai-websockets >=3.0.0.6 && <3.1,
40− warp >=3.1.2 && <3.3,
42+ warp >=3.1.2 && <3.4,
4143 websockets >=0.9.5.0 && <0.13
4244 exposed-modules:
4345 Language.Javascript.JSaddle.Warp
@@ -57,8 +59,10 @@ test-suite test-tool
5759 bytestring -any,
5860 containers -any,
5961 deepseq -any,
60− doctest >=0.10.1 && <0.12,
62+ doctest >=0.10.1 && <0.19,
63+ exceptions -any,
6164 filepath >=1.4.0.0 && <1.5,
65+ foreign-store -any,
6266 ghc-prim -any,
6367 http-types -any,
6468 jsaddle -any,
@@ -68,14 +72,16 @@ test-suite test-tool
6872 ref-tf -any,
6973 primitive -any,
7074 process >=1.2.3.0 && <1.7,
75+ random -any,
7176 stm -any,
7277 text -any,
7378 time -any,
7479 transformers -any,
80+ unliftio-core -any,
7581 wai -any,
7682 wai-websockets -any,
7783 warp -any,
78− webdriver >=0.8.4 && <0.9,
84+ webdriver >=0.8.4 && <0.10,
7985 websockets -any
8086 type: exitcode-stdio-1.0
8187 main-is: DocTest.hs
jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs+13−12
@@ -17,21 +17,22 @@ module Language.Javascript.JSaddle.Test (
1717 , listWindowProperties
1818) where
1919
20−import Control.Applicative
2120import Prelude hiding((!!), catch)
22−import 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 ((^.))
28−import Data.Monoid ((<>))
21+import Control.Applicative
22+import Control.Concurrent (threadDelay, forkIO, ThreadId)
2923import Control.Concurrent.MVar
3024 (tryTakeMVar, putMVar, takeMVar, newEmptyMVar, MVar)
31−import System.IO.Unsafe (unsafePerformIO)
32−import Control.Concurrent (threadDelay, forkIO, ThreadId)
25+import Control.Lens.Getter ((^.))
26+import Control.Monad (forever, forM)
27+import Control.Monad.IO.Class (MonadIO(..))
28+import Control.Monad.Trans.Reader (runReaderT)
29+import Data.Monoid ((<>))
3330import Data.Text (Text)
31+import qualified Data.Text as T
32+import Language.Javascript.JSaddle
3433import Language.Javascript.JSaddle.Warp (run)
34+import System.IO.Unsafe (unsafePerformIO)
35+import System.Timeout (timeout)
3536
3637context :: MVar JSContextRef
3738context = unsafePerformIO newEmptyMVar
@@ -52,12 +53,12 @@ startServer =
5253
5354-- >>> testJSaddle $ ((global ^. js "console" . js "log") # ["Hello"])
5455testJSaddle :: ToJSVal val => JSM val -> IO ()
55−testJSaddle f = do
56+testJSaddle f = timeout 10000000 (do
5657 startServer
5758 c <- takeMVar context
5859 runReaderT (unJSM $ (f >>= valToText >>= liftIO . putStrLn . T.unpack)
5960 `catch` \ (JSException e) -> valToText e >>= liftIO . putStrLn . T.unpack) c
60− putMVar context c
61+ putMVar context c) >>= maybe (putStrLn "testJSaddle timed out") return
6162
6263debugLog :: String -> IO ()
6364debugLog _ = return ()
jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+63−26
@@ -20,9 +20,12 @@ module Language.Javascript.JSaddle.WebSockets (
2020 , jsaddleApp
2121 , jsaddleWithAppOr
2222 , jsaddleAppWithJs
23+ , jsaddleAppWithJsOr
2324 , jsaddleAppPartial
2425 , jsaddleJs
26+ , jsaddleJs'
2527 , debug
28+ , debugOr
2629 , debugWrapper
2730) where
2831
@@ -57,45 +60,57 @@ import qualified Data.Map as M (empty, insert, lookup)
5760import Data.IORef
5861 (readIORef, newIORef, atomicModifyIORef')
5962import Data.ByteString.Lazy (ByteString)
60−import qualified Data.ByteString.Lazy as LBS (stripPrefix)
63+import qualified Data.ByteString.Lazy as LBS
64+ (toStrict, stripPrefix)
6165import Control.Concurrent.MVar
6266 (tryTakeMVar, MVar, tryPutMVar, modifyMVar_, putMVar, takeMVar,
6367 readMVar, newMVar, newEmptyMVar, modifyMVar)
6468import Network.Wai.Handler.Warp
6569 (defaultSettings, setTimeout, setPort, runSettings)
66−import Foreign.Store (newStore, readStore, lookupStore, writeStore, Store(..))
70+import Foreign.Store (readStore, lookupStore, writeStore, Store(..))
6771import Language.Javascript.JSaddle (askJSM)
6872import Control.Monad.IO.Class (MonadIO(..))
6973
7074import Language.Javascript.JSaddle.WebSockets.Compat (getTextMessageByteString)
75+import qualified Data.Text.Encoding as T (decodeUtf8)
7176
7277jsaddleOr :: ConnectionOptions -> JSM () -> Application -> IO Application
7378jsaddleOr opts entryPoint otherApp = do
7479 syncHandlers <- newIORef M.empty
80+ asyncHandlers <- newIORef M.empty
7581 let wsApp :: ServerApp
7682 wsApp pending_conn = do
7783 conn <- acceptRequest pending_conn
78− rec (processResult, processSyncResult, start) <- runJavaScript (sendTextData conn . encode) $ do
79− syncKey <- T.pack . show . contextId <$> askJSM
80− liftIO $ atomicModifyIORef' syncHandlers (\m -> (M.insert syncKey processSyncResult m, ()))
81− liftIO $ sendTextData conn (encode syncKey)
82− entryPoint
83− _ <- forkIO . forever $
84− receiveDataMessage conn >>= \msg -> case getTextMessageByteString msg of
85− Just t ->
86− case decode t of
87− Nothing -> error $ "jsaddle Results decode failed : " <> show t
88− Just r -> processResult r
89− _ -> error "jsaddle WebSocket unexpected binary data"
90− start
91− waitTillClosed 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"
106+ _ -> error "jsaddle WebSocket unexpected binary data"
92107
93108 -- Based on Network.WebSocket.forkPingThread
94109 waitTillClosed conn = ignore `handle` go 1
95110 where
96111 go :: Int -> IO ()
97112 go i = do
98− threadDelay (1 * 1000 * 1000)
113+ threadDelay (10 * 1000 * 1000)
99114 sendPing conn (T.pack $ show i)
100115 go (i + 1)
101116
@@ -128,9 +143,14 @@ jsaddleApp = jsaddleAppWithJs $ jsaddleJs False
128143
129144jsaddleAppWithJs :: ByteString -> Application
130145jsaddleAppWithJs js req sendResponse =
131− fromMaybe
132− (sendResponse $ W.responseLBS H.status403 [("Content-Type", "text/plain")] "Forbidden")
133− (jsaddleAppPartialWithJs 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)
134154
135155jsaddleWithAppOr :: ConnectionOptions -> JSM () -> Application -> IO Application
136156jsaddleWithAppOr opts entryPoint otherApp = jsaddleOr opts entryPoint $ \req sendResponse ->
@@ -156,7 +176,7 @@ jsaddleJs = jsaddleJs' Nothing
156176-- sed -e 's|\\|\\\\|g' -e 's|^| \\|' -e 's|$|\\n\\|' -e 's|"|\\"|g' data/jsaddle.js | pbcopy
157177jsaddleJs' :: Maybe ByteString -> Bool -> ByteString
158178jsaddleJs' jsaddleUri refreshOnLoad = "\
159− \if(typeof global !== \"undefined\") {\n\
179+ \if(typeof global !== \"undefined\" && typeof require === \"function\") {\n\
160180 \ global.window = global;\n\
161181 \ global.WebSocket = require('ws');\n\
162182 \}\n\
@@ -168,20 +188,30 @@ jsaddleJs' jsaddleUri refreshOnLoad = "\
168188 (jsaddleUri >>= LBS.stripPrefix "http")
169189 <> ";\n\
170190 \\n\
171− \ var ws = new WebSocket(wsaddress);\n\
191+ \ var ws0 = new WebSocket(wsaddress);\n\
172192 \ var syncKey = \"\";\n\
173193 \\n\
174− \ ws.onopen = function(e) {\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\
175198 \ " <> initState <> "\n\
176199 \\n\
177− \ ws.onmessage = function(e) {\n\
200+ \ ws0.onmessage = function(e) {\n\
178201 \ var batch = JSON.parse(e.data);\n\
179202 \ if(inCallback > 0) {\n\
180203 \ asyncBatch = batch;\n\
181204 \ return;\n\
182205 \ }\n\
183206 \ if(typeof batch === \"string\") {\n\
184− \ syncKey = batch;\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" <>
185215 (if refreshOnLoad
186216 then " var xhr = new XMLHttpRequest();\n\
187217 \ xhr.open('POST', '/reload/'+syncKey, true);\n\
@@ -203,7 +233,7 @@ jsaddleJs' jsaddleUri refreshOnLoad = "\
203233 \ return JSON.parse(xhr.response);})()")) <> "\
204234 \ };\n\
205235 \ };\n\
206− \ ws.onerror = function() {\n\
236+ \ ws0.onerror = function() {\n\
207237 \ setTimeout(connect, 1000);\n\
208238 \ };\n\
209239 \}\n\
@@ -222,6 +252,13 @@ debug port f = do
222252 jsaddleOr defaultConnectionOptions (registerContext >> f >> syncPoint) (withRefresh $ jsaddleAppWithJs $ jsaddleJs True)
223253 putStrLn $ "<a href=\"http://localhost:" <> show port <> "\">run</a>"
224254
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+
225262refreshMiddleware :: ((Response -> IO ResponseReceived) -> IO ResponseReceived) -> Middleware
226263refreshMiddleware refresh otherApp req sendResponse = case (W.requestMethod req, W.pathInfo req) of
227264 ("POST", ["reload", _syncKey]) -> refresh sendResponse
jsaddle-warp/tests/DocTest.hs+20−7
@@ -11,18 +11,17 @@ 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 (forkIO)
14+import Control.Concurrent
15+ (takeMVar, newEmptyMVar, forkIO, threadDelay, putMVar)
1516import Control.Monad (void)
1617import Control.Monad.IO.Class (MonadIO(..))
1718import Data.Monoid ((<>))
1819import System.Environment (getArgs)
19−import Language.Javascript.JSaddle.Run.Files (jsaddleJs)
20−import qualified Data.ByteString.Lazy.Char8 as BS (unpack)
2120import Test.WebDriver (runSession, defaultConfig, openPage, closeSession)
2221
2322main :: IO ()
2423main = do
25− putStrLn "Hello"
24+ putStrLn "Testing JSaddle"
2625 jsaddlePath <- getArgs >>= \case
2726 [arg] -> return arg
2827 _ -> do
@@ -30,14 +29,18 @@ main = do
3029 exitFailure
3130 hSetBuffering stdout LineBuffering
3231 hSetBuffering stderr LineBuffering
32+ putStrLn "Checking for phantomjs"
3333 node <- system "phantomjs --version" >>= \case
3434 ExitSuccess -> return ()
3535 e -> do
3636 putStrLn "phantomjs not found"
3737 exitWith e
38− forkIO . void $ readProcess "phantomjs" ["--webdriver=4444"] (BS.unpack jsaddleJs) >>= putStr
39− runSession defaultConfig $ do
40− openPage "http://localhost:3709"
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
4144 liftIO $ doctest [
4245 "-hide-all-packages",
4346 "-package=base-" ++ VERSION_base,
@@ -61,6 +64,10 @@ main = do
6164 "-package=ref-tf-" ++ VERSION_ref_tf,
6265 "-package=deepseq-" ++ VERSION_deepseq,
6366 "-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,
6471 "-i" <> "src",
6572 "-i" <> "src-ghc",
6673 "src/Language/Javascript/JSaddle/Test.hs",
@@ -83,6 +90,7 @@ main = do
8390 jsaddlePath </> "src/Language/Javascript/JSaddle/Arguments.hs",
8491 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes.hs",
8592 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes/Internal.hs",
93+ jsaddlePath </> "src/Language/Javascript/JSaddle/Debug.hs",
8694 jsaddlePath </> "src/Language/Javascript/JSaddle/Evaluate.hs",
8795 jsaddlePath </> "src/Language/Javascript/JSaddle/Exception.hs",
8896 jsaddlePath </> "src/Language/Javascript/JSaddle/Monad.hs",
@@ -96,4 +104,9 @@ main = do
96104 jsaddlePath </> "src/Language/Javascript/JSaddle/String.hs",
97105 jsaddlePath </> "src/Language/Javascript/JSaddle/Types.hs",
98106 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
99112 closeSession
jsaddle-webkit2gtk/default.nixdeleted−16
@@ -1,16 +0,0 @@
1−{ mkDerivation, aeson, base, bytestring, directory, gi-gio, gi-glib
2−, gi-gtk, gi-javascriptcore, gi-webkit2, haskell-gi-base, jsaddle
3−, stdenv, text, unix, webkit2gtk3-javascriptcore
4−}:
5−mkDerivation {
6− pname = "jsaddle-webkit2gtk";
7− version = "0.9.4.0";
8− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
9− libraryHaskellDepends = [
10− aeson base bytestring directory gi-gio gi-glib gi-gtk
11− gi-javascriptcore gi-webkit2 haskell-gi-base jsaddle text unix
12− webkit2gtk3-javascriptcore
13− ];
14− description = "Interface for JavaScript that works with GHCJS and GHC";
15− license = stdenv.lib.licenses.mit;
16−}
jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal+4−4
@@ -1,5 +1,5 @@
11name: jsaddle-webkit2gtk
2−version: 0.9.4.0
2+version: 0.9.8.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -26,7 +26,7 @@ library
2626 base <5
2727 if !impl(ghcjs -any)
2828 build-depends:
29− aeson >=0.8.0.2 && <1.3,
29+ aeson >=0.8.0.2 && <1.6,
3030 base <5,
3131 bytestring >=0.10.6.0 && <0.11,
3232 directory >=1.0.0.2 && <1.4,
@@ -35,8 +35,8 @@ library
3535 gi-gtk >=3.0.17 && <3.1,
3636 gi-webkit2 >=4.0.14 && <4.1,
3737 gi-javascriptcore >=4.0.14 && <4.1,
38− haskell-gi-base >=0.20 && <0.22,
39− haskell-gi-overloading ==0.0.*,
38+ haskell-gi-base >=0.20 && <0.26,
39+ haskell-gi-overloading >=0.0 && < 2.0,
4040 jsaddle >=0.9.4.0 && <0.10,
4141 text >=1.2.1.3 && <1.3,
4242 webkit2gtk3-javascriptcore >=0.14.0.0 && <0.15
jsaddle-webkit2gtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+15−36
@@ -43,29 +43,24 @@ import Data.Monoid ((<>))
4343import Data.ByteString.Lazy (toStrict, fromStrict)
4444import qualified Data.ByteString.Lazy as LB (ByteString)
4545import Data.Aeson (encode, decode)
46−import Data.Text (Text)
4746import qualified Data.Text as T (pack)
48−import Data.Text.Foreign (fromPtr)
4947import Data.Text.Encoding (decodeUtf8, encodeUtf8)
5048
51−import Foreign.Ptr (castPtr, nullPtr)
52−
5349import Data.GI.Base.BasicTypes (GObject)
5450import Data.GI.Base.Signals
5551 (connectSignalFunPtr, SignalConnectMode(..), SignalConnectMode,
5652 SignalHandlerId)
57−import Data.GI.Base.ManagedPtr (withManagedPtr)
5853
5954import GI.GLib (timeoutAdd, idleAdd, pattern PRIORITY_HIGH, pattern PRIORITY_DEFAULT)
6055import qualified GI.Gtk as Gtk (main, init)
6156import GI.Gtk
6257 (windowSetPosition, windowSetDefaultSize, windowNew,
63− scrolledWindowNew, noAdjustment, containerAdd,
58+ scrolledWindowNew, Adjustment, containerAdd,
6459 WindowType(..), WindowPosition(..), widgetDestroy,
6560 widgetGetToplevel, widgetShowAll, onWidgetDestroy,
6661 mainQuit)
67−import GI.Gio (noCancellable)
68−import GI.JavaScriptCore (Value(..), GlobalContext(..))
62+import GI.Gio (Cancellable)
63+import GI.JavaScriptCore (valueToString)
6964import GI.WebKit2
7065 (scriptDialogPromptSetText, scriptDialogPromptGetDefaultText,
7166 scriptDialogGetMessage, scriptDialogGetDialogType,
@@ -74,7 +69,7 @@ import GI.WebKit2
7469 setSettingsEnableJavascript, webViewNewWithUserContentManager,
7570 userContentManagerNew,
7671 userContentManagerRegisterScriptMessageHandler,
77− javascriptResultGetValue, javascriptResultGetGlobalContext,
72+ javascriptResultGetJsValue,
7873 webViewGetUserContentManager,
7974 mk_UserContentManagerScriptMessageReceivedCallback,
8075 wrap_UserContentManagerScriptMessageReceivedCallback,
@@ -83,17 +78,16 @@ import GI.WebKit2
8378 onWebViewLoadChanged, setSettingsEnableDeveloperExtras,
8479 webViewSetSettings, webViewGetSettings, ScriptDialogType(..))
8580
86−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
87− (JSValueRef, JSContextRef)
88−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
89− (jsstringgetcharactersptr, jsstringgetlength, jsstringrelease)
90−import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
91− (jsvaluetostringcopy)
92−
9381import Language.Javascript.JSaddle (JSM, Results, Batch)
9482import Language.Javascript.JSaddle.Run (runJavaScript)
9583import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
9684
85+noAdjustment :: Maybe Adjustment
86+noAdjustment = Nothing
87+
88+noCancellable :: Maybe Cancellable
89+noCancellable = Nothing
90+
9791quitWebView :: WebView -> IO ()
9892quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
9993 widgetDestroy w
@@ -109,21 +103,6 @@ postGUIAsync :: IO () -> IO ()
109103postGUIAsync action =
110104 void . idleAdd PRIORITY_DEFAULT $ action >> return False
111105
112−withJSContextRef :: GlobalContext -> (JSContextRef -> IO a) -> IO a
113−withJSContextRef (GlobalContext ctx) f = withManagedPtr ctx (f . castPtr)
114−
115−withJSValueRef :: Value -> (JSValueRef -> IO a) -> IO a
116−withJSValueRef (Value ptr) f = withManagedPtr ptr (f . castPtr)
117−
118−valueToText :: JSContextRef -> JSValueRef -> IO Text
119−valueToText ctxRef s = do
120− jsstring <- jsvaluetostringcopy ctxRef s nullPtr
121− l <- jsstringgetlength jsstring
122− p <- jsstringgetcharactersptr jsstring
123− result <- fromPtr (castPtr p) (fromIntegral l)
124− jsstringrelease jsstring
125− return result
126−
127106run :: JSM () -> IO ()
128107run main = do
129108 _ <- Gtk.init Nothing
@@ -172,16 +151,16 @@ connectUserContentManagerScriptMessageReceived obj cb after = liftIO $ do
172151 let cb' = wrap_UserContentManagerScriptMessageReceivedCallback cb
173152 cb'' <- mk_UserContentManagerScriptMessageReceivedCallback cb'
174153 connectSignalFunPtr obj "script-message-received::jsaddle" cb'' after
154+#if MIN_VERSION_haskell_gi_base(0,23,0)
155+ Nothing
156+#endif
175157
176158addJSaddleHandler :: WebView -> (Results -> IO ()) -> (Results -> IO Batch) -> IO ()
177159addJSaddleHandler webView processResult syncResults = do
178160 manager <- webViewGetUserContentManager webView
179161 _ <- onUserContentManagerScriptMessageReceived manager $ \result -> do
180− ctx <- javascriptResultGetGlobalContext result
181− arg <- javascriptResultGetValue result
182− bs <- withJSContextRef ctx $ \ctxRef ->
183− withJSValueRef arg $ \argRef ->
184− encodeUtf8 <$> valueToText ctxRef argRef
162+ arg <- javascriptResultGetJsValue result
163+ bs <- encodeUtf8 <$> valueToString arg
185164 mapM_ processResult (decode (fromStrict bs))
186165 _ <- onWebViewScriptDialog webView $ \dialog ->
187166 scriptDialogGetDialogType dialog >>= \case
jsaddle-webkitgtk/default.nixdeleted−16
@@ -1,16 +0,0 @@
1−{ mkDerivation, aeson, base, bytestring, directory, gi-glib, gi-gtk
2−, gi-javascriptcore, gi-webkit, haskell-gi-base, jsaddle, stdenv
3−, text, unix, webkitgtk3-javascriptcore
4−}:
5−mkDerivation {
6− pname = "jsaddle-webkitgtk";
7− version = "0.9.0.0";
8− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
9− libraryHaskellDepends = [
10− aeson base bytestring directory gi-glib gi-gtk gi-javascriptcore
11− gi-webkit haskell-gi-base jsaddle text unix
12− webkitgtk3-javascriptcore
13− ];
14− description = "Interface for JavaScript that works with GHCJS and GHC";
15− license = stdenv.lib.licenses.mit;
16−}
jsaddle-wkwebview/cbits-cocoa/AppDelegate.m+57−2
@@ -2,6 +2,9 @@
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);
58extern void callWithWebView(WKWebView *, HsStablePtr);
69
710@interface AppDelegate : NSObject <NSApplicationDelegate>
@@ -11,6 +14,12 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
1114- (instancetype)initApp:(HsStablePtr)handler progName:(NSString *)progName;
1215@end
1316
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+
1423@implementation AppDelegate
1524
1625- (void)applicationWillTerminate:(NSNotification *)aNotification {
@@ -31,18 +40,39 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
3140 return self;
3241}
3342
43+-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
44+ bool b = callWithCStringReturningBool([filename UTF8String], global_applicationOpenFile);
45+ return b ? YES : NO;
46+}
47+
3448-(void)applicationWillFinishLaunching:(NSNotification *)notification {
3549 WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
36− [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
50+ if (global_developerExtrasEnabled) {
51+ [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
52+ }
3753 WKWebView *webView = [[WKWebView alloc] initWithFrame: [_window.contentView frame] configuration:theConfiguration];
3854 [_window setContentView:webView];
3955 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);
4069}
4170
4271-(void)applicationDidFinishLaunching:(NSNotification *)notification {
4372 [_window orderFrontRegardless];
4473 [_window center];
4574 [NSApp activateIgnoringOtherApps:YES];
75+ callIO(global_didFinishLaunchingWithOptions);
4676}
4777
4878-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
@@ -51,8 +81,33 @@ extern void callWithWebView(WKWebView *, HsStablePtr);
5181
5282@end
5383
54−void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
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) {
55105 @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;
56111 NSApplication *application = [NSApplication sharedApplication];
57112 AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
58113 [application setDelegate:appDelegate];
jsaddle-wkwebview/cbits-uikit/AppDelegate.m+14−1
@@ -5,6 +5,7 @@
55
66extern void callIO(HsStablePtr);
77extern void callWithCString(const char * _Nonnull, HsStablePtr);
8+extern void callWithCIntCString(int n, const char * _Nonnull, HsStablePtr);
89
910@interface AppDelegate ()
1011
@@ -20,6 +21,7 @@ HsStablePtr global_applicationWillEnterForeground = 0;
2021HsStablePtr global_applicationWillTerminate = 0;
2122HsStablePtr global_applicationSignificantTimeChange = 0;
2223HsStablePtr global_applicationUniversalLink = 0;
24+HsStablePtr global_applicationDidReceiveRemoteNotification = 0;
2325uint64_t global_requestAuthorizationWithOptions = 0;
2426uint64_t global_requestAuthorizationOptionBadge = 0;
2527uint64_t global_requestAuthorizationOptionSound = 0;
@@ -115,6 +117,14 @@ HsStablePtr global_didFailToRegisterForRemoteNotificationsWithError = 0;
115117 if ([userInfo valueForKeyPath:@"aps.badge"] != nil) {
116118 [UIApplication sharedApplication].applicationIconBadgeNumber=[[[userInfo objectForKey:@"aps"] objectForKey:@"badge"] intValue];
117119 }
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+ }
118128 completionHandler(UIBackgroundFetchResultNewData);
119129}
120130
@@ -152,6 +162,7 @@ void runInWKWebView(HsStablePtr handler,
152162 HsStablePtr hs_applicationWillTerminate,
153163 HsStablePtr hs_applicationSignificantTimeChange,
154164 HsStablePtr hs_applicationUniversalLink,
165+ HsStablePtr hs_applicationDidReceiveRemoteNotification,
155166 const uint64_t hs_requestAuthorizationWithOptions,
156167 const uint64_t hs_requestAuthorizationOptionBadge,
157168 const uint64_t hs_requestAuthorizationOptionSound,
@@ -159,7 +170,8 @@ void runInWKWebView(HsStablePtr handler,
159170 const uint64_t hs_requestAuthorizationOptionCarPlay,
160171 const uint64_t hs_registerForRemoteNotifications,
161172 HsStablePtr hs_didRegisterForRemoteNotificationsWithDeviceToken,
162− HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError) {
173+ HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError,
174+ const uint64_t hs_developerExtrasEnabled) {
163175 @autoreleasepool {
164176 globalHandler = handler;
165177 global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
@@ -171,6 +183,7 @@ void runInWKWebView(HsStablePtr handler,
171183 global_applicationWillTerminate = hs_applicationWillTerminate;
172184 global_applicationSignificantTimeChange = hs_applicationSignificantTimeChange;
173185 global_applicationUniversalLink = hs_applicationUniversalLink;
186+ global_applicationDidReceiveRemoteNotification = hs_applicationDidReceiveRemoteNotification;
174187 global_requestAuthorizationWithOptions = hs_requestAuthorizationWithOptions;
175188 global_requestAuthorizationOptionBadge = hs_requestAuthorizationOptionBadge;
176189 global_requestAuthorizationOptionSound = hs_requestAuthorizationOptionSound;
jsaddle-wkwebview/default.nix+13−26
@@ -1,27 +1,14 @@
1−{ mkDerivation, stdenv, aeson, base, bytestring, jsaddle, data-default
2−, buildPackages, hostPlatform
3−}:
4−
1+{ filterSource ? (x: x) }:
2+self: super:
53let
6− platform = if hostPlatform.useiOSPrebuilt
7− then hostPlatform.xcodePlatform
8− else "MacOSX";
9− sdk = "${buildPackages.darwin.xcode}/Contents/Developer/Platforms/${platform}.platform/Developer/SDKs/${platform}.sdk";
10−
11−in mkDerivation {
12− pname = "jsaddle-wkwebview";
13− version = "0.9.4.0";
14− src = ./.;
15− libraryHaskellDepends = [ aeson base bytestring jsaddle data-default ];
16−
17− # HACK(matthewbauer): This overrides the builtin frameworks from
18− # Nixpkgs. There should be a more elegant way to do this without
19− # breaking other cflags - but haven’t found it yet.
20− preBuild = ''
21− NIX_CFLAGS_COMPILE="-F${sdk}/System/Library/Frameworks"
22− '';
23−
24− libraryFrameworkDepends = [ "${sdk}/System" ];
25− description = "Interface for JavaScript that works with GHCJS and GHC";
26− license = stdenv.lib.licenses.mit;
27−}
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+5−3
@@ -1,5 +1,5 @@
11name: jsaddle-wkwebview
2−version: 0.9.4.0
2+version: 0.9.8.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -29,13 +29,15 @@ library
2929 base <5
3030 default-language: Haskell2010
3131 hs-source-dirs: src
32− frameworks: Foundation, WebKit
3332 ghc-options: -ferror-spans -Wall
33+ if os(linux)
34+ buildable: False
3435 if impl(ghcjs)
3536 hs-source-dirs: src-ghcjs
3637 else
38+ frameworks: Foundation, WebKit
3739 build-depends:
38− aeson >=0.8.0.2 && <1.3,
40+ aeson >=0.8.0.2 && <1.6,
3941 bytestring >=0.10.6.0 && <0.11,
4042 directory,
4143 jsaddle >= 0.9.4.0 && <0.10,
jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+43
@@ -9,6 +9,7 @@ module Language.Javascript.JSaddle.WKWebView
99 , runHTMLWithBaseURL
1010 , runFile
1111 , mainBundleResourcePath
12+ , ApplicationState (..)
1213 , AppDelegateConfig (..)
1314 , AppDelegateNotificationConfig (..)
1415 , AuthorizationOption (..)
@@ -24,6 +25,7 @@ import Language.Javascript.JSaddle.WKWebView.Internal
2425 (jsaddleMain, jsaddleMainHTMLWithBaseURL, jsaddleMainFile, WKWebView(..), mainBundleResourcePath)
2526import System.Environment (getProgName)
2627import Foreign.C.String (CString, withCString)
28+import Foreign.C.Types (CBool, CInt)
2729import Foreign.StablePtr (StablePtr, newStablePtr)
2830import Language.Javascript.JSaddle (JSM)
2931
@@ -39,6 +41,7 @@ foreign import ccall runInWKWebView
3941 -> StablePtr (IO ()) -- applicationWillTerminate
4042 -> StablePtr (IO ()) -- applicationSignificantTimeChange
4143 -> StablePtr (CString -> IO ()) -- applicationUniversalLink
44+ -> StablePtr (CInt -> CString -> IO ()) -- applicationDidReceiveRemoteNotification
4245 -> Word64 -- whether to run requestAuthorizationWithOptions
4346 -> Word64 -- Ask for Badge authorization
4447 -> Word64 -- Ask for Sound authorization
@@ -47,6 +50,8 @@ foreign import ccall runInWKWebView
4750 -> Word64 -- registerForRemoteNotifications
4851 -> StablePtr (CString -> IO ()) -- didRegisterForRemoteNotificationsWithDeviceToken
4952 -> StablePtr (CString -> IO ()) -- didFailToRegisterForRemoteNotificationsWithError
53+ -> Word64 -- developerExtrasEnabled
54+ -> StablePtr (CString -> IO CBool) -- applicationOpenFile
5055 -> IO ()
5156
5257data AppDelegateConfig = AppDelegateConfig
@@ -59,7 +64,25 @@ data AppDelegateConfig = AppDelegateConfig
5964 , _appDelegateConfig_applicationWillTerminate :: IO ()
6065 , _appDelegateConfig_applicationSignificantTimeChange :: IO ()
6166 , _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
6276 , _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.
6386 }
6487
6588instance Default AppDelegateConfig where
@@ -73,7 +96,10 @@ instance Default AppDelegateConfig where
7396 , _appDelegateConfig_applicationWillTerminate = return ()
7497 , _appDelegateConfig_applicationSignificantTimeChange = return ()
7598 , _appDelegateConfig_applicationUniversalLink = \_ -> return ()
99+ , _appDelegateConfig_applicationDidReceiveRemoteNotification = \_ _ -> return ()
76100 , _appDelegateConfig_appDelegateNotificationConfig = def
101+ , _appDelegateConfig_developerExtrasEnabled = True
102+ , _appDelegateConfig_applicationOpenFile = \_ -> return False
77103 }
78104
79105data AuthorizationOption = AuthorizationOption_Badge
@@ -82,6 +108,12 @@ data AuthorizationOption = AuthorizationOption_Badge
82108 | AuthorizationOption_CarPlay
83109 deriving (Show, Read, Eq, Ord)
84110
111+data ApplicationState = ApplicationState_Active
112+ | ApplicationState_Inactive
113+ | ApplicationState_Background
114+ | ApplicationState_Unknown CInt
115+ deriving (Show, Read, Eq, Ord)
116+
85117data AppDelegateNotificationConfig = AppDelegateNotificationConfig
86118 { _appDelegateNotificationConfig_requestAuthorizationWithOptions :: Maybe (Set AuthorizationOption)
87119 , _appDelegateNotificationConfig_registerForRemoteNotifications :: Bool
@@ -144,6 +176,14 @@ run' cfg main = do
144176 applicationWillTerminate <- newStablePtr $ _appDelegateConfig_applicationWillTerminate cfg
145177 applicationSignificantTimeChange <- newStablePtr $ _appDelegateConfig_applicationSignificantTimeChange cfg
146178 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
147187
148188 -- AppDelegate notification configuration
149189 let ncfg = _appDelegateConfig_appDelegateNotificationConfig cfg
@@ -166,6 +206,7 @@ run' cfg main = do
166206 applicationWillTerminate
167207 applicationSignificantTimeChange
168208 applicationUniversalLink
209+ applicationDidReceiveRemoteNotification
169210 (fromBool requestAuthorizationWithOptions)
170211 (fromBool $ Set.member AuthorizationOption_Badge authorizationOptions)
171212 (fromBool $ Set.member AuthorizationOption_Sound authorizationOptions)
@@ -174,3 +215,5 @@ run' cfg main = do
174215 (fromBool registerForRemoteNotifications)
175216 didRegisterForRemoteNotificationsWithDeviceToken
176217 didFailToRegisterForRemoteNotificationsWithError
218+ (fromBool $ _appDelegateConfig_developerExtrasEnabled cfg)
219+ applicationOpenFile
jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+13
@@ -20,6 +20,7 @@ import qualified Data.Text as T (pack)
2020import Data.Text.Encoding (encodeUtf8)
2121
2222import Foreign.C.String (CString)
23+import Foreign.C.Types (CBool(..), CInt(..))
2324import Foreign.Ptr (Ptr, nullPtr)
2425import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
2526
@@ -36,7 +37,9 @@ foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO ()
3637foreign export ccall jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
3738foreign export ccall jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()
3839foreign export ccall callWithWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
40+foreign export ccall callWithCIntCString :: CInt -> CString -> StablePtr (CInt -> CString -> IO ()) -> IO ()
3941foreign export ccall callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
42+foreign export ccall callWithCStringReturningBool :: CString -> StablePtr (CString -> IO CBool) -> IO CBool
4043foreign export ccall callIO :: StablePtr (IO ()) -> IO ()
4144foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> StablePtr (Results -> IO Batch) -> IO ()
4245foreign import ccall loadHTMLStringWithBaseURL :: WKWebView -> CString -> CString -> IO ()
@@ -126,6 +129,16 @@ callWithCString c fptr = do
126129 f <- deRefStablePtr fptr
127130 f c
128131
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+
129142callIO :: StablePtr (IO ()) -> IO ()
130143callIO ptr = do
131144 f <- deRefStablePtr ptr
jsaddle/default.nixdeleted−23
@@ -1,23 +0,0 @@
1−{ mkDerivation, aeson, attoparsec, base, base64-bytestring
2−, bytestring, containers, deepseq, filepath, ghc-prim, http-types
3−, lens, primitive, process, random, ref-tf, scientific, stdenv, stm
4−, text, time, transformers, unordered-containers, unliftio-core
5−, vector, exceptions, ghcjs-base, ghcjs-prim, ghc
6−}:
7−mkDerivation {
8− pname = "jsaddle";
9− version = "0.9.4.0";
10− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
11− libraryHaskellDepends = [
12− aeson base base64-bytestring bytestring exceptions lens primitive
13− text transformers
14− ] ++ (if ghc.isGhcjs or false then [
15− ghcjs-base ghcjs-prim
16− ] else [
17− attoparsec containers deepseq filepath ghc-prim http-types process
18− random ref-tf scientific stm time unordered-containers vector
19− unliftio-core
20− ]);
21− description = "Interface for JavaScript that works with GHCJS and GHC";
22− license = stdenv.lib.licenses.mit;
23−}
jsaddle/jsaddle.cabal+14−15
@@ -1,5 +1,5 @@
11name: jsaddle
2−version: 0.9.4.0
2+version: 0.9.8.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 JavaScipt helper.
13+ drive a small JavaScript helper.
1414category: Web, Javascript
1515author: Hamish Mackenzie
1616
@@ -36,20 +36,20 @@ library
3636 else
3737 build-depends:
3838 attoparsec >=0.11 && <0.14,
39− containers >=0.5.6.2 && <0.6,
39+ containers >=0.5.6.2 && <0.7,
4040 deepseq >=1.3 && < 1.5,
4141 filepath >=1.4.0.0 && <1.5,
4242 ghc-prim,
4343 http-types >=0.8.6 && <0.13,
4444 process >=1.2.3.0 && <1.7,
45− random >= 1.1 && < 1.2,
45+ random >= 1.1 && < 1.3,
4646 ref-tf >=0.4.0.1 && <0.5,
4747 scientific >=0.3 && <0.4,
48− stm >=2.4.4 && <2.5,
49− time >=1.5.0.1 && <1.9,
48+ stm >=2.4.4 && <2.6,
49+ time >=1.5.0.1 && <1.11,
5050 unordered-containers >=0.2 && <0.3,
5151 vector >=0.10 && <0.13,
52− unliftio-core >=0.1 && < 0.2
52+ unliftio-core >=0.1 && < 0.3
5353 exposed-modules:
5454 Data.JSString
5555 Data.JSString.Internal
@@ -107,13 +107,14 @@ library
107107 Language.Javascript.JSaddle.Value
108108 Language.Javascript.JSaddle.Types
109109 build-depends:
110− aeson >=0.8.0.2 && <1.3,
111− base <5,
112− base64-bytestring >=1.0.0.1 && <1.1,
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,
113114 bytestring >=0.10.6.0 && <0.11,
114− exceptions >=0.8 && <0.9,
115− lens >=3.8.5 && <4.17,
116− primitive >=0.6.1.0 && <0.7,
115+ exceptions >=0.8 && <0.11,
116+ lens >=3.8.5 && <5.1,
117+ primitive >=0.6.1.0 && <0.8,
117118 text >=1.2.1.3 && <1.3,
118119 transformers >=0.4.2.0 && <0.6
119120 default-language: Haskell2010
@@ -123,5 +124,3 @@ library
123124 cpp-options: -DCHECK_UNCHECKED
124125 if flag(call-stacks) || flag(check-unchecked)
125126 cpp-options: -DJSADDLE_HAS_CALL_STACK
126−
127−
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/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 JavaScipt helper.
12+-- drive a small JavaScript 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 demonstated.
38+ -- uses more of the JSaddle EDSL feature being demonstrated.
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 code JavaScript code is executed in the the strict order
56− -- set out by the EDSL it done asynchonously to the Haskell code.
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.
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/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 unsafe "$r = eval($1);"
53+foreign import javascript safe "$r = eval($1);"
5454 js_eval :: JSString -> IO JSVal
5555#else
5656eval = evaluateScript . toJSString
jsaddle/src/Language/Javascript/JSaddle/Object.hs+5−6
@@ -526,12 +526,11 @@ array args = do
526526 Object . coerce <$> fromListIO rargs
527527
528528-- | JavaScript's global object
529−global :: Object
530529#ifdef ghcjs_HOST_OS
531−global = js_window
532−foreign import javascript unsafe "$r = window"
533− js_window :: Object
530+foreign import javascript unsafe "$r = globalThis"
531+ global :: Object
534532#else
533+global :: Object
535534global = Object . JSVal . unsafePerformIO $ newIORef 4
536535#endif
537536
@@ -557,7 +556,7 @@ objCallAsFunction :: MakeArgs args
557556objCallAsFunction f this args = do
558557 rargs <- makeArgs args >>= liftIO . Array.fromListIO
559558 liftIO $ js_apply f this rargs
560−foreign import javascript unsafe "$r = $1.apply($2, $3)"
559+foreign import javascript safe "$r = $1.apply($2, $3)"
561560 js_apply :: Object -> Object -> MutableJSArray -> IO JSVal
562561#else
563562objCallAsFunction f this args = do
@@ -577,7 +576,7 @@ objCallAsConstructor :: MakeArgs args
577576objCallAsConstructor f args = do
578577 rargs <- makeArgs args >>= liftIO . Array.fromListIO
579578 liftIO $ js_new f rargs
580−foreign import javascript unsafe "\
579+foreign import javascript safe "\
581580 switch($2.length) {\
582581 case 0 : $r = new $1(); break;\
583582 case 1 : $r = new $1($2[0]); break;\
jsaddle/src/Language/Javascript/JSaddle/Run.hs+2−1
@@ -227,10 +227,11 @@ runJavaScript sendBatch entryPoint = do
227227 zipWithM_ putMVar resultMVars results
228228 forM_ callbacksToFree $ \(JSValueReceived val) ->
229229 atomically (modifyTVar' callbacks (M.delete val))
230− (_, Failure callbacksToFree results exception) -> do
230+ (_, Failure callbacksToFree results exception err) -> do
231231 -- The exception will only be rethrown in Haskell if/when one of the
232232 -- missing results (if any) is evaluated.
233233 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
234+ putStrLn err
234235 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
235236 forM_ callbacksToFree $ \(JSValueReceived val) ->
236237 atomically (modifyTVar' callbacks (M.delete val))
jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+3−38
@@ -13,7 +13,6 @@
1313
1414module Language.Javascript.JSaddle.Run.Files (
1515 indexHtml
16− , jsaddleJs
1716 , initState
1817 , runBatch
1918 , ghcjsHelpers
@@ -42,7 +41,7 @@ initState = "\
4241 \ jsaddle_values.set(1, undefined);\n\
4342 \ jsaddle_values.set(2, false);\n\
4443 \ jsaddle_values.set(3, true);\n\
45− \ jsaddle_values.set(4, window);\n\
44+ \ jsaddle_values.set(4, globalThis);\n\
4645 \ var jsaddle_index = 100;\n\
4746 \ var expectedBatch = 1;\n\
4847 \ var lastResults = [0, {\"tag\": \"Success\", \"contents\": [[], []]}];\n\
@@ -316,7 +315,8 @@ runBatch send sendSync = "\
316315 \ catch (err) {\n\
317316 \ var n = ++jsaddle_index;\n\
318317 \ jsaddle_values.set(n, err);\n\
319− \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Failure\", \"contents\": [callbacksToFree, results, n]}]}" <> "\n\
318+ \ console.log(err);\n\
319+ \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Failure\", \"contents\": [callbacksToFree, results, n, String(err)]}]}" <> "\n\
320320 \ }\n\
321321 \ if(inCallback == 1) {\n\
322322 \ while(asyncBatch !== null) {\n\
@@ -337,41 +337,6 @@ runBatch send sendSync = "\
337337 \ runBatch(batch);\n\
338338 \"
339339
340−-- Use this to generate this string for embedding
341−-- sed -e 's|\\|\\\\|g' -e 's|^| \\|' -e 's|$|\\n\\|' -e 's|"|\\"|g' data/jsaddle.js | pbcopy
342−jsaddleJs :: ByteString
343−jsaddleJs = "\
344− \if(typeof global !== \"undefined\") {\n\
345− \ global.window = global;\n\
346− \ global.WebSocket = require('ws');\n\
347− \}\n\
348− \\n\
349− \var connect = function() {\n\
350− \ var wsaddress =\n\
351− \ typeof window.location === \"undefined\"\n\
352− \ ? \"ws://localhost:3709/\"\n\
353− \ : window.location.protocol.replace('http', 'ws')+\"//\"+window.location.hostname+(window.location.port?(\":\"+window.location.port):\"\");\n\
354− \\n\
355− \ var ws = new WebSocket(wsaddress);\n\
356− \\n\
357− \ ws.onopen = function(e) {\n\
358− \ " <> initState <> "\
359− \\n\
360− \ ws.onmessage = function(e) {\n\
361− \ var batch = JSON.parse(e.data);\n\
362− \\n\
363− \ " <> runBatch (\a -> "ws.send(JSON.stringify(" <> a <> "));") Nothing <> "\
364− \ };\n\
365− \ };\n\
366− \ ws.onerror = function() {\n\
367− \ setTimeout(connect, 1000);\n\
368− \ };\n\
369− \}\n\
370− \\n\
371− \ " <> ghcjsHelpers <> "\
372− \connect();\n\
373− \"
374−
375340ghcjsHelpers :: ByteString
376341ghcjsHelpers = "\
377342 \function h$isNumber(o) {\
jsaddle/src/Language/Javascript/JSaddle/Types.hs+10−2
@@ -124,6 +124,7 @@ import Data.Aeson
124124 (defaultOptions, genericToEncoding, ToJSON(..), FromJSON(..), Value)
125125import GHC.Generics (Generic)
126126import Control.Monad.IO.Unlift (MonadUnliftIO)
127+import qualified Control.Monad.Fail as Fail
127128#endif
128129
129130#if MIN_VERSION_base(4,9,0) && defined(CHECK_UNCHECKED)
@@ -165,7 +166,7 @@ data JSContextRef = JSContextRef {
165166type JSM = IO
166167#else
167168newtype JSM a = JSM { unJSM :: ReaderT JSContextRef IO a }
168− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadThrow, MonadUnliftIO)
169+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadThrow, MonadUnliftIO, Fail.MonadFail)
169170
170171instance MonadCatch JSM where
171172 t `catch` c = JSM (unJSM (syncAfter t) `catch` \e -> unJSM (c e))
@@ -178,6 +179,13 @@ instance MonadMask JSM where
178179 JSM $ uninterruptibleMask $ \unmask -> unJSM (a $ q unmask)
179180 where q :: (ReaderT JSContextRef IO a -> ReaderT JSContextRef IO a) -> JSM a -> JSM a
180181 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
181189
182190#endif
183191
@@ -496,7 +504,7 @@ instance ToJSON Result where
496504instance FromJSON Result
497505
498506data BatchResults = Success [JSValueReceived] [Result]
499− | Failure [JSValueReceived] [Result] JSValueReceived
507+ | Failure [JSValueReceived] [Result] JSValueReceived String
500508 deriving (Show, Generic)
501509
502510instance ToJSON BatchResults where
nix/hix.nixnew file+8
@@ -0,0 +1,8 @@
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+2−12
@@ -6,18 +6,8 @@ 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−
139cabal update
14−cabal new-build --constraint='Cabal>=1.24.2.0' 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
10+cabal new-build 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
1511cd jsaddle-warp
16−GHC_PACKAGE_PATH=/opt/ghc/$GHCVER/lib/ghc-$GHCVER/package.conf.d:~/.cabal/store/ghc-$GHCVER/package.db:./dist-newstyle/packagedb/ghc-$GHCVER jsaddle_datadir=`pwd` ../dist-newstyle/build/jsaddle-warp-$JSADDLE_WARP_VERSION/build/test-tool/test-tool ../jsaddle-warp
17−
18−# update the cache
19−rm -rf .cabal
20−cp -a /root/.cabal ./
21−rm -rf .ghc
22−cp -a /root/.ghc ./
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
2313