on 284cccf help sign in
35 files changed+409−472
since splitsnapshots
unifiedsplit
.travis.yml+24−40 README.md+9−9 cabal.project+35−3 default.nix+8 jsaddle-clib/default.nix+9 jsaddle-clib/jsaddle-clib.cabal+3−4 jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hs+1−3 jsaddle-warp/default.nix+25 jsaddle-warp/jsaddle-warp.cabal+9−15 jsaddle-warp/src/Language/Javascript/JSaddle/Test.hs+12−13 jsaddle-warp/src/Language/Javascript/JSaddle/WebSockets.hs+31−71 jsaddle-warp/tests/DocTest.hs+7−20 jsaddle-webkit2gtk/default.nix+16 jsaddle-webkit2gtk/jsaddle-webkit2gtk.cabal+4−4 jsaddle-webkit2gtk/src/Language/Javascript/JSaddle/WebKitGTK.hs+36−15 jsaddle-webkitgtk/default.nix+16 jsaddle-wkwebview/cbits-cocoa/AppDelegate.m+2−57 jsaddle-wkwebview/cbits-uikit/AppDelegate.m+1−14 jsaddle-wkwebview/cbits/WKWebView.m+1−1 jsaddle-wkwebview/default.nix+21−14 jsaddle-wkwebview/jsaddle-wkwebview.cabal+13−18 jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+17−73 jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+1−25 jsaddle/default.nix+23 jsaddle/jsaddle.cabal+15−14 jsaddle/src-ghc/Data/JSString/Internal/Type.hs+1−2 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+6−5 jsaddle/src/Language/Javascript/JSaddle/Run.hs+3−17 jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+38−7 jsaddle/src/Language/Javascript/JSaddle/Types.hs+2−10 nix/hix.nix−8 travis/build.sh+12−2
.travis.yml+24−40
@@ -1,47 +1,31 @@
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=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
2917
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
18+before_install:
19+ - docker build -t ghc -f travis/$OSVER.Dockerfile travis
4320
21+script:
22+ - docker run -v `pwd`:/build ghc
23+ sh -x -c "
24+ apt-get install -y cabal-install-$CABALVER ghc-$GHCVER &&
25+ export PATH=\$HOME/.cabal/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:/opt/alex/3.1.7/bin:/opt/happy/1.19.5/bin:\$PATH &&
26+ ./travis/build.sh"
4427notifications:
4528 irc:
4629 channels: "irc.freenode.net#ghcjs"
47− email: true
30+ skip_join: true
31+ 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](./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)
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)
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 WebKit2 interface this level of access is only available to WebKit Extensions.
126+WebKitGTK. With the newer WekKit2 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 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
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
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 avoid transferring large string values over the WebSocket use `toJSVal` to convert it to a `JSVal`.
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`.
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 next synchronous command is executed.
216+(because the lazy execution of the JS code). The exception will be thrown when the 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+35−3
@@ -1,10 +1,42 @@
1+flags: -overloaded-methods -overloaded-signals -overloaded-properties
2+
13packages:
24 jsaddle/
35 jsaddle-warp/
46 jsaddle-wkwebview/
7+-- jsaddle-webkitgtk/
58 jsaddle-webkit2gtk/
69 jsaddle-clib/
710
8−constraints: haskell-gi-overloading == 0.0
9−
10−allow-newer: websockets:random, splitmix:time, *:time, *:unix, *:process, stm:base
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
default.nixnew file+8
@@ -0,0 +1,8 @@
1+haskellPackages: {
2+ jsaddle = haskellPackages.callPackage ./jsaddle {};
3+ jsaddle-warp = haskellPackages.callPackage ./jsaddle-warp {};
4+ jsaddle-wkwebview = haskellPackages.callPackage ./jsaddle-wkwebview {};
5+ jsaddle-webkit2gtk = haskellPackages.callPackage ./jsaddle-webkit2gtk {};
6+ jsaddle-webkitgtk = haskellPackages.callPackage ./jsaddle-webkitgtk {};
7+ jsaddle-clib = haskellPackages.callPackage ./jsaddle-clib {};
8+}
jsaddle-clib/default.nixnew file+9
@@ -0,0 +1,9 @@
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+3−4
@@ -1,5 +1,5 @@
11name: jsaddle-clib
2−version: 0.9.8.0
2+version: 0.9.0.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -21,9 +21,8 @@ library
2121 Language.Javascript.JSaddle.CLib
2222 Language.Javascript.JSaddle.CLib.Internal
2323 build-depends:
24− aeson >=0.8.0.2 && <1.6,
24+ aeson >=0.8.0.2 && <1.3,
2525 base <5,
26− base-compat >=0.9.0 && <0.12,
2726 bytestring >=0.10.6.0 && <0.11,
2827 jsaddle >= 0.9.0.0 && <0.10,
2928 data-default,
@@ -33,7 +32,7 @@ library
3332 Include-dirs: cbits/include
3433 Includes: jsaddle.h
3534 Install-includes: jsaddle.h
36− ghc-options: -ferror-spans -Wall
35+ ghc-options: -ferror-spans -Wall -Werror
3736 if impl(ghcjs)
3837 hs-source-dirs: src-ghcjs
3938 else
jsaddle-clib/src-ghc/Language/Javascript/JSaddle/CLib.hs+1−3
@@ -8,9 +8,6 @@ module Language.Javascript.JSaddle.CLib
88 , appConfigToAppCallbacks
99 ) where
1010
11−import Prelude ()
12−import Prelude.Compat
13−
1411import Control.Monad (void)
1512import Control.Concurrent (forkIO)
1613
@@ -19,6 +16,7 @@ import Data.ByteString (useAsCString, packCString)
1916import Data.ByteString.Char8 (unpack)
2017import Data.ByteString.Lazy (ByteString, toStrict, fromStrict)
2118import Data.Default (def, Default)
19+import Data.Monoid ((<>))
2220import Data.Text (Text)
2321import qualified Data.Text.Encoding as T
2422
jsaddle-warp/default.nixnew file+25
@@ -0,0 +1,25 @@
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+9−15
@@ -1,5 +1,5 @@
11name: jsaddle-warp
2−version: 0.9.8.0
2+version: 0.9.5.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,22 +24,20 @@ 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,
28+ aeson >=0.8.0.2 && <1.3,
3129 bytestring >=0.10.6.0 && <0.11,
32− containers >=0.5.6.2 && <0.7,
30+ containers >=0.5.6.2 && <0.6,
3331 foreign-store >=0.2 && <0.3,
3432 http-types >=0.8.6 && <0.13,
3533 jsaddle >=0.9.4.0 && <0.10,
36− stm >=2.4.4 && <2.6,
34+ stm >=2.4.4 && <2.5,
3735 text >=1.2.1.3 && <1.3,
38− time >=1.5.0.1 && <1.11,
36+ time >=1.5.0.1 && <1.9,
3937 transformers >=0.4.2.0 && <0.6,
4038 wai >=3.0.3.0 && <3.3,
4139 wai-websockets >=3.0.0.6 && <3.1,
42− warp >=3.1.2 && <3.4,
40+ warp >=3.1.2 && <3.3,
4341 websockets >=0.9.5.0 && <0.13
4442 exposed-modules:
4543 Language.Javascript.JSaddle.Warp
@@ -59,10 +57,8 @@ test-suite test-tool
5957 bytestring -any,
6058 containers -any,
6159 deepseq -any,
62− doctest >=0.10.1 && <0.19,
63− exceptions -any,
60+ doctest >=0.10.1 && <0.12,
6461 filepath >=1.4.0.0 && <1.5,
65− foreign-store -any,
6662 ghc-prim -any,
6763 http-types -any,
6864 jsaddle -any,
@@ -72,16 +68,14 @@ test-suite test-tool
7268 ref-tf -any,
7369 primitive -any,
7470 process >=1.2.3.0 && <1.7,
75− random -any,
7671 stm -any,
7772 text -any,
7873 time -any,
7974 transformers -any,
80− unliftio-core -any,
8175 wai -any,
8276 wai-websockets -any,
8377 warp -any,
84− webdriver >=0.8.4 && <0.10,
78+ webdriver >=0.8.4 && <0.9,
8579 websockets -any
8680 type: exitcode-stdio-1.0
8781 main-is: DocTest.hs
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/WebSockets.hs+31−71
@@ -20,12 +20,9 @@ module Language.Javascript.JSaddle.WebSockets (
2020 , jsaddleApp
2121 , jsaddleWithAppOr
2222 , jsaddleAppWithJs
23− , jsaddleAppWithJsOr
2423 , jsaddleAppPartial
2524 , jsaddleJs
26− , jsaddleJs'
2725 , debug
28− , debugOr
2926 , debugWrapper
3027) where
3128
@@ -60,57 +57,45 @@ import qualified Data.Map as M (empty, insert, lookup)
6057import Data.IORef
6158 (readIORef, newIORef, atomicModifyIORef')
6259import Data.ByteString.Lazy (ByteString)
63−import qualified Data.ByteString.Lazy as LBS
64− (toStrict, stripPrefix)
60+import qualified Data.ByteString.Lazy as LBS (stripPrefix)
6561import Control.Concurrent.MVar
6662 (tryTakeMVar, MVar, tryPutMVar, modifyMVar_, putMVar, takeMVar,
6763 readMVar, newMVar, newEmptyMVar, modifyMVar)
6864import Network.Wai.Handler.Warp
6965 (defaultSettings, setTimeout, setPort, runSettings)
70−import Foreign.Store (readStore, lookupStore, writeStore, Store(..))
66+import Foreign.Store (newStore, readStore, lookupStore, writeStore, Store(..))
7167import Language.Javascript.JSaddle (askJSM)
7268import Control.Monad.IO.Class (MonadIO(..))
7369
7470import Language.Javascript.JSaddle.WebSockets.Compat (getTextMessageByteString)
75−import qualified Data.Text.Encoding as T (decodeUtf8)
7671
7772jsaddleOr :: ConnectionOptions -> JSM () -> Application -> IO Application
7873jsaddleOr opts entryPoint otherApp = do
7974 syncHandlers <- newIORef M.empty
80− asyncHandlers <- newIORef M.empty
8175 let wsApp :: ServerApp
8276 wsApp pending_conn = do
8377 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"
106− _ -> error "jsaddle WebSocket unexpected binary data"
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
10792
10893 -- Based on Network.WebSocket.forkPingThread
10994 waitTillClosed conn = ignore `handle` go 1
11095 where
11196 go :: Int -> IO ()
11297 go i = do
113− threadDelay (10 * 1000 * 1000)
98+ threadDelay (1 * 1000 * 1000)
11499 sendPing conn (T.pack $ show i)
115100 go (i + 1)
116101
@@ -139,18 +124,13 @@ jsaddleOr opts entryPoint otherApp = do
139124
140125
141126jsaddleApp :: Application
142−jsaddleApp = jsaddleAppWithJs $ jsaddleJs False
127+jsaddleApp = jsaddleAppWithJs $ jsaddleJs Nothing False
143128
144129jsaddleAppWithJs :: ByteString -> Application
145130jsaddleAppWithJs 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)
131+ fromMaybe
132+ (sendResponse $ W.responseLBS H.status403 [("Content-Type", "text/plain")] "Forbidden")
133+ (jsaddleAppPartialWithJs js req sendResponse)
154134
155135jsaddleWithAppOr :: ConnectionOptions -> JSM () -> Application -> IO Application
156136jsaddleWithAppOr opts entryPoint otherApp = jsaddleOr opts entryPoint $ \req sendResponse ->
@@ -158,7 +138,7 @@ jsaddleWithAppOr opts entryPoint otherApp = jsaddleOr opts entryPoint $ \req sen
158138 (jsaddleAppPartial req sendResponse))
159139
160140jsaddleAppPartial :: Request -> (Response -> IO ResponseReceived) -> Maybe (IO ResponseReceived)
161−jsaddleAppPartial = jsaddleAppPartialWithJs $ jsaddleJs False
141+jsaddleAppPartial = jsaddleAppPartialWithJs $ jsaddleJs Nothing False
162142
163143indexResponse :: Response
164144indexResponse = W.responseLBS H.status200 [("Content-Type", "text/html")] indexHtml
@@ -169,14 +149,11 @@ jsaddleAppPartialWithJs js req sendResponse = case (W.requestMethod req, W.pathI
169149 ("GET", ["jsaddle.js"]) -> Just $ sendResponse $ W.responseLBS H.status200 [("Content-Type", "application/javascript")] js
170150 _ -> Nothing
171151
172−jsaddleJs :: Bool -> ByteString
173−jsaddleJs = jsaddleJs' Nothing
174−
175152-- Use this to generate this string for embedding
176153-- 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\
154+jsaddleJs :: Maybe ByteString -> Bool -> ByteString
155+jsaddleJs jsaddleUri refreshOnLoad = "\
156+ \if(typeof global !== \"undefined\") {\n\
180157 \ global.window = global;\n\
181158 \ global.WebSocket = require('ws');\n\
182159 \}\n\
@@ -188,30 +165,20 @@ jsaddleJs' jsaddleUri refreshOnLoad = "\
188165 (jsaddleUri >>= LBS.stripPrefix "http")
189166 <> ";\n\
190167 \\n\
191− \ var ws0 = new WebSocket(wsaddress);\n\
168+ \ var ws = new WebSocket(wsaddress);\n\
192169 \ var syncKey = \"\";\n\
193170 \\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\
171+ \ ws.onopen = function(e) {\n\
198172 \ " <> initState <> "\n\
199173 \\n\
200− \ ws0.onmessage = function(e) {\n\
174+ \ ws.onmessage = function(e) {\n\
201175 \ var batch = JSON.parse(e.data);\n\
202176 \ if(inCallback > 0) {\n\
203177 \ asyncBatch = batch;\n\
204178 \ return;\n\
205179 \ }\n\
206180 \ 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" <>
181+ \ syncKey = batch;\n" <>
215182 (if refreshOnLoad
216183 then " var xhr = new XMLHttpRequest();\n\
217184 \ xhr.open('POST', '/reload/'+syncKey, true);\n\
@@ -233,7 +200,7 @@ jsaddleJs' jsaddleUri refreshOnLoad = "\
233200 \ return JSON.parse(xhr.response);})()")) <> "\
234201 \ };\n\
235202 \ };\n\
236− \ ws0.onerror = function() {\n\
203+ \ ws.onerror = function() {\n\
237204 \ setTimeout(connect, 1000);\n\
238205 \ };\n\
239206 \}\n\
@@ -249,14 +216,7 @@ debug :: Int -> JSM () -> IO ()
249216debug port f = do
250217 debugWrapper $ \withRefresh registerContext ->
251218 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)
219+ jsaddleOr defaultConnectionOptions (registerContext >> f >> syncPoint) (withRefresh $ jsaddleAppWithJs $ jsaddleJs Nothing True)
260220 putStrLn $ "<a href=\"http://localhost:" <> show port <> "\">run</a>"
261221
262222refreshMiddleware :: ((Response -> IO ResponseReceived) -> IO ResponseReceived) -> Middleware
jsaddle-warp/tests/DocTest.hs+7−20
@@ -11,17 +11,18 @@ 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)
1716import Control.Monad.IO.Class (MonadIO(..))
1817import Data.Monoid ((<>))
1918import System.Environment (getArgs)
19+import Language.Javascript.JSaddle.Run.Files (jsaddleJs)
20+import qualified Data.ByteString.Lazy.Char8 as BS (unpack)
2021import Test.WebDriver (runSession, defaultConfig, openPage, closeSession)
2122
2223main :: IO ()
2324main = do
24− putStrLn "Testing JSaddle"
25+ putStrLn "Hello"
2526 jsaddlePath <- getArgs >>= \case
2627 [arg] -> return arg
2728 _ -> do
@@ -29,18 +30,14 @@ main = do
2930 exitFailure
3031 hSetBuffering stdout LineBuffering
3132 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− putStrLn "Starting phantomjs"
39− forkIO . void $ readProcess "phantomjs" ["--webdriver=4444"] "" >>= putStr
40− threadDelay 5000000
41− putStrLn "Running Tests"
42− done <- newEmptyMVar
43− forkIO $ do
38+ forkIO . void $ readProcess "phantomjs" ["--webdriver=4444"] (BS.unpack jsaddleJs) >>= putStr
39+ runSession defaultConfig $ do
40+ openPage "http://localhost:3709"
4441 liftIO $ doctest [
4542 "-hide-all-packages",
4643 "-package=base-" ++ VERSION_base,
@@ -64,10 +61,6 @@ main = do
6461 "-package=ref-tf-" ++ VERSION_ref_tf,
6562 "-package=deepseq-" ++ VERSION_deepseq,
6663 "-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,
7164 "-i" <> "src",
7265 "-i" <> "src-ghc",
7366 "src/Language/Javascript/JSaddle/Test.hs",
@@ -90,7 +83,6 @@ main = do
9083 jsaddlePath </> "src/Language/Javascript/JSaddle/Arguments.hs",
9184 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes.hs",
9285 jsaddlePath </> "src/Language/Javascript/JSaddle/Classes/Internal.hs",
93− jsaddlePath </> "src/Language/Javascript/JSaddle/Debug.hs",
9486 jsaddlePath </> "src/Language/Javascript/JSaddle/Evaluate.hs",
9587 jsaddlePath </> "src/Language/Javascript/JSaddle/Exception.hs",
9688 jsaddlePath </> "src/Language/Javascript/JSaddle/Monad.hs",
@@ -104,9 +96,4 @@ main = do
10496 jsaddlePath </> "src/Language/Javascript/JSaddle/String.hs",
10597 jsaddlePath </> "src/Language/Javascript/JSaddle/Types.hs",
10698 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
11299 closeSession
jsaddle-webkit2gtk/default.nixnew file+16
@@ -0,0 +1,16 @@
1+{ mkDerivation, aeson, base, bytestring, directory, gi-gio, gi-glib
2+, gi-gtk, gi-javascriptcore, gi-webkit2, haskell-gi-base, jsaddle
3+, stdenv, text, unix, webkit2gtk3-javascriptcore
4+}:
5+mkDerivation {
6+ pname = "jsaddle-webkit2gtk";
7+ version = "0.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.8.0
2+version: 0.9.4.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.6,
29+ aeson >=0.8.0.2 && <1.3,
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.26,
39− haskell-gi-overloading >=0.0 && < 2.0,
38+ haskell-gi-base >=0.20 && <0.22,
39+ haskell-gi-overloading ==0.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+36−15
@@ -43,24 +43,29 @@ 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)
4647import qualified Data.Text as T (pack)
48+import Data.Text.Foreign (fromPtr)
4749import Data.Text.Encoding (decodeUtf8, encodeUtf8)
4850
51+import Foreign.Ptr (castPtr, nullPtr)
52+
4953import Data.GI.Base.BasicTypes (GObject)
5054import Data.GI.Base.Signals
5155 (connectSignalFunPtr, SignalConnectMode(..), SignalConnectMode,
5256 SignalHandlerId)
57+import Data.GI.Base.ManagedPtr (withManagedPtr)
5358
5459import GI.GLib (timeoutAdd, idleAdd, pattern PRIORITY_HIGH, pattern PRIORITY_DEFAULT)
5560import qualified GI.Gtk as Gtk (main, init)
5661import GI.Gtk
5762 (windowSetPosition, windowSetDefaultSize, windowNew,
58− scrolledWindowNew, Adjustment, containerAdd,
63+ scrolledWindowNew, noAdjustment, containerAdd,
5964 WindowType(..), WindowPosition(..), widgetDestroy,
6065 widgetGetToplevel, widgetShowAll, onWidgetDestroy,
6166 mainQuit)
62−import GI.Gio (Cancellable)
63−import GI.JavaScriptCore (valueToString)
67+import GI.Gio (noCancellable)
68+import GI.JavaScriptCore (Value(..), GlobalContext(..))
6469import GI.WebKit2
6570 (scriptDialogPromptSetText, scriptDialogPromptGetDefaultText,
6671 scriptDialogGetMessage, scriptDialogGetDialogType,
@@ -69,7 +74,7 @@ import GI.WebKit2
6974 setSettingsEnableJavascript, webViewNewWithUserContentManager,
7075 userContentManagerNew,
7176 userContentManagerRegisterScriptMessageHandler,
72− javascriptResultGetJsValue,
77+ javascriptResultGetValue, javascriptResultGetGlobalContext,
7378 webViewGetUserContentManager,
7479 mk_UserContentManagerScriptMessageReceivedCallback,
7580 wrap_UserContentManagerScriptMessageReceivedCallback,
@@ -78,16 +83,17 @@ import GI.WebKit2
7883 onWebViewLoadChanged, setSettingsEnableDeveloperExtras,
7984 webViewSetSettings, webViewGetSettings, ScriptDialogType(..))
8085
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+
8193import Language.Javascript.JSaddle (JSM, Results, Batch)
8294import Language.Javascript.JSaddle.Run (runJavaScript)
8395import Language.Javascript.JSaddle.Run.Files (initState, runBatch, ghcjsHelpers)
8496
85−noAdjustment :: Maybe Adjustment
86−noAdjustment = Nothing
87−
88−noCancellable :: Maybe Cancellable
89−noCancellable = Nothing
90−
9197quitWebView :: WebView -> IO ()
9298quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
9399 widgetDestroy w
@@ -103,6 +109,21 @@ postGUIAsync :: IO () -> IO ()
103109postGUIAsync action =
104110 void . idleAdd PRIORITY_DEFAULT $ action >> return False
105111
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+
106127run :: JSM () -> IO ()
107128run main = do
108129 _ <- Gtk.init Nothing
@@ -151,16 +172,16 @@ connectUserContentManagerScriptMessageReceived obj cb after = liftIO $ do
151172 let cb' = wrap_UserContentManagerScriptMessageReceivedCallback cb
152173 cb'' <- mk_UserContentManagerScriptMessageReceivedCallback cb'
153174 connectSignalFunPtr obj "script-message-received::jsaddle" cb'' after
154−#if MIN_VERSION_haskell_gi_base(0,23,0)
155− Nothing
156−#endif
157175
158176addJSaddleHandler :: WebView -> (Results -> IO ()) -> (Results -> IO Batch) -> IO ()
159177addJSaddleHandler webView processResult syncResults = do
160178 manager <- webViewGetUserContentManager webView
161179 _ <- onUserContentManagerScriptMessageReceived manager $ \result -> do
162− arg <- javascriptResultGetJsValue result
163− bs <- encodeUtf8 <$> valueToString arg
180+ ctx <- javascriptResultGetGlobalContext result
181+ arg <- javascriptResultGetValue result
182+ bs <- withJSContextRef ctx $ \ctxRef ->
183+ withJSValueRef arg $ \argRef ->
184+ encodeUtf8 <$> valueToText ctxRef argRef
164185 mapM_ processResult (decode (fromStrict bs))
165186 _ <- onWebViewScriptDialog webView $ \dialog ->
166187 scriptDialogGetDialogType dialog >>= \case
jsaddle-webkitgtk/default.nixnew file+16
@@ -0,0 +1,16 @@
1+{ mkDerivation, aeson, base, bytestring, directory, gi-glib, gi-gtk
2+, gi-javascriptcore, gi-webkit, haskell-gi-base, jsaddle, stdenv
3+, text, unix, webkitgtk3-javascriptcore
4+}:
5+mkDerivation {
6+ pname = "jsaddle-webkitgtk";
7+ version = "0.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+2−57
@@ -2,9 +2,6 @@
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);
85extern void callWithWebView(WKWebView *, HsStablePtr);
96
107@interface AppDelegate : NSObject <NSApplicationDelegate>
@@ -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 {
@@ -40,39 +31,18 @@ uint64_t global_developerExtrasEnabled = 1;
4031 return self;
4132}
4233
43−-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
44− bool b = callWithCStringReturningBool([filename UTF8String], global_applicationOpenFile);
45− return b ? YES : NO;
46−}
47−
4834-(void)applicationWillFinishLaunching:(NSNotification *)notification {
4935 WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
50− if (global_developerExtrasEnabled) {
51− [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
52− }
36+ [theConfiguration.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
5337 WKWebView *webView = [[WKWebView alloc] initWithFrame: [_window.contentView frame] configuration:theConfiguration];
5438 [_window setContentView:webView];
5539 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);
6940}
7041
7142-(void)applicationDidFinishLaunching:(NSNotification *)notification {
7243 [_window orderFrontRegardless];
7344 [_window center];
7445 [NSApp activateIgnoringOtherApps:YES];
75− callIO(global_didFinishLaunchingWithOptions);
7646}
7747
7848-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
@@ -81,33 +51,8 @@ uint64_t global_developerExtrasEnabled = 1;
8151
8252@end
8353
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) {
54+void runInWKWebView(HsStablePtr handler, const char * _Nonnull progName) {
10555 @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;
11156 NSApplication *application = [NSApplication sharedApplication];
11257 AppDelegate *appDelegate = [[AppDelegate alloc] initApp:handler progName:[NSString stringWithCString:progName encoding:NSUTF8StringEncoding]];
11358 [application setDelegate:appDelegate];
jsaddle-wkwebview/cbits-uikit/AppDelegate.m+1−14
@@ -5,7 +5,6 @@
55
66extern void callIO(HsStablePtr);
77extern void callWithCString(const char * _Nonnull, HsStablePtr);
8−extern void callWithCIntCString(int n, const char * _Nonnull, HsStablePtr);
98
109@interface AppDelegate ()
1110
@@ -21,7 +20,6 @@ HsStablePtr global_applicationWillEnterForeground = 0;
2120HsStablePtr global_applicationWillTerminate = 0;
2221HsStablePtr global_applicationSignificantTimeChange = 0;
2322HsStablePtr global_applicationUniversalLink = 0;
24−HsStablePtr global_applicationDidReceiveRemoteNotification = 0;
2523uint64_t global_requestAuthorizationWithOptions = 0;
2624uint64_t global_requestAuthorizationOptionBadge = 0;
2725uint64_t global_requestAuthorizationOptionSound = 0;
@@ -117,14 +115,6 @@ HsStablePtr global_didFailToRegisterForRemoteNotificationsWithError = 0;
117115 if ([userInfo valueForKeyPath:@"aps.badge"] != nil) {
118116 [UIApplication sharedApplication].applicationIconBadgeNumber=[[[userInfo objectForKey:@"aps"] objectForKey:@"badge"] intValue];
119117 }
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− }
128118 completionHandler(UIBackgroundFetchResultNewData);
129119}
130120
@@ -162,7 +152,6 @@ void runInWKWebView(HsStablePtr handler,
162152 HsStablePtr hs_applicationWillTerminate,
163153 HsStablePtr hs_applicationSignificantTimeChange,
164154 HsStablePtr hs_applicationUniversalLink,
165− HsStablePtr hs_applicationDidReceiveRemoteNotification,
166155 const uint64_t hs_requestAuthorizationWithOptions,
167156 const uint64_t hs_requestAuthorizationOptionBadge,
168157 const uint64_t hs_requestAuthorizationOptionSound,
@@ -170,8 +159,7 @@ void runInWKWebView(HsStablePtr handler,
170159 const uint64_t hs_requestAuthorizationOptionCarPlay,
171160 const uint64_t hs_registerForRemoteNotifications,
172161 HsStablePtr hs_didRegisterForRemoteNotificationsWithDeviceToken,
173− HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError,
174− const uint64_t hs_developerExtrasEnabled) {
162+ HsStablePtr hs_didFailToRegisterForRemoteNotificationsWithError) {
175163 @autoreleasepool {
176164 globalHandler = handler;
177165 global_willFinishLaunchingWithOptions = hs_willFinishLaunchingWithOptions;
@@ -183,7 +171,6 @@ void runInWKWebView(HsStablePtr handler,
183171 global_applicationWillTerminate = hs_applicationWillTerminate;
184172 global_applicationSignificantTimeChange = hs_applicationSignificantTimeChange;
185173 global_applicationUniversalLink = hs_applicationUniversalLink;
186− global_applicationDidReceiveRemoteNotification = hs_applicationDidReceiveRemoteNotification;
187174 global_requestAuthorizationWithOptions = hs_requestAuthorizationWithOptions;
188175 global_requestAuthorizationOptionBadge = hs_requestAuthorizationOptionBadge;
189176 global_requestAuthorizationOptionSound = hs_requestAuthorizationOptionSound;
jsaddle-wkwebview/cbits/WKWebView.m+1−1
@@ -97,7 +97,7 @@ void completeSync(JSaddleHandler *handler, const char * _Nonnull s) {
9797
9898void loadHTMLStringWithBaseURL(WKWebView *webView, const char * _Nonnull html, const char * _Nonnull url) {
9999 NSString *htmlString = [NSString stringWithCString:html encoding:NSUTF8StringEncoding];
100− NSURL *baseURL = [NSURL URLWithString:[[NSString alloc] initWithCString:url encoding:NSUTF8StringEncoding]];
100+ NSURL *baseURL = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:url encoding:NSUTF8StringEncoding]];
101101 dispatch_async(dispatch_get_main_queue(), ^{
102102 [webView loadHTMLString:htmlString baseURL:baseURL];
103103 });
jsaddle-wkwebview/default.nix+21−14
@@ -1,14 +1,21 @@
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−})
1+{ mkDerivation, stdenv, aeson, base, bytestring, jsaddle, data-default
2+, buildPackages, hostPlatform
3+}:
4+
5+mkDerivation {
6+ pname = "jsaddle-wkwebview";
7+ version = "0.9.4.0";
8+ src = ./.;
9+ libraryHaskellDepends = [ aeson base bytestring jsaddle data-default ];
10+ libraryFrameworkDepends = with buildPackages; if hostPlatform.useiOSCross or false then [
11+ (assert osx_sdk != null; osx_sdk)
12+ ] else [
13+ darwin.libobjc
14+ darwin.apple_sdk.libs.xpc
15+ darwin.apple_sdk.frameworks.Foundation
16+ darwin.apple_sdk.frameworks.Cocoa
17+ darwin.apple_sdk.frameworks.WebKit
18+ ];
19+ description = "Interface for JavaScript that works with GHCJS and GHC";
20+ license = stdenv.lib.licenses.mit;
21+}
jsaddle-wkwebview/jsaddle-wkwebview.cabal+13−18
@@ -1,5 +1,5 @@
11name: jsaddle-wkwebview
2−version: 0.9.8.0
2+version: 0.9.4.0
33cabal-version: >=1.10
44build-type: Simple
55license: MIT
@@ -25,31 +25,26 @@ source-repository head
2525library
2626 exposed-modules:
2727 Language.Javascript.JSaddle.WKWebView
28+ Language.Javascript.JSaddle.WKWebView.Internal
2829 build-depends:
29− base <5
30+ aeson >=0.8.0.2 && <1.3,
31+ base <5,
32+ bytestring >=0.10.6.0 && <0.11,
33+ directory,
34+ jsaddle >= 0.9.4.0 && <0.10,
35+ text,
36+ data-default,
37+ containers
3038 default-language: Haskell2010
3139 hs-source-dirs: src
40+ frameworks: Foundation, WebKit
3241 ghc-options: -ferror-spans -Wall
33− if os(linux)
34− buildable: False
42+ c-sources:
43+ cbits/WKWebView.m
3544 if impl(ghcjs)
3645 hs-source-dirs: src-ghcjs
3746 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
4947 hs-source-dirs: src-ghc
50− c-sources:
51− cbits/WKWebView.m
52− cc-options: -Wno-everything
5348 if os(ios)
5449 frameworks: UIKit, UserNotifications
5550 if flag(include-app-delegate)
jsaddle-wkwebview/src-ghc/Language/Javascript/JSaddle/WKWebView.hs+17−73
@@ -1,15 +1,11 @@
11module Language.Javascript.JSaddle.WKWebView
22 ( jsaddleMain
3− , jsaddleMainHTMLWithBaseURL
43 , jsaddleMainFile
54 , WKWebView(..)
65 , run
7− , run'
86 , runWithAppConfig
9− , runHTMLWithBaseURL
107 , runFile
118 , mainBundleResourcePath
12− , ApplicationState (..)
139 , AppDelegateConfig (..)
1410 , AppDelegateNotificationConfig (..)
1511 , AuthorizationOption (..)
@@ -21,11 +17,9 @@ import Data.Set (Set)
2117import qualified Data.Set as Set
2218import Data.Word
2319import Foreign.Marshal.Utils (fromBool)
24−import Language.Javascript.JSaddle.WKWebView.Internal
25− (jsaddleMain, jsaddleMainHTMLWithBaseURL, jsaddleMainFile, WKWebView(..), mainBundleResourcePath)
20+import Language.Javascript.JSaddle.WKWebView.Internal (jsaddleMain, jsaddleMainFile, WKWebView(..), mainBundleResourcePath)
2621import System.Environment (getProgName)
2722import Foreign.C.String (CString, withCString)
28−import Foreign.C.Types (CBool, CInt)
2923import Foreign.StablePtr (StablePtr, newStablePtr)
3024import Language.Javascript.JSaddle (JSM)
3125
@@ -41,7 +35,6 @@ foreign import ccall runInWKWebView
4135 -> StablePtr (IO ()) -- applicationWillTerminate
4236 -> StablePtr (IO ()) -- applicationSignificantTimeChange
4337 -> StablePtr (CString -> IO ()) -- applicationUniversalLink
44− -> StablePtr (CInt -> CString -> IO ()) -- applicationDidReceiveRemoteNotification
4538 -> Word64 -- whether to run requestAuthorizationWithOptions
4639 -> Word64 -- Ask for Badge authorization
4740 -> Word64 -- Ask for Sound authorization
@@ -50,8 +43,6 @@ foreign import ccall runInWKWebView
5043 -> Word64 -- registerForRemoteNotifications
5144 -> StablePtr (CString -> IO ()) -- didRegisterForRemoteNotificationsWithDeviceToken
5245 -> StablePtr (CString -> IO ()) -- didFailToRegisterForRemoteNotificationsWithError
53− -> Word64 -- developerExtrasEnabled
54− -> StablePtr (CString -> IO CBool) -- applicationOpenFile
5546 -> IO ()
5647
5748data AppDelegateConfig = AppDelegateConfig
@@ -64,25 +55,7 @@ data AppDelegateConfig = AppDelegateConfig
6455 , _appDelegateConfig_applicationWillTerminate :: IO ()
6556 , _appDelegateConfig_applicationSignificantTimeChange :: IO ()
6657 , _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
7658 , _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.
8659 }
8760
8861instance Default AppDelegateConfig where
@@ -96,10 +69,7 @@ instance Default AppDelegateConfig where
9669 , _appDelegateConfig_applicationWillTerminate = return ()
9770 , _appDelegateConfig_applicationSignificantTimeChange = return ()
9871 , _appDelegateConfig_applicationUniversalLink = \_ -> return ()
99− , _appDelegateConfig_applicationDidReceiveRemoteNotification = \_ _ -> return ()
10072 , _appDelegateConfig_appDelegateNotificationConfig = def
101− , _appDelegateConfig_developerExtrasEnabled = True
102− , _appDelegateConfig_applicationOpenFile = \_ -> return False
10373 }
10474
10575data AuthorizationOption = AuthorizationOption_Badge
@@ -108,12 +78,6 @@ data AuthorizationOption = AuthorizationOption_Badge
10878 | AuthorizationOption_CarPlay
10979 deriving (Show, Read, Eq, Ord)
11080
111−data ApplicationState = ApplicationState_Active
112− | ApplicationState_Inactive
113− | ApplicationState_Background
114− | ApplicationState_Unknown CInt
115− deriving (Show, Read, Eq, Ord)
116−
11781data AppDelegateNotificationConfig = AppDelegateNotificationConfig
11882 { _appDelegateNotificationConfig_requestAuthorizationWithOptions :: Maybe (Set AuthorizationOption)
11983 , _appDelegateNotificationConfig_registerForRemoteNotifications :: Bool
@@ -132,38 +96,29 @@ instance Default AppDelegateNotificationConfig where
13296
13397-- | Run JSaddle in a WKWebView
13498run :: JSM () -> IO ()
135−run f = run' def (jsaddleMain f)
99+run = run' Nothing def
136100
137101-- | Run JSaddle in a WKWebView
138102runWithAppConfig :: AppDelegateConfig -> JSM () -> IO ()
139−runWithAppConfig cfg = run' cfg . jsaddleMain
103+runWithAppConfig = run' Nothing
140104
141105-- | Run JSaddle in a WKWebView first loading the specified file
142106-- 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 ())
107+runFile :: ByteString -- ^ The file to navigate to.
108+ -> ByteString -- ^ The path to allow read access to.
109+ -> AppDelegateConfig
110+ -> JSM ()
111+ -> IO ()
112+runFile url allowing = run' $ Just (url, allowing)
113+
114+run' :: Maybe (ByteString, ByteString)
115+ -> AppDelegateConfig
116+ -> JSM ()
164117 -> IO ()
165−run' cfg main = do
166− handler <- newStablePtr main
118+run' mUrl cfg f = do
119+ handler <- case mUrl of
120+ Just (url, allowing) -> newStablePtr (jsaddleMainFile url allowing f)
121+ Nothing -> newStablePtr (jsaddleMain f)
167122 progName <- getProgName
168123
169124 -- AppDelegate callbacks
@@ -176,14 +131,6 @@ run' cfg main = do
176131 applicationWillTerminate <- newStablePtr $ _appDelegateConfig_applicationWillTerminate cfg
177132 applicationSignificantTimeChange <- newStablePtr $ _appDelegateConfig_applicationSignificantTimeChange cfg
178133 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
187134
188135 -- AppDelegate notification configuration
189136 let ncfg = _appDelegateConfig_appDelegateNotificationConfig cfg
@@ -206,7 +153,6 @@ run' cfg main = do
206153 applicationWillTerminate
207154 applicationSignificantTimeChange
208155 applicationUniversalLink
209− applicationDidReceiveRemoteNotification
210156 (fromBool requestAuthorizationWithOptions)
211157 (fromBool $ Set.member AuthorizationOption_Badge authorizationOptions)
212158 (fromBool $ Set.member AuthorizationOption_Sound authorizationOptions)
@@ -215,5 +161,3 @@ run' cfg main = do
215161 (fromBool registerForRemoteNotifications)
216162 didRegisterForRemoteNotificationsWithDeviceToken
217163 didFailToRegisterForRemoteNotificationsWithError
218− (fromBool $ _appDelegateConfig_developerExtrasEnabled cfg)
219− applicationOpenFile
jsaddle-wkwebview/src/Language/Javascript/JSaddle/WKWebView/Internal.hs+1−25
@@ -1,7 +1,6 @@
11{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
22module Language.Javascript.JSaddle.WKWebView.Internal
33 ( jsaddleMain
4− , jsaddleMainHTMLWithBaseURL
54 , jsaddleMainFile
65 , WKWebView(..)
76 , mainBundleResourcePath
@@ -20,7 +19,6 @@ import qualified Data.Text as T (pack)
2019import Data.Text.Encoding (encodeUtf8)
2120
2221import Foreign.C.String (CString)
23−import Foreign.C.Types (CBool(..), CInt(..))
2422import Foreign.Ptr (Ptr, nullPtr)
2523import Foreign.StablePtr (StablePtr, newStablePtr, deRefStablePtr)
2624
@@ -37,9 +35,7 @@ foreign export ccall jsaddleStart :: StablePtr (IO ()) -> IO ()
3735foreign export ccall jsaddleResult :: StablePtr (Results -> IO ()) -> CString -> IO ()
3836foreign export ccall jsaddleSyncResult :: StablePtr (Results -> IO Batch) -> JSaddleHandler -> CString -> IO ()
3937foreign export ccall callWithWebView :: WKWebView -> StablePtr (WKWebView -> IO ()) -> IO ()
40−foreign export ccall callWithCIntCString :: CInt -> CString -> StablePtr (CInt -> CString -> IO ()) -> IO ()
4138foreign export ccall callWithCString :: CString -> StablePtr (CString -> IO ()) -> IO ()
42−foreign export ccall callWithCStringReturningBool :: CString -> StablePtr (CString -> IO CBool) -> IO CBool
4339foreign export ccall callIO :: StablePtr (IO ()) -> IO ()
4440foreign import ccall addJSaddleHandler :: WKWebView -> StablePtr (IO ()) -> StablePtr (Results -> IO ()) -> StablePtr (Results -> IO Batch) -> IO ()
4541foreign import ccall loadHTMLStringWithBaseURL :: WKWebView -> CString -> CString -> IO ()
@@ -53,19 +49,9 @@ jsaddleMain :: JSM () -> WKWebView -> IO ()
5349jsaddleMain f webView = do
5450 pwd <- getCurrentDirectory
5551 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 =
6652 jsaddleMain' f webView $
6753 useAsCString baseURL $ \url ->
68− useAsCString initialHTML $ \html ->
54+ useAsCString (toStrict indexHtml) $ \html ->
6955 loadHTMLStringWithBaseURL webView html url
7056
7157-- | Run JSaddle in a WKWebView first loading the specified file
@@ -129,16 +115,6 @@ callWithCString c fptr = do
129115 f <- deRefStablePtr fptr
130116 f c
131117
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−
142118callIO :: StablePtr (IO ()) -> IO ()
143119callIO ptr = do
144120 f <- deRefStablePtr ptr
jsaddle/default.nixnew file+23
@@ -0,0 +1,23 @@
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+15−14
@@ -1,5 +1,5 @@
11name: jsaddle
2−version: 0.9.8.0
2+version: 0.9.4.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
@@ -36,20 +36,20 @@ library
3636 else
3737 build-depends:
3838 attoparsec >=0.11 && <0.14,
39− containers >=0.5.6.2 && <0.7,
39+ containers >=0.5.6.2 && <0.6,
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.3,
45+ random >= 1.1 && < 1.2,
4646 ref-tf >=0.4.0.1 && <0.5,
4747 scientific >=0.3 && <0.4,
48− stm >=2.4.4 && <2.6,
49− time >=1.5.0.1 && <1.11,
48+ stm >=2.4.4 && <2.5,
49+ time >=1.5.0.1 && <1.9,
5050 unordered-containers >=0.2 && <0.3,
5151 vector >=0.10 && <0.13,
52− unliftio-core >=0.1 && < 0.3
52+ unliftio-core >=0.1 && < 0.2
5353 exposed-modules:
5454 Data.JSString
5555 Data.JSString.Internal
@@ -107,14 +107,13 @@ library
107107 Language.Javascript.JSaddle.Value
108108 Language.Javascript.JSaddle.Types
109109 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,
110+ aeson >=0.8.0.2 && <1.3,
111+ base <5,
112+ base64-bytestring >=1.0.0.1 && <1.1,
114113 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,
114+ exceptions >=0.8 && <0.9,
115+ lens >=3.8.5 && <4.17,
116+ primitive >=0.6.1.0 && <0.7,
118117 text >=1.2.1.3 && <1.3,
119118 transformers >=0.4.2.0 && <0.6
120119 default-language: Haskell2010
@@ -124,3 +123,5 @@ library
124123 cpp-options: -DCHECK_UNCHECKED
125124 if flag(call-stacks) || flag(check-unchecked)
126125 cpp-options: -DJSADDLE_HAS_CALL_STACK
126+
127+
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/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 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/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/Object.hs+6−5
@@ -526,11 +526,12 @@ array args = do
526526 Object . coerce <$> fromListIO rargs
527527
528528-- | JavaScript's global object
529+global :: Object
529530#ifdef ghcjs_HOST_OS
530−foreign import javascript unsafe "$r = globalThis"
531− global :: Object
531+global = js_window
532+foreign import javascript unsafe "$r = window"
533+ js_window :: Object
532534#else
533−global :: Object
534535global = Object . JSVal . unsafePerformIO $ newIORef 4
535536#endif
536537
@@ -556,7 +557,7 @@ objCallAsFunction :: MakeArgs args
556557objCallAsFunction f this args = do
557558 rargs <- makeArgs args >>= liftIO . Array.fromListIO
558559 liftIO $ js_apply f this rargs
559−foreign import javascript safe "$r = $1.apply($2, $3)"
560+foreign import javascript unsafe "$r = $1.apply($2, $3)"
560561 js_apply :: Object -> Object -> MutableJSArray -> IO JSVal
561562#else
562563objCallAsFunction f this args = do
@@ -576,7 +577,7 @@ objCallAsConstructor :: MakeArgs args
576577objCallAsConstructor f args = do
577578 rargs <- makeArgs args >>= liftIO . Array.fromListIO
578579 liftIO $ js_new f rargs
579−foreign import javascript safe "\
580+foreign import javascript unsafe "\
580581 switch($2.length) {\
581582 case 0 : $r = new $1(); break;\
582583 case 1 : $r = new $1($2[0]); break;\
jsaddle/src/Language/Javascript/JSaddle/Run.hs+3−17
@@ -73,23 +73,10 @@ import Language.Javascript.JSaddle.Types
7373 Object(..), JSValueReceived(..), JSM(..), Batch(..), JSValueForSend(..), syncPoint, syncAfter, sendCommand)
7474import Language.Javascript.JSaddle.Exception (JSException(..))
7575import 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
7976import GHC.Stats (getGCStatsEnabled, getGCStats, GCStats(..))
80−#endif
8177import Data.Foldable (forM_)
8278#endif
8379
84−#ifndef ghcjs_HOST_OS
85−#if MIN_VERSION_base(4,11,0)
86−currentBytesUsed = gcdetails_live_bytes . gc
87−#else
88−getRTSStatsEnabled = getGCStatsEnabled
89−getRTSStats = getGCStats
90−#endif
91−#endif
92−
9380-- | Enable (or disable) JSaddle logging
9481enableLogging :: Bool -> JSM ()
9582#ifdef ghcjs_HOST_OS
@@ -206,8 +193,8 @@ runJavaScript sendBatch entryPoint = do
206193 logInfo s =
207194 readIORef loggingEnabled >>= \case
208195 True -> do
209− currentBytesUsedStr <- getRTSStatsEnabled >>= \case
210− True -> show . currentBytesUsed <$> getRTSStats
196+ currentBytesUsedStr <- getGCStatsEnabled >>= \case
197+ True -> show . currentBytesUsed <$> getGCStats
211198 False -> return "??"
212199 cbCount <- M.size <$> readTVarIO callbacks
213200 putStrLn . s $ "M " <> currentBytesUsedStr <> " CB " <> show cbCount <> " "
@@ -227,11 +214,10 @@ runJavaScript sendBatch entryPoint = do
227214 zipWithM_ putMVar resultMVars results
228215 forM_ callbacksToFree $ \(JSValueReceived val) ->
229216 atomically (modifyTVar' callbacks (M.delete val))
230− (_, Failure callbacksToFree results exception err) -> do
217+ (_, Failure callbacksToFree results exception) -> do
231218 -- The exception will only be rethrown in Haskell if/when one of the
232219 -- missing results (if any) is evaluated.
233220 putStrLn "A JavaScript exception was thrown! (may not reach Haskell code)"
234− putStrLn err
235221 zipWithM_ putMVar resultMVars $ results <> repeat (ThrowJSValue exception)
236222 forM_ callbacksToFree $ \(JSValueReceived val) ->
237223 atomically (modifyTVar' callbacks (M.delete val))
jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs+38−7
@@ -13,6 +13,7 @@
1313
1414module Language.Javascript.JSaddle.Run.Files (
1515 indexHtml
16+ , jsaddleJs
1617 , initState
1718 , runBatch
1819 , ghcjsHelpers
@@ -41,7 +42,7 @@ initState = "\
4142 \ jsaddle_values.set(1, undefined);\n\
4243 \ jsaddle_values.set(2, false);\n\
4344 \ jsaddle_values.set(3, true);\n\
44− \ jsaddle_values.set(4, globalThis);\n\
45+ \ jsaddle_values.set(4, window);\n\
4546 \ var jsaddle_index = 100;\n\
4647 \ var expectedBatch = 1;\n\
4748 \ var lastResults = [0, {\"tag\": \"Success\", \"contents\": [[], []]}];\n\
@@ -93,10 +94,6 @@ runBatch send sendSync = "\
9394 \ var n = d.contents[1];\n\
9495 \ jsaddle_values.set(n, d.contents[0]);\n\
9596 \ 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\
10097 \ case \"GetPropertyByName\":\n\
10198 \ var n = d.contents[2];\n\
10299 \ jsaddle_values.set(n, jsaddle_values.get(d.contents[0])[d.contents[1]]);\n\
@@ -315,8 +312,7 @@ runBatch send sendSync = "\
315312 \ catch (err) {\n\
316313 \ var n = ++jsaddle_index;\n\
317314 \ 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\
315+ \ " <> send "{\"tag\": \"BatchResults\", \"contents\": [batch[2], {\"tag\": \"Failure\", \"contents\": [callbacksToFree, results, n]}]}" <> "\n\
320316 \ }\n\
321317 \ if(inCallback == 1) {\n\
322318 \ while(asyncBatch !== null) {\n\
@@ -337,6 +333,41 @@ runBatch send sendSync = "\
337333 \ runBatch(batch);\n\
338334 \"
339335
336+-- Use this to generate this string for embedding
337+-- sed -e 's|\\|\\\\|g' -e 's|^| \\|' -e 's|$|\\n\\|' -e 's|"|\\"|g' data/jsaddle.js | pbcopy
338+jsaddleJs :: ByteString
339+jsaddleJs = "\
340+ \if(typeof global !== \"undefined\") {\n\
341+ \ global.window = global;\n\
342+ \ global.WebSocket = require('ws');\n\
343+ \}\n\
344+ \\n\
345+ \var connect = function() {\n\
346+ \ var wsaddress =\n\
347+ \ typeof window.location === \"undefined\"\n\
348+ \ ? \"ws://localhost:3709/\"\n\
349+ \ : window.location.protocol.replace('http', 'ws')+\"//\"+window.location.hostname+(window.location.port?(\":\"+window.location.port):\"\");\n\
350+ \\n\
351+ \ var ws = new WebSocket(wsaddress);\n\
352+ \\n\
353+ \ ws.onopen = function(e) {\n\
354+ \ " <> initState <> "\
355+ \\n\
356+ \ ws.onmessage = function(e) {\n\
357+ \ var batch = JSON.parse(e.data);\n\
358+ \\n\
359+ \ " <> runBatch (\a -> "ws.send(JSON.stringify(" <> a <> "));") Nothing <> "\
360+ \ };\n\
361+ \ };\n\
362+ \ ws.onerror = function() {\n\
363+ \ setTimeout(connect, 1000);\n\
364+ \ };\n\
365+ \}\n\
366+ \\n\
367+ \ " <> ghcjsHelpers <> "\
368+ \connect();\n\
369+ \"
370+
340371ghcjsHelpers :: ByteString
341372ghcjsHelpers = "\
342373 \function h$isNumber(o) {\
jsaddle/src/Language/Javascript/JSaddle/Types.hs+2−10
@@ -124,7 +124,6 @@ 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
128127#endif
129128
130129#if MIN_VERSION_base(4,9,0) && defined(CHECK_UNCHECKED)
@@ -166,7 +165,7 @@ data JSContextRef = JSContextRef {
166165type JSM = IO
167166#else
168167newtype JSM a = JSM { unJSM :: ReaderT JSContextRef IO a }
169− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadThrow, MonadUnliftIO, Fail.MonadFail)
168+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadThrow, MonadUnliftIO)
170169
171170instance MonadCatch JSM where
172171 t `catch` c = JSM (unJSM (syncAfter t) `catch` \e -> unJSM (c e))
@@ -179,13 +178,6 @@ instance MonadMask JSM where
179178 JSM $ uninterruptibleMask $ \unmask -> unJSM (a $ q unmask)
180179 where q :: (ReaderT JSContextRef IO a -> ReaderT JSContextRef IO a) -> JSM a -> JSM a
181180 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
189181
190182#endif
191183
@@ -504,7 +496,7 @@ instance ToJSON Result where
504496instance FromJSON Result
505497
506498data BatchResults = Success [JSValueReceived] [Result]
507− | Failure [JSValueReceived] [Result] JSValueReceived String
499+ | Failure [JSValueReceived] [Result] JSValueReceived
508500 deriving (Show, Generic)
509501
510502instance ToJSON BatchResults where
nix/hix.nixdeleted−8
@@ -1,8 +0,0 @@
1−{pkgs, ...}:
2−{ modules = [({pkgs, ...}: {
3− packages.webkit2gtk3-javascriptcore.components.library.doHaddock = false;
4− })];
5− shell.buildInputs = [ pkgs.nodejs ];
6− shell.tools.cabal = {};
7− shell.crossPlatforms = p: [ p.ghcjs ];
8−}
travis/build.sh+12−2
@@ -6,8 +6,18 @@ GHCVER=`ghc --numeric-version`
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+
913cabal update
10−cabal new-build 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
14+cabal new-build --constraint='Cabal>=1.24.2.0' 'jsaddle-warp:lib:jsaddle-warp' 'jsaddle-warp:test:test-tool'
1115cd jsaddle-warp
12−GHC_PACKAGE_PATH=/opt/ghc/$GHCVER/lib/ghc-$GHCVER/package.conf.d:~/.cabal/store/ghc-$GHCVER/package.db:../dist-newstyle/packagedb/ghc-$GHCVER ../dist-newstyle/build/x86_64-linux/ghc-$GHCVER/jsaddle-warp-$JSADDLE_WARP_VERSION/t/test-tool/build/test-tool/test-tool ../jsaddle
16+GHC_PACKAGE_PATH=/opt/ghc/$GHCVER/lib/ghc-$GHCVER/package.conf.d:~/.cabal/store/ghc-$GHCVER/package.db:./dist-newstyle/packagedb/ghc-$GHCVER jsaddle_datadir=`pwd` ../dist-newstyle/build/jsaddle-warp-$JSADDLE_WARP_VERSION/build/test-tool/test-tool ../jsaddle-warp
17+
18+# update the cache
19+rm -rf .cabal
20+cp -a /root/.cabal ./
21+rm -rf .ghc
22+cp -a /root/.ghc ./
1323