~alcinnz/amphiarao

ref: 9414d297f39e6fe6733d0307b975a994b1896f6f amphiarao/src/Webdriver.hs -rw-r--r-- 5.3 KiB
9414d297 — Adrian Cochrane Add noops for window (re)sizing, resorting to throwing errors. 3 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, DeriveGeneric #-}
module Webdriver(serveWebdriver) where

import Happstack.Lite
import Control.Concurrent.MVar
import Data.Aeson
import Data.Text (Text)
import GHC.Generics
import Data.ByteString.Lazy (ByteString)

import qualified Data.HashMap.Strict as M
import Data.UUID as ID
import Data.UUID.V4

import Control.Monad.IO.Class (liftIO)
import Data.Maybe (fromMaybe, isJust)

import qualified Network.URI as URI
import qualified Network.URI.Fetch as URI

import Capabilities (processCaps)
import JSON
import qualified Internal as WD
import qualified Internal.Load as WD

serveWebdriver :: WD.Sessions -> ServerPart Response
serveWebdriver sessions = do
  msum [
      dir "status" serveStatus,
      dir "session" $ postSession sessions,
      dir "session" $ path $ serveSession sessions,
      nullDir >> ok (toResponse ("This is a WebDriver endpoint. Please copy the URL to hand to Selenium." :: Text))
    ]
serveSession :: WD.Sessions -> String -> ServerPart Response
serveSession sessions = WD.withSession fail (\uuid session -> msum [
        delSession sessions uuid,
        dir "timeouts" $ getTimeout session,
        dir "timeouts" $ setTimeout session,
        dir "url" $ navigateTo session,
        dir "url" $ getURL session,
        dir "refresh" $ reloadPage session,
        dir "back" $ sessionAction WD.back session,
        dir "forward" $ sessionAction WD.next session,
        dir "window" $ msum [ -- Noops
            getWindowHandle uuid,
            delSession sessions uuid, -- Closing the only window closes the session.
            switchWindowHandle uuid,
            dir "handles" $ getWindowHandles uuid,
            dir "rect" $ unsupportedOp,
            dir "maximize" $ unsupportedOp,
            dir "minimize" $ unsupportedOp,
            dir "fullscreen" $ unsupportedOp
        ],
        dir "frame" $ msum [noSuchFrame, dir "parent" $ ok $ toResponse ()] -- Noops
    ]) sessions
  where
    fail uuid'| Just _ <- ID.fromString uuid' = errJSON 404 "invalid session ID" $ (
        "Session " ++ uuid' ++ " was not found in active sessions")
        | otherwise = errJSON 404 "invalid session ID" $ ("UUID " ++ uuid' ++ " is not a valid UUID")

data Status = Status {ready :: Bool, message :: Text} deriving Generic
instance ToJSON Status
serveStatus = okJSON $ Status True "Always ready to open new sessions"

data NewSession = NewSession {sessionId :: UUID, capabilities :: Object} deriving Generic
instance ToJSON NewSession
postSession sessions = do
    method POST
    nullDir
    caps' <- getJSON
    case processCaps caps' of
        Just caps -> do
            (uuid, _) <- liftIO $ WD.createSession sessions caps
            okJSON $ NewSession uuid caps
        Nothing -> errJSON 500 "session not created" "Invalid capabilities specified!"

delSession sessions uuid = do
    method DELETE
    nullDir
    liftIO $ WD.delSession uuid sessions
    ok $ toResponse ()

getTimeout session = do
    method GET
    nullDir
    session' <- liftIO $ readMVar session
    okJSON $ WD.timeouts session'

setTimeout session = do
    method POST
    nullDir
    update' <- getJSON
    session' <- liftIO $ readMVar session
    case update' of
        Just (Object update) | Object current <- toJSON $ WD.timeouts session',
                Success new <- fromJSON $ toJSON $ M.union update current -> do
            liftIO $ swapMVar session session' {WD.timeouts = new}
            ok $ toResponse ()
        _ -> errJSON 400 "invalid argument" "Failed to parse JSON input"

data NavigateTo = NavigateTo { url :: String } deriving Generic
instance FromJSON NavigateTo
navigateTo session = do
    method POST
    nullDir
    target' <- getJSON
    case target' of
        Just target | Just url' <- URI.parseAbsoluteURI target -> do
            liftIO $ WD.load session url'
            ok $ toResponse ()
        Just target -> errJSON 400 "invalid argument" (target ++ " is not an absolute URL")
        Nothing -> errJSON 400 "invalid argument" "Failed to parse JSON input"

getURL session = do
    method GET
    nullDir
    session' <- liftIO $ readMVar session
    ok $ toResponse $ show $ WD.currentURL session'

reloadPage session = do
    method POST
    nullDir
    session' <- liftIO $ readMVar session
    liftIO $ WD.load' session $ WD.currentURL session'
    ok $ toResponse ()

sessionAction cb session = do
    method POST
    nullDir
    liftIO $ cb session
    ok $ toResponse ()

---- Windowing noops
getWindowHandle uuid = do
    method GET
    nullDir
    ok $ toResponse ("window-" ++ ID.toString uuid)

data WindowHandle = WindowHandle { handle :: String } deriving Generic
instance FromJSON WindowHandle
switchWindowHandle uuid = do
    method POST
    nullDir
    handle <- getJSON
    case handle of
        Nothing -> errJSON 400 "invalid argument" "Failed to parse JSON input"
        Just (WindowHandle handle') | handle' /= "window-" ++ ID.toString uuid ->
            errJSON 404 "no such window" "Rhapsode isn't a multi-window or multi-tab browser"
        _ -> ok $ toResponse ()

getWindowHandles uuid = do
    method GET
    nullDir
    okJSON ["window-" ++ ID.toString uuid]

noSuchFrame = do
    method POST
    nullDir
    errJSON 404 "no such frame" "Rhapsode doesn't support frames"

unsupportedOp = do
    nullDir
    errJSON 400 "unsupported operation" "Windowsize is meaningless to Rhapsode"