on develop help sign in
Add support for Blob and ArrayBuffer to XhrResponse
commite61e94b7b1061a52f6923e934f83b7508fec61b5 authorAli Abrar <aliabrar@gmail.com> dateSat 6 Feb 2016, 12:53 (11 years ago) parent822eb63
7 files changed+126−53
src-ghc/Reflex/Dom/Internal/Foreign.hs+38−9 src-ghc/Reflex/Dom/WebSocket/Foreign.hs+1−3 src-ghc/Reflex/Dom/Xhr/Foreign.hs+31−24 src-ghcjs/Reflex/Dom/Internal/Foreign.hs+21 src-ghcjs/Reflex/Dom/Xhr/Foreign.hs+24−14 src/Reflex/Dom/Xhr.hs+2−1 src/Reflex/Dom/Xhr/ResponseType.hs+9−2
src-ghc/Reflex/Dom/Internal/Foreign.hs+38−9
@@ -1,11 +1,16 @@
11{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables, LambdaCase #-}
22module Reflex.Dom.Internal.Foreign where
33
4−import Control.Lens hiding (set)
5−import GHCJS.DOM hiding (runWebGUI)
64import Control.Concurrent
5+import Control.Exception (bracket)
6+import Control.Lens hiding (set)
7+import Control.Monad
78import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
9+import Data.ByteString (ByteString)
10+import Data.List
11+import Foreign.Marshal hiding (void)
812import Foreign.Ptr
13+import GHCJS.DOM hiding (runWebGUI)
914import GHCJS.DOM.Navigator
1015import GHCJS.DOM.Window
1116import Graphics.UI.Gtk hiding (Widget)
@@ -14,14 +19,14 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1419import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1520import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
1621import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
17−import Graphics.UI.Gtk.WebKit.WebView
1822import Graphics.UI.Gtk.WebKit.Types hiding (Event, Widget)
19−import Graphics.UI.Gtk.WebKit.WebSettings
2023import Graphics.UI.Gtk.WebKit.WebFrame
2124import Graphics.UI.Gtk.WebKit.WebInspector
22−import Data.List
25+import Graphics.UI.Gtk.WebKit.WebSettings
26+import Graphics.UI.Gtk.WebKit.WebView
2327import System.Directory
2428import System.Glib.FFI hiding (void)
29+import qualified Data.ByteString as BS
2530
2631#ifndef mingw32_HOST_OS
2732import System.Posix.Signals
@@ -117,18 +122,42 @@ fromJSStringMaybe c t = do
117122 return $ Just s
118123
119124getLocationHost :: WebView -> IO String
120−getLocationHost wv = do
121− c <- webFrameGetGlobalContext =<< webViewGetMainFrame wv
125+getLocationHost wv = withWebViewContext wv $ \c -> do
122126 script <- jsstringcreatewithutf8cstring "location.host"
123127 lh <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
124128 lh' <- fromJSStringMaybe c lh
125129 return $ maybe "" id lh'
126130
127131getLocationProtocol :: WebView -> IO String
128−getLocationProtocol wv = do
129− c <- webFrameGetGlobalContext =<< webViewGetMainFrame wv
132+getLocationProtocol wv = withWebViewContext wv $ \c -> do
130133 script <- jsstringcreatewithutf8cstring "location.protocol"
131134 lp <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
132135 lp' <- fromJSStringMaybe c lp
133136 return $ maybe "" id lp'
134137
138+bsToArrayBuffer :: JSContextRef -> ByteString -> IO JSValueRef
139+bsToArrayBuffer c bs = do
140+ elems <- forM (BS.unpack bs) $ \x -> jsvaluemakenumber c $ fromIntegral x
141+ let numElems = length elems
142+ bracket (mallocArray numElems) free $ \elemsArr -> do
143+ pokeArray elemsArr elems
144+ a <- jsobjectmakearray c (fromIntegral numElems) elemsArr nullPtr
145+ newUint8Array <- jsstringcreatewithutf8cstring "new Uint8Array(this)"
146+ jsevaluatescript c newUint8Array a nullPtr 1 nullPtr
147+
148+bsFromArrayBuffer :: JSContextRef -> JSValueRef -> IO ByteString
149+bsFromArrayBuffer c a = do
150+ let getIntegral = fmap round . (\x -> jsvaluetonumber c x nullPtr)
151+ getByteLength <- jsstringcreatewithutf8cstring "this.byteLength"
152+ byteLength <- getIntegral =<< jsevaluatescript c getByteLength a nullPtr 1 nullPtr
153+ toUint8Array <- jsstringcreatewithutf8cstring "new Uint8Array(this)"
154+ uint8Array <- jsevaluatescript c toUint8Array a nullPtr 1 nullPtr
155+ getIx <- jsstringcreatewithutf8cstring "this[0][this[1]]"
156+ let arrayLookup i = do
157+ i' <- jsvaluemakenumber c (fromIntegral i)
158+ args <- toJSObject c [uint8Array, i']
159+ getIntegral =<< jsevaluatescript c getIx args nullPtr 1 nullPtr
160+ fmap BS.pack $ forM [0..byteLength-1] arrayLookup
161+
162+withWebViewContext :: WebView -> (JSContextRef -> IO a) -> IO a
163+withWebViewContext wv f = f =<< webFrameGetGlobalContext =<< webViewGetMainFrame wv
src-ghc/Reflex/Dom/WebSocket/Foreign.hs+1−3
@@ -15,7 +15,6 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
1515import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1616import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1717import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
18−import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
1918import Graphics.UI.Gtk.WebKit.WebView
2019import qualified Data.ByteString as BS
2120import qualified Data.Text as T
@@ -27,8 +26,7 @@ data JSWebSocket = JSWebSocket { wsValue :: JSValueRef
2726 }
2827
2928newWebSocket :: WebView -> String -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
30−newWebSocket wv url onMessage onOpen onClose = do
31− c <- webFrameGetGlobalContext =<< webViewGetMainFrame wv
29+newWebSocket wv url onMessage onOpen onClose = withWebViewContext wv $ \c -> do
3230 url' <- jsvaluemakestring c =<< jsstringcreatewithutf8cstring url
3331 newWSArgs <- toJSObject c [url']
3432 newWS <- jsstringcreatewithutf8cstring "(function(that) { var ws = new WebSocket(that[0]); ws['binaryType'] = 'arraybuffer'; return ws; })(this)"
src-ghc/Reflex/Dom/Xhr/Foreign.hs+31−24
@@ -1,6 +1,8 @@
11{-# LANGUAGE QuasiQuotes, ForeignFunctionInterface #-}
2+
23module Reflex.Dom.Xhr.Foreign where
34
5+import Control.Monad
46import qualified Data.Text as T
57import Data.Text (Text)
68import System.Glib.FFI
@@ -9,11 +11,11 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
911import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1012import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1113import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
12−import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
1314import Reflex.Dom.Xhr.ResponseType
1415import Reflex.Dom.Xhr.Exception
1516import Control.Concurrent.MVar
1617import Control.Exception.Base
18+import Graphics.UI.Gtk.WebKit.Types hiding (Text)
1719
1820import Reflex.Dom.Internal.Foreign
1921
@@ -25,23 +27,24 @@ data XMLHttpRequest
2527 }
2628 deriving (Eq, Ord)
2729
28−data XhrResponseBody = XhrResponseBody { unXhrResponseBody :: JSValueRef }
29−
3030stringToJSValue :: JSContextRef -> String -> IO JSValueRef
3131stringToJSValue ctx s = jsvaluemakestring ctx =<< jsstringcreatewithutf8cstring s
3232
33−toResponseType :: XhrResponseType -> String
34−toResponseType XhrResponseType_Default = ""
35−toResponseType XhrResponseType_ArrayBuffer = "arraybuffer"
36−toResponseType XhrResponseType_Blob = "blob"
37−toResponseType XhrResponseType_Document = "document"
38−toResponseType XhrResponseType_JSON = "json"
39−toResponseType XhrResponseType_Text = "text"
33+fromResponseType :: XhrResponseType -> String
34+fromResponseType XhrResponseType_Default = ""
35+fromResponseType XhrResponseType_ArrayBuffer = "arraybuffer"
36+fromResponseType XhrResponseType_Blob = "blob"
37+fromResponseType XhrResponseType_Text = "text"
38+
39+toResponseType :: String -> Maybe XhrResponseType
40+toResponseType "" = Just XhrResponseType_Default
41+toResponseType "arraybuffer" = Just XhrResponseType_ArrayBuffer
42+toResponseType "blob" = Just XhrResponseType_Blob
43+toResponseType "text" = Just XhrResponseType_Text
44+toResponseType _ = Nothing
4045
4146xmlHttpRequestNew :: WebView -> IO XMLHttpRequest
42−xmlHttpRequestNew wv = do
43− wf <- webViewGetMainFrame wv
44− jsContext <- webFrameGetGlobalContext wf
47+xmlHttpRequestNew wv = withWebViewContext wv $ \jsContext -> do
4548 xhrScript <- jsstringcreatewithutf8cstring "new XMLHttpRequest()"
4649 xhr' <- jsevaluatescript jsContext xhrScript nullPtr nullPtr 1 nullPtr
4750 jsvalueprotect jsContext xhr'
@@ -80,31 +83,35 @@ xmlHttpRequestGetReadyState xhr = do
8083 d <- jsvaluetonumber c rs nullPtr
8184 return $ truncate d
8285
86+xmlHttpRequestGetResponseType :: XMLHttpRequest -> IO (Maybe XhrResponseType)
87+xmlHttpRequestGetResponseType xhr = do
88+ script <- jsstringcreatewithutf8cstring "this.responseType"
89+ rt <- jsevaluatescript (xhrContext xhr) script (xhrValue xhr) nullPtr 1 nullPtr
90+ ms <- fromJSStringMaybe (xhrContext xhr) rt
91+ return $ join $ fmap toResponseType ms
92+
8393xmlHttpRequestGetResponse :: XMLHttpRequest -> IO (Maybe XhrResponseBody)
8494xmlHttpRequestGetResponse xhr = do
8595 let c = xhrContext xhr
96+ mrt <- xmlHttpRequestGetResponseType xhr
8697 script <- jsstringcreatewithutf8cstring "this.response"
8798 t <- jsevaluatescript c script (xhrValue xhr) nullPtr 1 nullPtr
8899 isNull <- jsvalueisnull c t
89100 case isNull of
90101 True -> return Nothing
91− False -> return $ Just $ XhrResponseBody t
102+ False -> case mrt of
103+ Just XhrResponseType_ArrayBuffer -> Just . XhrResponseBody_ArrayBuffer <$> bsFromArrayBuffer c t
104+ Just XhrResponseType_Blob -> Just . XhrResponseBody_Blob . Blob . castForeignPtr <$> newForeignPtr_ t
105+ Just XhrResponseType_Default -> fmap (XhrResponseBody_Default . T.pack) <$> fromJSStringMaybe c t
106+ Just XhrResponseType_Text -> fmap (XhrResponseBody_Text . T.pack) <$> fromJSStringMaybe c t
107+ _ -> return Nothing
92108
93109xmlHttpRequestGetResponseText :: XMLHttpRequest -> IO (Maybe Text)
94110xmlHttpRequestGetResponseText xhr = do
95111 let c = xhrContext xhr
96112 script <- jsstringcreatewithutf8cstring "this.responseText"
97113 t <- jsevaluatescript c script (xhrValue xhr) nullPtr 1 nullPtr
98− isNull <- jsvalueisnull c t
99− case isNull of
100− True -> return Nothing
101− False -> do
102− j <- jsvaluetostringcopy c t nullPtr
103− l <- jsstringgetmaximumutf8cstringsize j
104− s <- allocaBytes (fromIntegral l) $ \ps -> do
105− _ <- jsstringgetutf8cstring'_ j ps (fromIntegral l)
106− peekCString ps
107− return $ Just $ T.pack s
114+ fmap (fmap T.pack) $ fromJSStringMaybe c t
108115
109116xmlHttpRequestSend :: XMLHttpRequest -> Maybe String -> IO ()
110117xmlHttpRequestSend xhr payload = do
src-ghcjs/Reflex/Dom/Internal/Foreign.hs+21
@@ -4,9 +4,16 @@ module Reflex.Dom.Internal.Foreign ( module Reflex.Dom.Internal.Foreign
44 ) where
55
66import Control.Monad
7+import Data.ByteString (ByteString)
8+import Foreign
9+import Foreign.C
710import GHCJS.DOM
811import GHCJS.DOM.Types
912import GHCJS.Types
13+import JavaScript.TypedArray.ArrayBuffer as JS
14+import qualified Data.ByteString as BS
15+import qualified GHCJS.Buffer as JS
16+import qualified GHCJS.Marshal.Pure as JS
1017
1118quitWebView :: WebView -> IO ()
1219quitWebView = error "quitWebView: unimplemented in GHCJS"
@@ -20,3 +27,17 @@ foreign import javascript unsafe "location['protocol']" getLocationProtocol_ ::
2027
2128getLocationProtocol :: FromJSString r => a -> IO r
2229getLocationProtocol _ = liftM fromJSString getLocationProtocol_
30+
31+withWebViewContext :: a -> (a -> IO b) -> IO b
32+withWebViewContext a f = f a
33+
34+foreign import javascript safe "new DataView($3,$1,$2)" js_dataView :: Int -> Int -> JSVal -> JSVal
35+
36+foreign import javascript unsafe "new Uint8Array($1_1.buf, $1_2, $2)[\"buffer\"]" extractByteArray :: Ptr CChar -> Int -> IO JSVal
37+
38+bsToArrayBuffer :: a -> ByteString -> IO JSVal
39+bsToArrayBuffer _ bs = BS.useAsCString bs $ \cStr -> do
40+ extractByteArray cStr $ BS.length bs
41+
42+bsFromArrayBuffer :: a -> JSVal -> IO ByteString
43+bsFromArrayBuffer _ ab = liftM (JS.toByteString 0 Nothing . JS.createFromArrayBuffer) $ JS.unsafeFreeze $ JS.pFromJSVal ab
src-ghcjs/Reflex/Dom/Xhr/Foreign.hs+24−14
@@ -15,14 +15,11 @@ import GHCJS.DOM.XMLHttpRequest
1515import Data.Maybe (fromMaybe)
1616import GHCJS.DOM.EventTarget (dispatchEvent)
1717import GHCJS.DOM.EventM (EventM, on)
18−import GHCJS.DOM.JSFFI.XMLHttpRequest (XHRError(..))
19−import GHCJS.Types
18+import Reflex.Dom.Internal.Foreign
2019import Reflex.Dom.Xhr.Exception
2120import Reflex.Dom.Xhr.ResponseType
2221import Control.Exception (catch, throwIO)
2322
24−data XhrResponseBody = XhrResponseBody { unXhrResponseBody :: JSVal }
25−
2623prepareWebView :: WebView -> IO ()
2724prepareWebView _ = return ()
2825
@@ -114,16 +111,21 @@ xmlHttpRequestGetResponseXML = getResponseXML
114111xmlHttpRequestSetResponseType :: XMLHttpRequest -> XMLHttpRequestResponseType -> IO ()
115112xmlHttpRequestSetResponseType = setResponseType
116113
117−toResponseType :: XhrResponseType -> XMLHttpRequestResponseType
118−toResponseType XhrResponseType_Default = XMLHttpRequestResponseType
119−toResponseType XhrResponseType_ArrayBuffer = XMLHttpRequestResponseTypeArraybuffer
120−toResponseType XhrResponseType_Blob = XMLHttpRequestResponseTypeBlob
121−toResponseType XhrResponseType_Document = XMLHttpRequestResponseTypeDocument
122−toResponseType XhrResponseType_JSON = XMLHttpRequestResponseTypeJson
123−toResponseType XhrResponseType_Text = XMLHttpRequestResponseTypeText
114+fromResponseType :: XhrResponseType -> XMLHttpRequestResponseType
115+fromResponseType XhrResponseType_Default = XMLHttpRequestResponseType
116+fromResponseType XhrResponseType_ArrayBuffer = XMLHttpRequestResponseTypeArraybuffer
117+fromResponseType XhrResponseType_Blob = XMLHttpRequestResponseTypeBlob
118+fromResponseType XhrResponseType_Text = XMLHttpRequestResponseTypeText
119+
120+toResponseType :: XMLHttpRequestResponseType -> Maybe XhrResponseType
121+toResponseType XMLHttpRequestResponseType = Just XhrResponseType_Default
122+toResponseType XMLHttpRequestResponseTypeArraybuffer = Just XhrResponseType_ArrayBuffer
123+toResponseType XMLHttpRequestResponseTypeBlob = Just XhrResponseType_Blob
124+toResponseType XMLHttpRequestResponseTypeText = Just XhrResponseType_Text
125+toResponseType _ = Nothing
124126
125−xmlHttpRequestGetResponseType :: XMLHttpRequest -> IO XMLHttpRequestResponseType
126−xmlHttpRequestGetResponseType = getResponseType
127+xmlHttpRequestGetResponseType :: XMLHttpRequest -> IO (Maybe XhrResponseType)
128+xmlHttpRequestGetResponseType = fmap toResponseType . getResponseType
127129
128130xmlHttpRequestGetStatus :: XMLHttpRequest -> IO Word
129131xmlHttpRequestGetStatus = getStatus
@@ -137,5 +139,13 @@ xmlHttpRequestGetResponseURL = getResponseURL
137139xmlHttpRequestGetResponse :: XMLHttpRequest -> IO (Maybe XhrResponseBody)
138140xmlHttpRequestGetResponse xhr = do
139141 mr <- getResponse xhr
140− return $ fmap (\(GObject r) -> XhrResponseBody r) mr
142+ rt <- xmlHttpRequestGetResponseType xhr
143+ case rt of
144+ Just XhrResponseType_Blob -> return $ fmap (XhrResponseBody_Blob . castToBlob) mr
145+ Just XhrResponseType_Text -> fmap (Just . XhrResponseBody_Text) $ xmlHttpRequestGetStatusText xhr
146+ Just XhrResponseType_Default -> fmap (Just . XhrResponseBody_Text) $ xmlHttpRequestGetStatusText xhr
147+ Just XhrResponseType_ArrayBuffer -> case (fmap unGObject mr) of
148+ Nothing -> return Nothing
149+ Just ptr -> fmap (Just . XhrResponseBody_ArrayBuffer) $ bsFromArrayBuffer ptr ptr
150+ _ -> return Nothing
141151
src/Reflex/Dom/Xhr.hs+2−1
@@ -131,7 +131,7 @@ newXMLHttpRequestWithError req cb = do
131131 (fromMaybe "" $ _xhrRequestConfig_user c)
132132 (fromMaybe "" $ _xhrRequestConfig_password c)
133133 iforM_ (_xhrRequestConfig_headers c) $ xmlHttpRequestSetRequestHeader xhr
134− maybe (return ()) (xmlHttpRequestSetResponseType xhr . toResponseType) rt
134+ maybe (return ()) (xmlHttpRequestSetResponseType xhr . fromResponseType) rt
135135 _ <- xmlHttpRequestOnreadystatechange xhr $ do
136136 readyState <- liftIO $ xmlHttpRequestGetReadyState xhr
137137 status <- liftIO $ xmlHttpRequestGetStatus xhr
@@ -226,3 +226,4 @@ decodeText = decode . BL.fromStrict . encodeUtf8
226226-- | Convenience function to decode JSON-encoded responses.
227227decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
228228decodeXhrResponse = join . fmap decodeText . _xhrResponse_responseText
229+
src/Reflex/Dom/Xhr/ResponseType.hs+9−2
@@ -3,12 +3,19 @@
33module Reflex.Dom.Xhr.ResponseType where
44
55import Data.Typeable
6+import Data.Text (Text)
7+import GHCJS.DOM.Blob (Blob)
8+import Data.ByteString (ByteString)
69
710data XhrResponseType = XhrResponseType_Default
811 | XhrResponseType_ArrayBuffer
912 | XhrResponseType_Blob
10− | XhrResponseType_Document
11− | XhrResponseType_JSON
1213 | XhrResponseType_Text
1314 deriving (Show, Read, Eq, Ord, Typeable)
1415
16+data XhrResponseBody = XhrResponseBody_Default Text
17+ | XhrResponseBody_Text Text
18+ | XhrResponseBody_Blob Blob
19+ | XhrResponseBody_ArrayBuffer ByteString
20+ deriving (Eq)
21+