on develop help sign in
Add Foreign.JavaScript.TH and modify everything else to work with it
commit9156be6fb5288b561b8ef0a148a30d911765f368 authorRyan Trinkle <ryan.trinkle@gmail.com> dateMon 22 Jun 2015, 19:28 (11 years ago) parent543547b
8 files changed+442−50
default.nix+2 reflex-dom.cabal+7−2 src-ghc/Reflex/Dom/Xhr/Foreign.hs+1−1 src-ghcjs/Reflex/Dom/Xhr/Foreign.hs−1 src/Foreign/JavaScript/TH.hs+372 src/Reflex/Dom/Class.hs+12−3 src/Reflex/Dom/Internal.hs+47−42 src/Reflex/Dom/Xhr.hs+1−1
default.nix+2
@@ -2,6 +2,7 @@
22, mtl, ref-tf, reflex, safe, text, these
33, transformers, data-default, semigroups, aeson
44, ghc, webkitgtk3-javascriptcore, exception-transformers
5+, webkitgtk24x
56}:
67
78mkDerivation {
@@ -24,5 +25,6 @@ mkDerivation {
2425 aeson
2526 exception-transformers
2627 ] ++ (if (ghc.pname or null) == "ghcjs" then [ ] else [ webkitgtk3-javascriptcore ]);
28+ pkgconfigDepends = if (ghc.pname or null) == "ghcjs" then [ ] else [ webkitgtk24x ];
2729 license = null;
2830}
reflex-dom.cabal+7−2
@@ -20,6 +20,7 @@ library
2020 hs-source-dirs: src
2121 build-depends:
2222 base >= 4.7 && < 4.9,
23+ template-haskell,
2324 reflex == 0.2.*,
2425 dependent-sum == 0.2.*,
2526 dependent-map == 0.1.*,
@@ -35,7 +36,7 @@ library
3536 text == 1.2.*,
3637 bytestring == 0.10.*,
3738 data-default == 0.5.*,
38− aeson == 0.8.*,
39+ aeson >= 0.8 && < 0.10,
3940 time == 1.5.*,
4041 exception-transformers == 0.4.*,
4142 directory == 1.2.*
@@ -44,7 +45,8 @@ library
4445 hs-source-dirs: src-ghcjs
4546 cpp-options: -D__GHCJS__
4647 build-depends:
47− ghcjs-base
48+ ghcjs-base,
49+ hashable == 1.2.*
4850 else
4951 hs-source-dirs: src-ghc
5052 build-depends:
@@ -52,6 +54,8 @@ library
5254 gtk3 == 0.13.*,
5355 webkitgtk3 == 0.13.*,
5456 webkitgtk3-javascriptcore == 0.13.*
57+ pkgconfig-depends:
58+ webkitgtk-3.0 >= 1.8
5559
5660 exposed-modules:
5761 Reflex.Dom
@@ -63,6 +67,7 @@ library
6367 Reflex.Dom.Widget.Lazy
6468 Reflex.Dom.Xhr
6569 Reflex.Dom.Time
70+ Foreign.JavaScript.TH
6671 other-modules:
6772 Reflex.Dom.Internal.Foreign
6873 Reflex.Dom.Xhr.Foreign
src-ghc/Reflex/Dom/Xhr/Foreign.hs+1−1
@@ -14,7 +14,7 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
1414
1515data XMLHttpRequest
1616 = XMLHttpRequest { xhrValue :: JSValueRef
17− , xhrContext :: JSContextRef
17+ , xhrContext :: JSContextRef --TODO: Get rid of this
1818 }
1919 deriving (Eq, Ord)
2020
src-ghcjs/Reflex/Dom/Xhr/Foreign.hs−1
@@ -32,7 +32,6 @@ castTo gtype objTypeName obj =
3232 -> unsafeCastGObject gobj
3333 | otherwise -> error $ "Cannot cast object to " ++ objTypeName
3434
35−
3635newtype XMLHttpRequest = XMLHttpRequest { unXMLHttpRequest :: JSRef XMLHttpRequest }
3736
3837instance Eq XMLHttpRequest where
src/Foreign/JavaScript/TH.hsnew file+372
@@ -0,0 +1,372 @@
1+{-# LANGUAGE ForeignFunctionInterface, CPP, TemplateHaskell, TypeFamilies, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, StandaloneDeriving, GeneralizedNewtypeDeriving, ExistentialQuantification, FunctionalDependencies, EmptyDataDecls, FlexibleContexts, RankNTypes #-}
2+#ifdef __GHCJS__
3+{-# LANGUAGE JavaScriptFFI #-}
4+#endif
5+module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
6+ , Safety (..)
7+ ) where
8+
9+import Language.Haskell.TH
10+
11+#ifdef __GHCJS__
12+import qualified GHCJS.Marshal as JS
13+import qualified GHCJS.Foreign as JS
14+import qualified GHCJS.Types as JS
15+import GHCJS.DOM.Types
16+import Data.Word
17+import Foreign.Ptr
18+import Foreign.C.Types
19+import Data.Hashable
20+#else
21+import System.Glib.FFI hiding (withForeignPtr)
22+import Graphics.UI.Gtk.WebKit.WebView
23+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
24+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
25+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
26+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
27+import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
28+import Graphics.UI.Gtk.WebKit.DOM.Node
29+import Graphics.UI.Gtk.WebKit.Types
30+#endif
31+
32+import Control.Monad
33+import Control.Monad.Fix
34+import Control.Monad.IO.Class
35+import Control.Monad.Exception
36+import Foreign.Marshal
37+import Data.ByteString (ByteString)
38+import qualified Data.ByteString as BS
39+import Control.Concurrent
40+import Data.Coerce
41+
42+import Reflex.Dom (WithWebView, askWebView, runWithWebView, unWebViewSingleton)
43+
44+#ifdef __GHCJS__
45+type JSFFI_Internal = JS.JSArray (JSRef JSCtx_IO) -> IO (JS.JSRef (JSRef JSCtx_IO))
46+newtype JSFFI = JSFFI JSFFI_Internal
47+#else
48+newtype JSFFI = JSFFI String
49+#endif
50+
51+newtype JSFun x = JSFun { unJSFun :: JSRef x }
52+
53+instance ToJS x (JSFun x) where
54+ withJS (JSFun r) f = f r
55+
56+instance FromJS x (JSFun x) where
57+ fromJS = return . JSFun
58+
59+class IsJSContext x where
60+ data JSRef x
61+
62+class (MonadIO (JSM m), MonadFix (JSM m), MonadJS x (JSM m)) => HasJS x m | m -> x where
63+ type JSM m :: * -> *
64+ liftJS :: JSM m a -> m a
65+
66+-- | A Monad that is capable of executing JavaScript
67+class Monad m => MonadJS x m | m -> x where
68+ runJS :: JSFFI -> [JSRef x] -> m (JSRef x)
69+ forkJS :: m () -> m ThreadId
70+ mkJSUndefined :: m (JSRef x)
71+ isJSNull :: JSRef x -> m Bool
72+ isJSUndefined :: JSRef x -> m Bool
73+ fromJSString :: JSRef x -> m String
74+ fromJSArray :: JSRef x -> m [JSRef x]
75+ fromJSNumber :: JSRef x -> m Double
76+ withJSString :: String -> (JSRef x -> m r) -> m r
77+ withJSNumber :: Double -> (JSRef x -> m r) -> m r
78+ withJSArray :: [JSRef x] -> (JSRef x -> m r) -> m r
79+ withJSUint8Array :: MonadJS x m => ByteString -> (JSUint8Array x -> m r) -> m r
80+ -- | Create a JSFun with zero arguments; should be equilvant to `syncCallback AlwaysRetain True` in GHCJS
81+ mkJSFun :: ([JSRef x] -> m (JSRef x)) -> m (JSFun x) --TODO: Support 'this', exceptions
82+ freeJSFun :: JSFun x -> m ()
83+ setJSProp :: String -> JSRef x -> JSRef x -> m ()
84+ withJSNode :: Node -> (JSRef x -> m r) -> m r
85+
86+#ifdef __GHCJS__
87+
88+data JSCtx_IO
89+
90+instance IsJSContext JSCtx_IO where
91+ newtype JSRef JSCtx_IO = JSRef_IO { unJSRef_IO :: JS.JSRef (JSRef JSCtx_IO) }
92+
93+instance MonadJS JSCtx_IO IO where
94+ runJS (JSFFI f) l = liftM JSRef_IO . f =<< JS.toArray (coerce l)
95+ forkJS = forkIO
96+ mkJSUndefined = return $ JSRef_IO JS.jsUndefined
97+ isJSNull (JSRef_IO r) = return $ JS.isNull r
98+ isJSUndefined (JSRef_IO r) = return $ JS.isUndefined r
99+ fromJSString (JSRef_IO r) = return $ JS.fromJSString $ JS.castRef r
100+ fromJSArray (JSRef_IO r) = liftM coerce $ JS.fromArray $ JS.castRef r
101+ fromJSNumber (JSRef_IO r) = do
102+ Just n <- JS.fromJSRef $ JS.castRef r
103+ return n
104+ withJSString s f = f $ JSRef_IO $ JS.castRef $ JS.toJSString s
105+ withJSNumber n f = do
106+ r <- JS.toJSRef n
107+ f $ JSRef_IO $ JS.castRef r
108+ withJSArray l f = do
109+ r <- JS.toArray $ coerce l
110+ f $ JSRef_IO $ JS.castRef r
111+ withJSUint8Array payload f = BS.useAsCString payload $ \cStr -> do
112+ ba <- extractByteArray cStr $ BS.length payload
113+ f $ JSUint8Array $ JSRef_IO ba
114+ mkJSFun f = do
115+ cb <- JS.syncCallback1 JS.AlwaysRetain True $ \args -> do
116+ l <- JS.fromArray args
117+ JSRef_IO result <- f $ coerce l
118+ return $ JS.castRef result
119+ liftM (JSFun . JSRef_IO) $ funWithArguments cb
120+ freeJSFun (JSFun (JSRef_IO r)) = JS.release $ JS.castRef r
121+ setJSProp s (JSRef_IO o) (JSRef_IO v) = JS.setProp s o v
122+ withJSNode n f = f $ JSRef_IO $ JS.castRef $ unNode n
123+
124+foreign import javascript unsafe "new Uint8Array($1_1.buf, $1_2, $2)" extractByteArray :: Ptr CChar -> Int -> IO (JS.JSRef (JSRef JSCtx_IO))
125+
126+foreign import javascript unsafe "function(){ return $1(arguments); }" funWithArguments :: JS.JSFun (JS.JSArray (JS.JSRef b) -> IO a) -> IO (JS.JSRef (JSRef JSCtx_IO))
127+
128+#else
129+
130+askJSContext :: MonadIO m => WithWebView x m JSContextRef
131+askJSContext = do
132+ wv <- askWebView
133+ liftIO $ webFrameGetGlobalContext =<< webViewGetMainFrame (unWebViewSingleton wv)
134+
135+-- | Make a JS string available that is not wrapped up as a JSValueRef
136+withJSStringRaw :: MonadAsyncException m => String -> (JSStringRef -> m r) -> m r
137+withJSStringRaw str a = do
138+ bracket (liftIO $ jsstringcreatewithutf8cstring str) (liftIO . jsstringrelease) $ \strRef -> do
139+ a strRef
140+
141+fromJSNumberRaw :: MonadIO m => JSValueRef -> WithWebView x m Double
142+fromJSNumberRaw val = do
143+ jsContext <- askJSContext
144+ liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
145+
146+foreign import ccall "wrapper" mkJSObjectCallAsFunctionCallback :: JSObjectCallAsFunctionCallback' -> IO (FunPtr JSObjectCallAsFunctionCallback')
147+
148+data JSCtx_JavaScriptCore x
149+
150+instance IsJSContext (JSCtx_JavaScriptCore x) where
151+ newtype JSRef (JSCtx_JavaScriptCore x) = JSRef_JavaScriptCore { unJSRef_JavaScriptCore :: JSValueRef }
152+
153+instance MonadIO m => HasJS (JSCtx_JavaScriptCore x) (WithWebView x m) where
154+ type JSM (WithWebView x m) = WithWebView x IO
155+ liftJS a = do
156+ wv <- askWebView
157+ liftIO $ runWithWebView a wv
158+
159+instance MonadJS (JSCtx_JavaScriptCore x) (WithWebView x IO) where
160+ runJS (JSFFI body) args = do
161+ jsContext <- askJSContext
162+ withJSArray args $ \(JSRef_JavaScriptCore this) -> liftIO $ do
163+ result <- bracket (jsstringcreatewithutf8cstring body) jsstringrelease $ \script -> jsevaluatescript jsContext script this nullPtr 1 nullPtr
164+ return $ JSRef_JavaScriptCore result --TODO: Protect and unprotect result
165+ forkJS a = do
166+ wv <- askWebView
167+ liftIO $ forkIO $ runWithWebView a wv
168+ mkJSUndefined = do
169+ jsContext <- askJSContext
170+ liftM JSRef_JavaScriptCore $ liftIO $ jsvaluemakeundefined jsContext
171+ isJSNull (JSRef_JavaScriptCore r) = do
172+ jsContext <- askJSContext
173+ liftIO $ jsvalueisnull jsContext r
174+ isJSUndefined (JSRef_JavaScriptCore r) = do
175+ jsContext <- askJSContext
176+ liftIO $ jsvalueisundefined jsContext r
177+ fromJSString (JSRef_JavaScriptCore r) = do
178+ jsContext <- askJSContext
179+ liftIO $ do
180+ s <- jsvaluetostringcopy jsContext r nullPtr --TODO: Deal with exceptions
181+ l <- jsstringgetmaximumutf8cstringsize s
182+ allocaBytes (fromIntegral l) $ \ps -> do
183+ _ <- jsstringgetutf8cstring'_ s ps (fromIntegral l)
184+ peekCString ps
185+ withJSString str a = do
186+ jsContext <- askJSContext
187+ withJSStringRaw str $ \strRef -> do
188+ valRef <- liftIO $ jsvaluemakestring jsContext strRef
189+ a $ JSRef_JavaScriptCore valRef --TODO: Protect/unprotect valRef
190+ withJSNumber n a = do
191+ jsContext <- askJSContext
192+ valRef <- liftIO $ jsvaluemakenumber jsContext n
193+ a $ JSRef_JavaScriptCore valRef --TODO: Protect/unprotect valRef
194+ withJSArray elems a = do
195+ jsContext <- askJSContext
196+ let numElems = length elems
197+ bracket (liftIO $ mallocArray numElems) (liftIO . free) $ \elemsArr -> do
198+ liftIO $ pokeArray elemsArr $ coerce elems
199+ bracket (liftIO $ jsobjectmakearray jsContext (fromIntegral numElems) elemsArr nullPtr) (const $ return ()) $ \arrRef -> do --TODO: Do we need to protect/unprotect the array object?
200+ a $ JSRef_JavaScriptCore arrRef --TODO: Protect/unprotect valRef
201+ --TODO: When supported by webkitgtk3-javascriptcore, go directly from C string to Uint8Array without creating each item individually
202+ withJSUint8Array payload f = withJSArrayFromList (BS.unpack payload) $ \x -> do
203+ payloadRef <- runJS (JSFFI "new Uint8Array(this[0])") [x]
204+ f $ JSUint8Array payloadRef
205+ fromJSArray (JSRef_JavaScriptCore a) = do
206+ jsContext <- askJSContext
207+ lenRef <- withJSStringRaw "length" $ \lengthStr -> do
208+ liftIO $ jsobjectgetproperty jsContext a lengthStr nullPtr --TODO: Exceptions
209+ lenDouble <- fromJSNumberRaw lenRef
210+ let len = round lenDouble
211+ liftIO $ forM [0..len-1] $ \i -> do
212+ liftM JSRef_JavaScriptCore $ jsobjectgetpropertyatindex jsContext a i nullPtr --TODO: Exceptions
213+ fromJSNumber (JSRef_JavaScriptCore val) = do
214+ jsContext <- askJSContext
215+ liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
216+ mkJSFun a = do
217+ wv <- askWebView
218+ jsContext <- askJSContext
219+ cb <- liftIO $ mkJSObjectCallAsFunctionCallback $ \_ _ _ argc argv _ -> do --TODO: Use the exception pointer to handle haskell exceptions
220+ args <- forM [0..fromIntegral (argc-1)] $ \n -> do
221+ x <- peekElemOff argv n
222+ jsvalueprotect jsContext x
223+ return $ JSRef_JavaScriptCore x --TODO: Unprotect eventually
224+ liftM unJSRef_JavaScriptCore $ runWithWebView (a args) wv
225+ cbRef <- liftIO $ jsobjectmakefunctionwithcallback jsContext nullPtr cb
226+ liftIO $ jsvalueprotect jsContext cbRef
227+ return $ JSFun $ JSRef_JavaScriptCore cbRef
228+ freeJSFun (JSFun (JSRef_JavaScriptCore f)) = do
229+ jsContext <- askJSContext
230+ liftIO $ jsvalueunprotect jsContext f
231+ setJSProp propName (JSRef_JavaScriptCore objRef) (JSRef_JavaScriptCore valRef) = do
232+ withJSStringRaw propName $ \propNameRaw -> do
233+ jsContext <- askJSContext
234+ liftIO $ jsobjectsetproperty jsContext objRef propNameRaw valRef 0 nullPtr --TODO: property attribute, exceptions
235+ withJSNode (Node n) f = do
236+ jsContext <- askJSContext
237+ withForeignPtr n $ \nPtr -> do
238+ let acquire = liftIO $ do
239+ jsNode <- webkit_dom_node_to_js jsContext nPtr
240+ jsvalueprotect jsContext jsNode
241+ return jsNode
242+ release = liftIO . jsvalueunprotect jsContext
243+ bracket acquire release $ f . JSRef_JavaScriptCore
244+
245+withForeignPtr :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b
246+withForeignPtr fo io = do
247+ r <- io $ unsafeForeignPtrToPtr fo
248+ liftIO $ touchForeignPtr fo
249+ return r
250+
251+foreign import ccall unsafe "webkit_dom_node_to_js" webkit_dom_node_to_js :: JSContextRef -> Ptr Node -> IO JSValueRef
252+
253+#endif
254+
255+class FromJS x a where
256+ fromJS :: MonadJS x m => JSRef x -> m a
257+
258+instance FromJS x () where
259+ fromJS _ = return () --TODO: Should this do some kind of checking for the js value?
260+
261+instance FromJS x String where
262+ fromJS = fromJSString
263+
264+instance FromJS x a => FromJS x (Maybe a) where
265+ fromJS x = do
266+ n <- isJSNull x
267+ if n then return Nothing else liftM Just $ fromJS x
268+
269+class ToJS x a where
270+ withJS :: MonadJS x m => a -> (JSRef x -> m r) -> m r
271+
272+instance ToJS x (JSRef x) where
273+ withJS r = ($ r)
274+
275+instance FromJS x (JSRef x) where
276+ fromJS = return
277+
278+instance ToJS x String where
279+ withJS = withJSString
280+
281+newtype JSArray a = JSArray { unJSArray :: [a] }
282+
283+instance ToJS x a => ToJS x (JSArray a) where
284+ withJS = withJSArrayFromList . unJSArray
285+
286+instance FromJS x a => FromJS x (JSArray a) where
287+ fromJS = liftM JSArray . mapM fromJS <=< fromJSArray
288+
289+withJSArrayFromList :: (ToJS x a, MonadJS x m) => [a] -> (JSRef x -> m r) -> m r
290+withJSArrayFromList as f = go as []
291+ where go [] jsRefs = withJSArray (reverse jsRefs) f
292+ go (h:t) jsRefs = withJS h $ \hRef -> go t (hRef : jsRefs)
293+
294+newtype JSUint8Array x = JSUint8Array { unJSUint8Array :: JSRef x }
295+
296+instance ToJS x (JSUint8Array x) where
297+ withJS (JSUint8Array r) = ($ r)
298+
299+instance FromJS x (JSUint8Array x) where
300+ fromJS = return . JSUint8Array
301+
302+instance ToJS x Word8 where
303+ withJS n = withJSNumber $ fromIntegral n --TODO: Check things; throw exceptions
304+
305+instance ToJS x Int where
306+ withJS n = withJSNumber $ fromIntegral n --TODO: Check things; throw exceptions
307+
308+instance FromJS x Int where
309+ fromJS = liftM round . fromJSNumber --TODO: Check things; throw exceptions
310+
311+instance ToJS x Double where
312+ withJS = withJSNumber
313+
314+instance FromJS x Double where
315+ fromJS = fromJSNumber
316+
317+instance ToJS x Node where
318+ withJS = withJSNode
319+
320+
321+fromJSUint8Array :: MonadJS x m => JSUint8Array x -> m ByteString
322+fromJSUint8Array (JSUint8Array a) = do
323+ vals <- fromJSArray a
324+ doubles <- mapM fromJSNumber vals
325+ return $ BS.pack $ map round doubles
326+
327+importJS :: Safety -> String -> String -> Q Type -> Q [Dec]
328+importJS safety body name qt = do
329+ t <- qt
330+ let (argTypes, _) = parseType t
331+ argNames <- forM argTypes $ \_ -> do
332+ arg <- newName "arg"
333+ argRef <- newName "argRef"
334+ return (arg, argRef)
335+ (jsffiDecs, jsffiExp) <- mkJSFFI safety body
336+ let go [] = [| runJS $(return jsffiExp) $(listE $ map (varE . snd) argNames) >>= fromJS
337+ |]
338+ go ((arg, argRef) : args) = [| withJS $(varE arg) $ $(lamE [varP argRef] $ go args) |]
339+ e <- lamE (map (varP. fst) argNames) $ go argNames
340+ let n = mkName name
341+ return $ jsffiDecs ++
342+ [ SigD n t
343+ , ValD (VarP n) (NormalB e) []
344+ ]
345+
346+mkJSFFI :: Safety -> String -> Q ([Dec], Exp)
347+#ifdef __GHCJS__
348+mkJSFFI safety body = do
349+ -- n <- newName "jsffi" --TODO: Should use newName, but that doesn't seem to work with ghcjs
350+ let n = mkName $ "jsffi_" ++ show (abs (hash (show safety, body)))
351+ t <- [t| JSFFI_Internal |]
352+ let wrappedBody = "(function(){ return (" ++ body ++ "); }).apply($1)"
353+ let decs = [ForeignD $ ImportF JavaScript safety wrappedBody n t]
354+ e <- [| JSFFI $(varE n) |]
355+ return (decs, e)
356+#else
357+mkJSFFI _ body = do
358+ e <- [| JSFFI body |]
359+ return ([], e)
360+#endif
361+
362+parseType :: Type -> ([Type], Type)
363+parseType (ForallT _ [AppT (AppT (ConT monadJs) (VarT _)) (VarT m)] funType)
364+ | monadJs == ''MonadJS = go funType
365+ where go t = case t of
366+ AppT (AppT ArrowT arg) t' ->
367+ let (args, result) = go t'
368+ in (arg : args, result)
369+ AppT (VarT m') result
370+ | m' == m -> ([], result)
371+ _ -> error $ "parseType: can't parse type " ++ show t
372+parseType t = error $ "parseType: can't parse type " ++ show t
src/Reflex/Dom/Class.hs+12−3
@@ -28,9 +28,9 @@ keycodeEnter = 13
2828keycodeEscape :: Int
2929keycodeEscape = 27
3030
31−class ( Reflex t, MonadHold t m, MonadIO m, MonadAsyncException m, Functor m, MonadReflexCreateTrigger t m
31+class ( Reflex t, MonadHold t m, MonadIO m, Functor m, MonadReflexCreateTrigger t m
3232 , HasDocument m, HasWebView m, HasWebView (WidgetHost m), HasWebView (GuiAction m)
33− , MonadIO (WidgetHost m), MonadAsyncException (WidgetHost m), MonadIO (GuiAction m), MonadAsyncException (GuiAction m), Functor (WidgetHost m), MonadSample t (WidgetHost m)
33+ , MonadIO (WidgetHost m), MonadIO (GuiAction m), Functor (WidgetHost m), MonadSample t (WidgetHost m)
3434 , HasPostGui t (GuiAction m) (WidgetHost m), HasPostGui t (GuiAction m) m, HasPostGui t (GuiAction m) (GuiAction m)
3535 , MonadRef m, MonadRef (WidgetHost m)
3636 , Ref m ~ Ref IO, Ref (WidgetHost m) ~ Ref IO --TODO: Eliminate this reliance on IO
@@ -55,13 +55,22 @@ instance HasDocument m => HasDocument (ReaderT r m) where
5555instance HasDocument m => HasDocument (StateT r m) where
5656 askDocument = lift askDocument
5757
58+-- | A singleton type for a given WebView; we use this to statically guarantee that different WebViews (and thus different javscript contexts) don't get mixed up
59+newtype WebViewSingleton x = WebViewSingleton { unWebViewSingleton :: WebView }
60+
61+withWebViewSingleton :: WebView -> (forall x. WebViewSingleton x -> r) -> r
62+withWebViewSingleton wv f = f $ WebViewSingleton wv
63+
5864class Monad m => HasWebView m where
59− askWebView :: m WebView
65+ type WebViewPhantom m :: *
66+ askWebView :: m (WebViewSingleton (WebViewPhantom m))
6067
6168instance HasWebView m => HasWebView (ReaderT r m) where
69+ type WebViewPhantom (ReaderT r m) = WebViewPhantom m
6270 askWebView = lift askWebView
6371
6472instance HasWebView m => HasWebView (StateT r m) where
73+ type WebViewPhantom (StateT r m) = WebViewPhantom m
6574 askWebView = lift askWebView
6675
6776newtype Restore m = Restore { restore :: forall a. m a -> IO a }
src/Reflex/Dom/Internal.hs+47−42
@@ -30,38 +30,38 @@ import qualified Data.Text as T
3030import Data.Text.Encoding
3131import Data.Monoid ((<>))
3232
33−data GuiEnv t h
33+data GuiEnv t h x
3434 = GuiEnv { _guiEnvDocument :: !HTMLDocument
3535 , _guiEnvPostGui :: !(h () -> IO ())
3636 , _guiEnvRunWithActions :: !([DSum (EventTrigger t)] -> h ())
37− , _guiEnvWebView :: !WebView
37+ , _guiEnvWebView :: !(WebViewSingleton x)
3838 }
3939
4040--TODO: Poorly named
41−newtype Gui t h m a = Gui { unGui :: ReaderT (GuiEnv t h) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
41+newtype Gui t h x m a = Gui { unGui :: ReaderT (GuiEnv t h x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
4242
43−runGui :: Gui t h m a -> GuiEnv t h -> m a
43+runGui :: Gui t h x m a -> GuiEnv t h x -> m a
4444runGui (Gui g) env = runReaderT g env
4545
46−instance MonadTrans (Gui t h) where
46+instance MonadTrans (Gui t h x) where
4747 lift = Gui . lift
4848
49−instance MonadRef m => MonadRef (Gui t h m) where
50− type Ref (Gui t h m) = Ref m
49+instance MonadRef m => MonadRef (Gui t h x m) where
50+ type Ref (Gui t h x m) = Ref m
5151 newRef = lift . newRef
5252 readRef = lift . readRef
5353 writeRef r = lift . writeRef r
5454
55−instance MonadAtomicRef m => MonadAtomicRef (Gui t h m) where
55+instance MonadAtomicRef m => MonadAtomicRef (Gui t h x m) where
5656 atomicModifyRef r f = lift $ atomicModifyRef r f
5757
58−instance MonadSample t m => MonadSample t (Gui t h m) where
58+instance MonadSample t m => MonadSample t (Gui t h x m) where
5959 sample b = lift $ sample b
6060
61−instance MonadHold t m => MonadHold t (Gui t h m) where
61+instance MonadHold t m => MonadHold t (Gui t h x m) where
6262 hold a0 e = lift $ hold a0 e
6363
64−instance (Reflex t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (Gui t h m) where
64+instance (Reflex t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (Gui t h x m) where
6565 newEventWithTrigger initializer = lift $ newEventWithTrigger initializer
6666
6767data WidgetEnv
@@ -79,24 +79,26 @@ liftM concat $ mapM makeLenses
7979 , ''GuiEnv
8080 ]
8181
82−instance Monad m => HasDocument (Gui t h m) where
82+instance Monad m => HasDocument (Gui t h x m) where
8383 askDocument = Gui $ view guiEnvDocument
8484
8585instance HasDocument m => HasDocument (Widget t m) where
8686 askDocument = lift askDocument
8787
88−instance Monad m => HasWebView (Gui t h m) where
88+instance Monad m => HasWebView (Gui t h x m) where
89+ type WebViewPhantom (Gui t h x m) = x
8990 askWebView = Gui $ view guiEnvWebView
9091
91−instance MonadIORestore m => MonadIORestore (Gui t h m) where
92+instance MonadIORestore m => MonadIORestore (Gui t h x m) where
9293 askRestore = Gui $ do
9394 r <- askRestore
9495 return $ Restore $ restore r . unGui
9596
9697instance HasWebView m => HasWebView (Widget t m) where
98+ type WebViewPhantom (Widget t m) = WebViewPhantom m
9799 askWebView = lift askWebView
98100
99−instance (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h (Gui t h m) where
101+instance (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h (Gui t h x m) where
100102 askPostGui = Gui $ view guiEnvPostGui
101103 askRunWithActions = Gui $ view guiEnvRunWithActions
102104
@@ -134,12 +136,12 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Widget t m)
134136 newEventWithTrigger = lift . newEventWithTrigger
135137
136138instance ( MonadRef m, Ref m ~ Ref IO, MonadRef h, Ref h ~ Ref IO --TODO: Shouldn't need to be IO
137− , MonadIO m, MonadAsyncException m, MonadIO h, MonadAsyncException h, Functor m
139+ , MonadIO m, MonadIO h, Functor m
138140 , ReflexHost t, MonadReflexCreateTrigger t m, MonadSample t m, MonadHold t m
139141 , MonadFix m, HasWebView h, HasPostGui t h h
140− ) => MonadWidget t (Widget t (Gui t h m)) where
141− type WidgetHost (Widget t (Gui t h m)) = Gui t h m
142− type GuiAction (Widget t (Gui t h m)) = h
142+ ) => MonadWidget t (Widget t (Gui t h x m)) where
143+ type WidgetHost (Widget t (Gui t h x m)) = Gui t h x m
144+ type GuiAction (Widget t (Gui t h x m)) = h
143145 askParent = Widget $ view widgetEnvParent
144146 --TODO: Use types to separate cohorts of possibly-recursive events/behaviors
145147 -- | Schedule an action to occur after the current cohort has been built; this is necessary because Behaviors built in the current cohort may not be read until after it is complete
@@ -160,7 +162,7 @@ instance ( MonadRef m, Ref m ~ Ref IO, MonadRef h, Ref h ~ Ref IO --TODO: Should
160162-- runWidget :: (Monad m, IsNode n, Reflex t) => n -> Widget t m a -> m (a, Event t (m ()))
161163 getRunWidget = return runWidget
162164
163−runWidget :: (Monad m, Reflex t, IsNode n) => n -> Widget t (Gui t h m) a -> WidgetHost (Widget t (Gui t h m)) (a, WidgetHost (Widget t (Gui t h m)) (), Event t (WidgetHost (Widget t (Gui t h m)) ()))
165+runWidget :: (Monad m, Reflex t, IsNode n) => n -> Widget t (Gui t h x m) a -> WidgetHost (Widget t (Gui t h x m)) (a, WidgetHost (Widget t (Gui t h x m)) (), Event t (WidgetHost (Widget t (Gui t h x m)) ()))
164166runWidget rootElement w = do
165167 (result, WidgetState postBuild voidActions) <- runStateT (runReaderT (unWidget w) (WidgetEnv $ toNode rootElement)) (WidgetState (return ()) [])
166168 let voidAction = mergeWith (>>) voidActions
@@ -174,34 +176,36 @@ holdOnStartup a0 ma = do
174176 runFrameWithTriggerRef startupDoneTriggerRef a
175177 hold a0 startupDone
176178
177−mainWidget :: Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
178−mainWidget w = runWebGUI $ \webView -> do
179+mainWidget :: (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> IO ()
180+mainWidget w = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
179181 Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
180182 Just body <- documentGetBody doc
181− attachWidget body webView w
183+ attachWidget body webViewSing w
182184
183−mainWidgetWithHead :: Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
184−mainWidgetWithHead h b = runWebGUI $ \webView -> do
185+--TODO: The x's should be unified here
186+mainWidgetWithHead :: (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> IO ()
187+mainWidgetWithHead h b = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
185188 Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
186189 Just headElement <- liftM (fmap castToHTMLElement) $ documentGetHead doc
187− attachWidget headElement webView h
190+ attachWidget headElement webViewSing h
188191 Just body <- documentGetBody doc
189− attachWidget body webView b
192+ attachWidget body webViewSing b
190193
191−mainWidgetWithCss :: ByteString -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
192−mainWidgetWithCss css w = runWebGUI $ \webView -> do
194+mainWidgetWithCss :: ByteString -> (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> IO ()
195+mainWidgetWithCss css w = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
193196 Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
194197 Just headElement <- liftM (fmap castToHTMLElement) $ documentGetHead doc
195198 htmlElementSetInnerHTML headElement $ "<style>" <> T.unpack (decodeUtf8 css) <> "</style>" --TODO: Fix this
196199 Just body <- documentGetBody doc
197− attachWidget body webView w
200+ attachWidget body webViewSing w
198201
199−newtype WithWebView m a = WithWebView { unWithWebView :: ReaderT WebView m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
202+newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
200203
201−instance (Monad m) => HasWebView (WithWebView m) where
204+instance (Monad m) => HasWebView (WithWebView x m) where
205+ type WebViewPhantom (WithWebView x m) = x
202206 askWebView = WithWebView ask
203207
204−instance HasPostGui t h m => HasPostGui t (WithWebView h) (WithWebView m) where
208+instance HasPostGui t h m => HasPostGui t (WithWebView x h) (WithWebView x m) where
205209 askPostGui = do
206210 postGui <- lift askPostGui
207211 webView <- askWebView
@@ -214,33 +218,34 @@ instance HasPostGui Spider SpiderHost SpiderHost where
214218 askPostGui = return $ \h -> liftIO $ runSpiderHost h
215219 askRunWithActions = return fireEvents
216220
217−instance MonadTrans WithWebView where
221+instance MonadTrans (WithWebView x) where
218222 lift = WithWebView . lift
219223
220−instance MonadRef m => MonadRef (WithWebView m) where
221− type Ref (WithWebView m) = Ref m
224+instance MonadRef m => MonadRef (WithWebView x m) where
225+ type Ref (WithWebView x m) = Ref m
222226 newRef = lift . newRef
223227 readRef = lift . readRef
224228 writeRef r = lift . writeRef r
225229
226−instance MonadAtomicRef m => MonadAtomicRef (WithWebView m) where
230+instance MonadAtomicRef m => MonadAtomicRef (WithWebView x m) where
227231 atomicModifyRef r = lift . atomicModifyRef r
228232
229−deriving instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView m)
230−instance MonadReflexHost t m => MonadReflexHost t (WithWebView m) where
233+deriving instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m)
234+
235+instance MonadReflexHost t m => MonadReflexHost t (WithWebView x m) where
231236 fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
232237 subscribeEvent = lift . subscribeEvent
233238 runFrame = lift . runFrame
234239 runHostFrame = lift . runHostFrame
235240
236−runWithWebView :: WithWebView m a -> WebView -> m a
241+runWithWebView :: WithWebView x m a -> WebViewSingleton x -> m a
237242runWithWebView = runReaderT . unWithWebView
238243
239−attachWidget :: (IsHTMLElement e) => e -> WebView -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) a -> IO a
244+attachWidget :: forall e x a. (IsHTMLElement e) => e -> WebViewSingleton x -> Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) a -> IO a
240245attachWidget rootElement wv w = runSpiderHost $ flip runWithWebView wv $ do --TODO: It seems to re-run this handler if the URL changes, even if it's only the fragment
241246 Just doc <- liftM (fmap castToHTMLDocument) $ liftIO $ nodeGetOwnerDocument rootElement
242247 frames <- liftIO newChan
243− rec let guiEnv = GuiEnv doc (writeChan frames . runSpiderHost . flip runWithWebView wv) runWithActions wv :: GuiEnv Spider (WithWebView SpiderHost)
248+ rec let guiEnv = GuiEnv doc (writeChan frames . runSpiderHost . flip runWithWebView wv) runWithActions wv :: GuiEnv Spider (WithWebView x SpiderHost) x
244249 runWithActions dm = do
245250 voidActionNeeded <- fireEventsAndRead dm $ do
246251 sequence =<< readEvent voidActionHandle
src/Reflex/Dom/Xhr.hs+1−1
@@ -70,7 +70,7 @@ newXMLHttpRequest req cb = do
7070 wv <- askWebView
7171 postGui <- askPostGui
7272 liftIO $ do
73− xhr <- xmlHttpRequestNew wv
73+ xhr <- xmlHttpRequestNew $ unWebViewSingleton wv
7474 let c = _xhrRequest_config req
7575 xmlHttpRequestOpen
7676 xhr