hbro-1.2.0.0: library/Hbro/WebView/Signals.hs
{-# LANGUAGE TemplateHaskell #-}
module Hbro.WebView.Signals where
-- {{{ Imports
import Hbro.Error
import Hbro.Gdk.KeyVal
import Hbro.Keys as Keys hiding(Hooks)
import Hbro.Keys.Model ((.|))
import Hbro.Keys.Signals as Keys
import Hbro.Logger
import Hbro.Prelude hiding(on)
import Graphics.UI.Gtk.WebKit.Lifted as Lifted
import Control.Lens.Lens
import Control.Lens.Getter
import Control.Lens.Setter
import Control.Lens.TH
import Data.Set as S hiding(map)
import Graphics.UI.Gtk.Abstract.Widget hiding(KeyVal)
import Graphics.UI.Gtk.Gdk.EventM as Gdk
import Graphics.UI.Gtk.WebKit.Download as W hiding(Download, downloadGetUri, downloadGetSuggestedFilename)
import Graphics.UI.Gtk.WebKit.WebNavigationAction
import Graphics.UI.Gtk.WebKit.WebPolicyDecision
import Graphics.UI.Gtk.WebKit.WebView as W hiding(LoadFinished)
import Network.URI
import System.Glib.Signals hiding(Signal)
-- }}}
data ResourceAction = Load | Download'
data Download = Download URI Text (Maybe Int)
instance Describable Download where describe _ = "Download"
data LinkHovered = LinkHovered URI (Maybe Text)
instance Describable LinkHovered where describe _ = "LinkHovered"
data LinkClicked = LinkClicked URI MouseButton
instance Describable LinkClicked where describe _ = "LinkClicked"
data LoadRequested = LoadRequested URI
instance Describable LoadRequested where describe _ = "LoadRequested"
data LoadStarted = LoadStarted
instance Describable LoadStarted where describe _ = "LoadStarted"
data LoadFinished = LoadFinished
instance Describable LoadFinished where describe _ = "LoadFinished"
data NewWindow = NewWindow URI
instance Describable NewWindow where describe _ = "NewWindow"
data ResourceOpened = ResourceOpened URI Text
instance Describable ResourceOpened where describe _ = "ResourceOpened"
data TitleChanged = TitleChanged Text
instance Describable TitleChanged where describe _ = "TitleChanged"
data Signals = Signals
{ _download :: TMVar Download
, _keyPressed :: TMVar KeyPressed
, _linkClicked :: TMVar LinkClicked
, _linkHovered :: TMVar LinkHovered
, _loadRequested :: TMVar LoadRequested
, _loadStarted :: TMVar LoadStarted
, _loadFinished :: TMVar LoadFinished
-- _newWebView :: TMVar URI,
, _newWindow :: TMVar NewWindow
, _resourceOpened :: TMVar ResourceOpened
, _titleChanged :: TMVar TitleChanged
}
makeLensesWith ?? ''Signals $ lensRules
& lensField .~ (\name -> Just (tailSafe name ++ "L"))
initSignals :: (BaseIO m) => m Signals
initSignals = io (Signals <$> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO
<*> newEmptyTMVarIO)
-- | Sequentially bind all signals.
attach :: (BaseIO m) => WebView -> Signals -> m ()
attach webView signals = sequence_
[ attachDownload webView (signals^.downloadL)
, attachLinkHovered webView (signals^.linkHoveredL)
, attachLoadStarted webView (signals^.loadStartedL)
, attachLoadFinished webView (signals^.loadFinishedL)
, attachNavigationRequest webView (signals^.linkClickedL, signals^.loadRequestedL)
, attachNewWebView webView (signals^.newWindowL)
, attachNewWindow webView (signals^.newWindowL)
, attachResourceOpened webView (signals^.resourceOpenedL)
, attachTitleChanged webView (signals^.titleChangedL)
, attachKeyPressed webView (signals^.keyPressedL)
]
attachDownload :: (BaseIO m) => WebView -> TMVar Download -> m (ConnectId WebView)
attachDownload webView signal = gSync . on webView downloadRequested $ \d -> do
logErrors $ do
amount <- io $ downloadGetTotalSize d
uri <- downloadGetUri d
name <- downloadGetSuggestedFilename d
debugM "hbro.signals" $ "Requested download <" ++ tshow uri ++ ">"
atomically . tryPutTMVar signal $ Download uri name (Just amount)
return False
attachLinkHovered :: (BaseIO m) => WebView -> TMVar LinkHovered -> m (ConnectId WebView)
attachLinkHovered webView signal = gSync . on webView hoveringOverLink $ \title uri -> void . runMaybeT $ do
debugM "hbro.signals" $ "Link hovered <" ++ tshow uri ++ ">"
u <- MaybeT . return $ parseURI . unpack =<< uri
atomically . tryPutTMVar signal $ LinkHovered u title
-- Triggered in 2 cases:
-- 1/ Javascript window.open()
-- 2/ Context menu "Open in new window"
attachNewWebView :: (BaseIO m) => WebView -> TMVar NewWindow -> m (ConnectId WebView)
attachNewWebView webView signal = gSync . on webView createWebView $ \_frame -> do
webView' <- webViewNew
on webView' webViewReady $ return True
on webView' navigationPolicyDecisionRequested $ \_ request _ decision -> do
logErrors $ do
uri <- networkRequestGetUri request
debugM "hbro.signals" $ "New window <" ++ tshow uri ++ ">"
atomically . tryPutTMVar signal $ NewWindow uri
webPolicyDecisionIgnore decision
return True
return webView'
attachLoadStarted :: (BaseIO m) => WebView -> TMVar LoadStarted -> m (ConnectId WebView)
attachLoadStarted webView signal = gSync . on webView loadStarted $ \_frame -> do
debugM "hbro.signals" "Load started"
void . atomically $ tryPutTMVar signal LoadStarted
attachLoadFinished :: (BaseIO m) => WebView -> TMVar LoadFinished -> m (ConnectId WebView)
attachLoadFinished webView signal = gSync . on webView loadFinished $ \_frame -> do
debugM "hbro.signals" "Load finished"
void . atomically $ tryPutTMVar signal LoadFinished
attachNavigationRequest :: (BaseIO m) => WebView -> (TMVar LinkClicked, TMVar LoadRequested) -> m (ConnectId WebView)
attachNavigationRequest webView (signal1, signal2) = gSync . on webView navigationPolicyDecisionRequested $ \_frame request action decision -> do
reason <- webNavigationActionGetReason action
button <- toMouseButton <$> webNavigationActionGetButton action
-- io . putStrLn . ("Request data: " ++) =<< networkRequestGetBody request
-- io . putStrLn . ("Request type: " ++) . describe =<< networkRequestGetContentType request
-- io . putStrLn . ("Request type: " ++) . describe =<< networkRequestGetURI request
logErrors $ do
uri <- networkRequestGetUri request
case (reason, button) of
(WebNavigationReasonLinkClicked, Just b) -> io $ do
debugM "hbro.signals" $ "Link clicked <" ++ tshow uri ++ ">"
atomically . tryPutTMVar signal1 $ LinkClicked uri b
webPolicyDecisionIgnore decision
(WebNavigationReasonOther, _) -> io $ do
debugM "hbro.signals" $ "Navigation request [" ++ tshow reason ++ "] to <" ++ tshow uri ++ ">"
webPolicyDecisionUse decision
(WebNavigationReasonBackForward, _) -> io $ do
debugM "hbro.signals" $ "Navigation request [" ++ tshow reason ++ "] to <" ++ tshow uri ++ ">"
webPolicyDecisionUse decision
(WebNavigationReasonReload, _) -> io $ do
debugM "hbro.signals" $ "Navigation request [" ++ tshow reason ++ "] to <" ++ tshow uri ++ ">"
webPolicyDecisionUse decision
(WebNavigationReasonFormSubmitted, _) -> io $ do
debugM "hbro.signals" $ "Form submitted to <" ++ tshow uri ++ ">"
webPolicyDecisionUse decision
_ -> io $ do
debugM "hbro.signals" $ "Navigation request [" ++ tshow reason ++ "] to <" ++ tshow uri ++ ">"
void . atomically . tryPutTMVar signal2 $ LoadRequested uri
webPolicyDecisionIgnore decision
`catchError` \e -> io $ do
errorM "hbro.signals" $ tshow e
webPolicyDecisionUse decision
return True
where
toMouseButton 1 = Just LeftButton
toMouseButton 2 = Just MiddleButton
toMouseButton 3 = Just RightButton
toMouseButton _ = Nothing
attachNewWindow :: (BaseIO m) => WebView -> TMVar NewWindow -> m (ConnectId WebView)
attachNewWindow webView signal = gSync . on webView newWindowPolicyDecisionRequested $ \_frame request _action decision -> do
logErrors $ do
uri <- networkRequestGetUri request
debugM "hbro.signals" $ "New window request <" ++ tshow uri ++ ">"
atomically . tryPutTMVar signal $ NewWindow uri
webPolicyDecisionIgnore decision
return True
attachResourceOpened :: (BaseIO m) => WebView -> TMVar ResourceOpened -> m (ConnectId WebView)
attachResourceOpened webView signal = gSync . on webView mimeTypePolicyDecisionRequested $ \_frame request mimetype decision -> do
logErrors $ do
uri <- networkRequestGetUri request
debugM "hbro.signals" $ "Opening resource [MIME type=" ++ mimetype ++ "] at <" ++ tshow uri ++ ">"
atomically . tryPutTMVar signal $ ResourceOpened uri mimetype
webPolicyDecisionUse decision
return True
attachTitleChanged :: (BaseIO m) => WebView -> TMVar TitleChanged -> m (ConnectId WebView)
attachTitleChanged webView signal = gSync . on webView W.titleChanged $ \_frame title -> do
debugM "hbro.signals" $ "Title changed to: " ++ title
void . atomically . tryPutTMVar signal $ TitleChanged title
attachKeyPressed :: (BaseIO m) => WebView -> TMVar KeyPressed -> m (ConnectId WebView)
attachKeyPressed webView signal = gSync . on webView keyPressEvent $ do
modifiers <- S.delete _Shift . S.fromList . map Keys.Modifier <$> Gdk.eventModifier
key <- KeyVal <$> Gdk.eventKeyVal
io . runMaybeT $ do
guard . not $ isModifier key || isModalKey key
let theStroke = modifiers .| key
debugM "hbro.signals" $ "Pressed: " ++ describe theStroke
atomically . tryPutTMVar signal $ KeyPressed theStroke
return False