module Reflex.Dom.Internal.Foreign where
import Control.Concurrent
import Control.Exception (bracket)
import Control.Lens hiding (set)
import Control.Monad
import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
import Data.ByteString (ByteString)
import Data.List
import Foreign.Marshal hiding (void)
import Foreign.Ptr
import GHCJS.DOM hiding (runWebGUI)
import GHCJS.DOM.Navigator
import GHCJS.DOM.Window
import Graphics.UI.Gtk hiding (Widget)
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
import Graphics.UI.Gtk.WebKit.Types hiding (Event, Widget)
import Graphics.UI.Gtk.WebKit.WebFrame
import Graphics.UI.Gtk.WebKit.WebInspector
import Graphics.UI.Gtk.WebKit.WebSettings
import Graphics.UI.Gtk.WebKit.WebView
import System.Directory
import System.Glib.FFI hiding (void)
import qualified Data.ByteString as BS
#ifndef mingw32_HOST_OS
import System.Posix.Signals
#endif
quitWebView :: WebView -> IO ()
quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv
widgetDestroy w
installQuitHandler :: WebView -> IO ()
#ifdef mingw32_HOST_OS
installQuitHandler wv = return ()
#else
installQuitHandler wv = installHandler keyboardSignal (Catch (quitWebView wv)) Nothing >> return ()
#endif
makeDefaultWebView :: String -> (WebView -> IO ()) -> IO ()
makeDefaultWebView userAgentKey main = do
_ <- initGUI
window <- windowNew
_ <- timeoutAddFull (yield >> return True) priorityHigh 10
windowSetDefaultSize window 900 600
windowSetPosition window WinPosCenter
scrollWin <- scrolledWindowNew Nothing Nothing
webView <- webViewNew
settings <- webViewGetWebSettings webView
userAgent <- settings `get` webSettingsUserAgent
settings `set` [ webSettingsUserAgent := userAgent ++ " " ++ userAgentKey
, webSettingsEnableUniversalAccessFromFileUris := True
, webSettingsEnableDeveloperExtras := True
]
webViewSetWebSettings webView settings
window `containerAdd` scrollWin
scrollWin `containerAdd` webView
_ <- on window objectDestroy . liftIO $ mainQuit
widgetShowAll window
_ <- webView `on` loadFinished $ \_ -> do
main webView
inspector <- webViewGetInspector webView
_ <- inspector `on` inspectWebView $ \_ -> do
inspectorWindow <- windowNew
windowSetDefaultSize inspectorWindow 900 600
inspectorScrollWin <- scrolledWindowNew Nothing Nothing
inspectorWebView <- webViewNew
inspectorWindow `containerAdd` inspectorScrollWin
inspectorScrollWin `containerAdd` inspectorWebView
widgetShowAll inspectorWindow
return inspectorWebView
wf <- webViewGetMainFrame webView
pwd <- getCurrentDirectory
webFrameLoadString wf "" Nothing $ "file://" ++ pwd ++ "/"
installQuitHandler webView
mainGUI
runWebGUI :: (WebView -> IO ()) -> IO ()
runWebGUI = runWebGUI' "GHCJS"
runWebGUI' :: String -> (WebView -> IO ()) -> IO ()
runWebGUI' userAgentKey main = do
mbWindow <- currentWindow
case mbWindow of
Just window -> do
Just n <- getNavigator window
agent <- getUserAgent n
unless ((" " ++ userAgentKey) `isSuffixOf` agent) $ main (castToWebView window)
Nothing -> do
makeDefaultWebView userAgentKey main
foreign import ccall "wrapper"
wrapper :: JSObjectCallAsFunctionCallback' -> IO JSObjectCallAsFunctionCallback
toJSObject :: JSContextRef -> [Ptr OpaqueJSValue] -> IO JSObjectRef
toJSObject ctx args = do
o <- jsobjectmake ctx nullPtr nullPtr
iforM_ args $ \n a -> do
prop <- jsstringcreatewithutf8cstring $ show n
jsobjectsetproperty ctx o prop a 1 nullPtr
return o
fromJSStringMaybe :: JSContextRef -> JSValueRef -> IO (Maybe String)
fromJSStringMaybe c t = do
isNull <- jsvalueisnull c t
case isNull of
True -> return Nothing
False -> do
j <- jsvaluetostringcopy c t nullPtr
l <- jsstringgetmaximumutf8cstringsize j
s <- allocaBytes (fromIntegral l) $ \ps -> do
_ <- jsstringgetutf8cstring'_ j ps (fromIntegral l)
peekCString ps
return $ Just s
getLocationHost :: WebView -> IO String
getLocationHost wv = withWebViewContext wv $ \c -> do
script <- jsstringcreatewithutf8cstring "location.host"
lh <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
lh' <- fromJSStringMaybe c lh
return $ maybe "" id lh'
getLocationProtocol :: WebView -> IO String
getLocationProtocol wv = withWebViewContext wv $ \c -> do
script <- jsstringcreatewithutf8cstring "location.protocol"
lp <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
lp' <- fromJSStringMaybe c lp
return $ maybe "" id lp'
bsToArrayBuffer :: JSContextRef -> ByteString -> IO JSValueRef
bsToArrayBuffer c bs = do
elems <- forM (BS.unpack bs) $ \x -> jsvaluemakenumber c $ fromIntegral x
let numElems = length elems
bracket (mallocArray numElems) free $ \elemsArr -> do
pokeArray elemsArr elems
a <- jsobjectmakearray c (fromIntegral numElems) elemsArr nullPtr
newUint8Array <- jsstringcreatewithutf8cstring "new Uint8Array(this)"
jsevaluatescript c newUint8Array a nullPtr 1 nullPtr
bsFromArrayBuffer :: JSContextRef -> JSValueRef -> IO ByteString
bsFromArrayBuffer c a = do
let getIntegral = fmap round . (\x -> jsvaluetonumber c x nullPtr)
getByteLength <- jsstringcreatewithutf8cstring "this.byteLength"
byteLength <- getIntegral =<< jsevaluatescript c getByteLength a nullPtr 1 nullPtr
toUint8Array <- jsstringcreatewithutf8cstring "new Uint8Array(this)"
uint8Array <- jsevaluatescript c toUint8Array a nullPtr 1 nullPtr
getIx <- jsstringcreatewithutf8cstring "this[0][this[1]]"
let arrayLookup i = do
i' <- jsvaluemakenumber c (fromIntegral i)
args <- toJSObject c [uint8Array, i']
getIntegral =<< jsevaluatescript c getIx args nullPtr 1 nullPtr
fmap BS.pack $ forM [0..byteLength1] arrayLookup
withWebViewContext :: WebView -> (JSContextRef -> IO a) -> IO a
withWebViewContext wv f = f =<< webFrameGetGlobalContext =<< webViewGetMainFrame wv