diff --git a/.ghci b/.ghci
deleted file mode 100644
--- a/.ghci
+++ /dev/null
@@ -1,2 +0,0 @@
-:set -XOverloadedStrings -XScopedTypeVariables
-:m Test.WebDriver Data.Default.Class
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
 # Change Log
 
-##0.9.0.1
+## 0.10.0.0
+* Add Aeson 2 compatibility to support GHC 9.0 and 9.2
+* Derive MonadMask instance for the WD monad
+* Fix parameter name in the `focusWindow` function
+* Fix Cookie ToJSON instance
+* Convert a couple noReturn calls to ignoreReturn to avoid a crash
+* Switch to hpack
+* Switch to GitHub Actions CI
+* Remove cabal flags and shorten extra-source-files
+
+## 0.9.0.1
 * Fixed build errors when building against aeson-1.4.3.0
 
 ## 0.9
@@ -44,7 +54,7 @@
 ## 0.8.1
 * Previously internal convenience functions `noReturn` and `ignoreReturn` are now exported in Test.WebDriver.JSON
 * `elemInfo` is now deprecated due to it being phased out in the Marionette (Firefox) driver. It will likely be removed once Selenium 4 is released.
-* Fixed an issue causing PAC settings to not work.  
+* Fixed an issue causing PAC settings to not work.
 
 ## 0.8.0.4
 * Quick fix to parse "unsupported command" errors when using Marionette driver (Selenium + Marionette has nonstandard behavior when reporting that error type)
@@ -57,7 +67,7 @@
 * Fixed issue introduced in 0.8 that caused build failure when using aeson < 0.10
 
 ## 0.8.0.1
-* findElems and and findElemsFrom were accidentally changed to return a single Element in the last release. This has been fixed. 
+* findElems and and findElemsFrom were accidentally changed to return a single Element in the last release. This has been fixed.
 
 ## 0.8
 
@@ -109,18 +119,18 @@
   * Reworked and improved session history API
     * Added a `SessionHistory` record type to replace the old `(Request, Response ByteString)` type. The new type has the same data as the previous tuple, but additionally records the number of attempted HTTP retries, and instead of `Response ByteString` uses `Either SomeException (Response ByteString)` so that HTTP request errors can be logged.
     * Removed `wdKeepSessHist` field from `WDConfig` and replaced it with `wdHistoryConfig`, which uses a new `SessionHistoryConfig` type.
-      
+
       ```hs
         -- |A function used to append new requests/responses to session history.
         type SessionHistoryConfig = SessionHistory -> [SessionHistory] -> [SessionHistory]
       ```
     * The new field can be configured using several new constants: `noHistory`, `onlyMostRecentHistory`, and `unlimitedHistory`. Note: `unlimitedHistory` is now the default configuration for history. For the old behavior, use `onlyMostRecentHistory`.
     * New top-level functions for accessing session history
-      
+
       ```hs
         -- |Gets the command history for the current session.
         getSessionHistory :: WDSessionState wd => wd [SessionHistory]
-        
+
         -- |Prints a history of API requests to stdout after computing the given action
         --  or after an exception is thrown
         dumpSessionHistory :: WDSessionStateControl wd => wd a -> wd a
@@ -132,30 +142,30 @@
 ### Typeclass API
 * `WDSessionState` is now a superclass of `Monad` and `Applicative` instead of `MonadBaseControl IO`. This makes the class significantly more flexible in how it can be used, as it no longer requires `IO` as a base monad.
   * For convenience the following constraint aliases were added (requires `ConstraintKinds` extension to use). Several existing API functions have been updated to use these new constraints where appropriate.
-    
+
     ```hs
       type WDSessionStateIO s = (WDSessionState s, MonadBase IO s)
       type WDSessionStateControl s = (WDSessionState s, MonadBaseControl IO s)
     ```
-    
+
   * The `WDSessionStateControl` constraint is equivalent to the previous `WDSessionState` constraint.
   * The `WebDriver` class is unaffected (it is now a superclass of `WDSessionStateControl`), so code using the basic `Test.WebDriver` API will not be affected.
 
 * New typeclasses added to `Test.WebDriver.Capabilities`: `GetCapabilities` and `SetCapabilities`; for convenience a constraint alias `HasCapabilities` has been added to work with both of these classes (requires `ConstraintKinds` extension to use)
-      
+
       ```hs
         -- |A typeclass for readable 'Capabilities'
         class GetCapabilities t where
           getCaps :: t -> Capabilities
-        
+
         -- |A typeclass for writable 'Capabilities'
         class SetCapabilities t where
           setCaps :: Capabilities -> t -> t
-        
+
         -- |Read/write 'Capabilities'
         type HasCapabilities t = (GetCapabilities t, SetCapabilities t)
       ```
-      
+
 ### Minor API changes (not exposed to `Test.WebDriver` module)
 * `Test.WebDriver.Session` changes
   * new function `mostRecentHistory` added
@@ -181,7 +191,7 @@
 ## 0.6.2
 * Supports GHC 7.10
 * Supports reworked Chrome capabilities used by newer versions of WebDriver
-* Servers that return empty JSON strings for commands with no return value will no longer cause parse errors 
+* Servers that return empty JSON strings for commands with no return value will no longer cause parse errors
 
 ## 0.6.1
 * Added the ability to pass HTTP request headers at session creation
@@ -192,7 +202,7 @@
 * Support for monad-control 1.0
 
 ## 0.6.0.3
-* Relaxed upper bounds on text and http-client versions 
+* Relaxed upper bounds on text and http-client versions
 
 ## 0.6.0.2
 * Added support for aeson > 0.8 and network > 2.6
@@ -263,7 +273,7 @@
 * The LogPref type has been renamed to LogLevel to reflect its use within the new log interface
 
 ### new features
-* a new log interface as specified by the webdriver standard. This includes the functions getLogs and getLogTypes, and the types LogType and LogEntry. 
+* a new log interface as specified by the webdriver standard. This includes the functions getLogs and getLogTypes, and the types LogType and LogEntry.
 * waitWhile and waitUntil now show more detailed information about why an explicit wait timed out.
 
 ## hs-webdriver 0.5.0.1
@@ -289,7 +299,7 @@
 * NoSessionId and doSessCommand were moved from Test.WebDriver.Classes to Test.WebDriver.Commands.Internal
 
 ### bug fixes
-* fixed a typo in the export list of Firefox.Profile; deleteFile is now correctly exported instead of removeFile from System.Directory 
+* fixed a typo in the export list of Firefox.Profile; deleteFile is now correctly exported instead of removeFile from System.Directory
 * fixed an error in the JSON representation of MouseButton
 
 ### new features
@@ -307,7 +317,7 @@
 ### known issues
 * Because of the way loadProfile currently adds directories to the profileFiles HashMap, it's possible for extensions added via addExtension to be overriden by the extensions originally listed in the on-disk extensions directory.
 
-### new features 
+### new features
 * It's now possible to add entire directories to a profile in pure code using addFile and addExtension.
 * new functions in Common.Profile: unionProfiles, onProfileFiles, onProfilePrefs
 * new function in Commands.Wait: onTimeout
@@ -330,7 +340,7 @@
 ### new features
 * new Common.Profile functions: hasExtension, hasFile
 
-## hs-webdriver 0.3.1 
+## hs-webdriver 0.3.1
 
 ### API changes
 * The representation of Profiles has changed, allowing it to store arbitrary files as well as extensions. The functional API for working with preferences and extensions ismostly unchanged, except for the behavior of calling addExtension consecutively with the same filepath argument.
@@ -344,7 +354,7 @@
 * An issue involving lazy bytestring IO in the zip-archive package means that unusually large profiles might exceed the OSes open file limit.
 
 ### new features
-* several new functions for working with Firefox/Opera profiles have been added. This includes functions for loading large profiles from disk, functions for working with zipped profiles, and functions for adding arbitrary files to a profile in pure code. 
+* several new functions for working with Firefox/Opera profiles have been added. This includes functions for loading large profiles from disk, functions for working with zipped profiles, and functions for adding arbitrary files to a profile in pure code.
 * new helper functions were added to Test.WebDriver.Commands.Wait, exported from the cond package.
 
 ## hs-webdriver 0.3.0.1
@@ -354,7 +364,7 @@
 * major bux fixes in the Firefox profile code. Note that loadProfile is unlikely
 to work as expected, but prepareTempProfile should.
 
-## hs-webdriver 0.3 
+## hs-webdriver 0.3
 
 ### API changes
 * 2 typeclasses were introduced. All WebDriver commands are now overloaded on WebDriver class, so that monad transformers with a WD base can be used conveniently.
@@ -384,7 +394,7 @@
 ## hs-webdriver 0.1
 
 ### API changes
-* getWindowSize, setWindowSize, getWindowPos, and setWindowPos have all been deprived of their WindowHandle argument. This is due to the fact that using unfocused windows with those commands causes undefined behavior. 
+* getWindowSize, setWindowSize, getWindowPos, and setWindowPos have all been deprived of their WindowHandle argument. This is due to the fact that using unfocused windows with those commands causes undefined behavior.
 
 ### new features
 * the mkCookie function for convenient cookie construction
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
 Copyright (c) 2012-2015, Adam Curtis
+Copyright (c) 2022, Tom McLaughlin
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
 [![Build Status](https://travis-ci.org/kallisti-dev/hs-webdriver.svg?branch=master)](https://travis-ci.org/kallisti-dev/hs-webdriver)
 
+# Welcome to `webdriver`
+[![Hackage](https://img.shields.io/hackage/v/webdriver.svg)](https://hackage.haskell.org/package/webdriver) ![webdriver](https://github.com/codedownio/hs-webdriver/workflows/ci/badge.svg)
+
 hs-webdriver is a Selenium WebDriver client for the Haskell programming language. You can use it to automate browser sessions for testing, system administration, etc.
 
 For more information about Selenium itself, see http://seleniumhq.org/
diff --git a/TODO.md b/TODO.md
deleted file mode 100644
--- a/TODO.md
+++ /dev/null
@@ -1,18 +0,0 @@
-#known issues
-- fix loadProfile so that it doesn't cause an overlap with user addExtension calls
-
-#features
-- support new w3c webdriver spec: http://www.w3.org/TR/webdriver/
-  - try to support backwards compatibility with old wire protocol, otherwise provide legacy API in submodule (?) 
-  - mock-up example of actions API: http://lpaste.net/3484676197546196992
-- allow WDConfig to automatically load drivers. add modules with driver loading functions
-- overload URL inputs/outputs to implicitly support structured URL types
-- add support for Opera profiles
-- POST "/session/{sessionId}/phantom/execute"
-
-#documentation
-- document errors.
-
-
-#considerations
-- consider adding withSession to SessionState so that it can be overloaded easily, or rewriting it so that it's overloaded but not a method itself.
diff --git a/src/Test/WebDriver.hs b/src/Test/WebDriver.hs
--- a/src/Test/WebDriver.hs
+++ b/src/Test/WebDriver.hs
@@ -2,40 +2,41 @@
 This module serves as the top-level interface to the Haskell WebDriver bindings,
 providing most of the functionality you're likely to want.
 -}
-module Test.WebDriver
-  ( -- * WebDriver monad
-    WD(..) 
-    -- * Running WebDriver commands
+
+module Test.WebDriver (
+  -- * WebDriver monad
+  WD(..)
+  -- * Running WebDriver commands
   , runSession, withSession, runWD
-    -- * WebDriver configuration
+  -- * WebDriver configuration
   , WDConfig(..), defaultConfig
-    -- ** Configuration helper functions
-    -- | Instead of working with the 'Capabilities' record directly, you can use 
-    --   these config modifier functions to specify common options.
-  , useBrowser, useProxy, useVersion, usePlatform 
-    -- ** Session history configuration
+  -- ** Configuration helper functions
+  -- | Instead of working with the 'Capabilities' record directly, you can use
+  --   these config modifier functions to specify common options.
+  , useBrowser, useProxy, useVersion, usePlatform
+  -- ** Session history configuration
   , SessionHistoryConfig, noHistory, unlimitedHistory, onlyMostRecentHistory
-    -- ** HTTP request header utilities
+  -- ** HTTP request header utilities
   , withRequestHeaders, withAuthHeaders
-    -- * WebDriver commands
+  -- * WebDriver commands
   , module Test.WebDriver.Commands
-    -- * Capabilities (advanced configuration)
+  -- * Capabilities (advanced configuration)
   , Capabilities(..), defaultCaps, allCaps, modifyCaps
   , Platform(..), ProxyType(..)
-    -- ** Browser-specific capabilities
+  -- ** Browser-specific capabilities
   , Browser(..), LogLevel(..)
-    -- *** Browser defaults
+  -- *** Browser defaults
   , firefox, chrome, ie, opera, iPhone, iPad, android
-   -- * Exception handling
+  -- * Exception handling
   , finallyClose, closeOnException
   , module Test.WebDriver.Exceptions
-    -- * Accessing session history
+  -- * Accessing session history
   , SessionHistory(..), getSessionHistory, dumpSessionHistory
   ) where
 
-import Test.WebDriver.Types
 import Test.WebDriver.Commands
-import Test.WebDriver.Monad
-import Test.WebDriver.Exceptions
 import Test.WebDriver.Config
+import Test.WebDriver.Exceptions
+import Test.WebDriver.Monad
 import Test.WebDriver.Session
+import Test.WebDriver.Types
diff --git a/src/Test/WebDriver/Capabilities.hs b/src/Test/WebDriver/Capabilities.hs
--- a/src/Test/WebDriver/Capabilities.hs
+++ b/src/Test/WebDriver/Capabilities.hs
@@ -1,14 +1,12 @@
-{-# LANGUAGE OverloadedStrings, RecordWildCards, ConstraintKinds
-  #-}
+{-# LANGUAGE OverloadedStrings, RecordWildCards, ConstraintKinds, CPP #-}
 module Test.WebDriver.Capabilities where
 
-import Test.WebDriver.Firefox.Profile
 import Test.WebDriver.Chrome.Extension
+import Test.WebDriver.Firefox.Profile
 import Test.WebDriver.JSON
 
 import Data.Aeson
 import Data.Aeson.Types (Parser, typeMismatch, Pair)
-import qualified Data.HashMap.Strict as HM (delete, toList, empty)
 
 import Data.Text (Text, toLower, toUpper)
 import Data.Default.Class (Default(..))
@@ -21,6 +19,13 @@
 
 import Prelude -- hides some "unused import" warnings
 
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.KeyMap          as HM (delete, toList, empty)
+#else
+import qualified Data.HashMap.Strict        as HM (delete, toList, empty)
+#endif
+
+
 -- |A typeclass for readable 'Capabilities'
 class GetCapabilities t where
   getCaps :: t -> Capabilities
@@ -47,7 +52,7 @@
 useVersion :: HasCapabilities t => String -> t -> t
 useVersion v = modifyCaps $ \c -> c { version = Just v }
 
--- |A helper function for setting the 'platform' capability of a 'HasCapabilities' instance 
+-- |A helper function for setting the 'platform' capability of a 'HasCapabilities' instance
 usePlatform :: HasCapabilities t => Platform -> t -> t
 usePlatform p = modifyCaps $ \c -> c { platform = p }
 
@@ -242,7 +247,7 @@
 
         Phantomjs {..}
           -> catMaybes [ opt "phantomjs.binary.path" phantomjsBinary
-                       ] ++ 
+                       ] ++
                        [ "phantomjs.cli.args" .= phantomjsOptions
                        ]
 
@@ -276,7 +281,7 @@
 
     where --some helpful JSON accessor shorthands
           req :: FromJSON a => Text -> Parser a
-          req = (o .:)            -- required field
+          req = (o .:) . fromText  -- required field
           opt :: FromJSON a => Text -> a -> Parser a
           opt k d = o .:?? k .!= d -- optional field
           b :: Text -> Parser (Maybe Bool)
@@ -644,7 +649,7 @@
       _ -> fail $ "Invalid ProxyType " ++ show pTyp
     where
       f :: FromJSON a => Text -> Parser a
-      f = (obj .:)
+      f = (obj .:) . fromText
   parseJSON v = typeMismatch "ProxyType" v
 
 instance ToJSON ProxyType where
@@ -768,4 +773,3 @@
       0 -> return AlignTop
       1 -> return AlignBottom
       _ -> fail $ "Invalid integer for IEElementScrollBehavior: " ++ show n
-
diff --git a/src/Test/WebDriver/Class.hs b/src/Test/WebDriver/Class.hs
--- a/src/Test/WebDriver/Class.hs
+++ b/src/Test/WebDriver/Class.hs
@@ -1,22 +1,16 @@
-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, FlexibleContexts, CPP,
-             GeneralizedNewtypeDeriving, RecordWildCards, ConstraintKinds #-}
-#ifndef CABAL_BUILD_DEVELOPER
-{-# OPTIONS_GHC  -fno-warn-warnings-deprecations #-}
-#endif
-module Test.WebDriver.Class
-       ( -- * WebDriver class
-         WebDriver(..), Method, methodDelete, methodGet, methodPost,
-       ) where
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ConstraintKinds #-}
+
+module Test.WebDriver.Class (
+  -- * WebDriver class
+  WebDriver(..), Method, methodDelete, methodGet, methodPost,
+  ) where
 import Test.WebDriver.Session
 
 import Data.Aeson
 import Data.Text (Text)
-#if !MIN_VERSION_base(4,8,0)
-import Data.Monoid (Monoid) -- for some reason "import Prelude" trick doesn't work with "import Data.Monoid"
-#endif
-
 import Network.HTTP.Types.Method (methodDelete, methodGet, methodPost, Method)
-
 import Control.Monad.Trans.Class
 import Control.Monad.Trans.Error
 import Control.Monad.Trans.Except
@@ -32,6 +26,11 @@
 import Control.Monad.Trans.Writer.Strict as SW
 import Data.CallStack
 
+#if !MIN_VERSION_base(4,8,0)
+import Data.Monoid (Monoid) -- for some reason "import Prelude" trick doesn't work with "import Data.Monoid"
+#endif
+
+
 -- |A class for monads that can handle wire protocol requests. This is the
 -- operation underlying all of the high-level commands exported in
 -- "Test.WebDriver.Commands". For more information on the wire protocol see
@@ -52,7 +51,6 @@
 instance WebDriver wd => WebDriver (LS.StateT s wd) where
   doCommand rm t a = lift (doCommand rm t a)
 
-
 instance WebDriver wd => WebDriver (MaybeT wd) where
   doCommand rm t a = lift (doCommand rm t a)
 
@@ -76,7 +74,6 @@
 
 instance WebDriver wd => WebDriver (ExceptT e wd) where
   doCommand rm t a = lift (doCommand rm t a)
-
 
 instance (Monoid w, WebDriver wd) => WebDriver (SRWS.RWST r w s wd) where
   doCommand rm t a = lift (doCommand rm t a)
diff --git a/src/Test/WebDriver/Commands.hs b/src/Test/WebDriver/Commands.hs
--- a/src/Test/WebDriver/Commands.hs
+++ b/src/Test/WebDriver/Commands.hs
@@ -81,12 +81,10 @@
 import Control.Monad
 import Control.Monad.Base
 import Data.Aeson
-import Data.Aeson.TH
 import Data.Aeson.Types
 import Data.ByteString.Base64.Lazy as B64
 import Data.ByteString.Lazy as LBS (ByteString, writeFile)
 import Data.CallStack
-import qualified Data.Char as C
 import qualified Data.Foldable as F
 import Data.Maybe
 import Data.String (fromString)
@@ -98,6 +96,7 @@
 import Test.WebDriver.Capabilities
 import Test.WebDriver.Class
 import Test.WebDriver.Commands.Internal
+import Test.WebDriver.Cookies
 import Test.WebDriver.Exceptions.Internal
 import Test.WebDriver.JSON
 import Test.WebDriver.Session
@@ -125,7 +124,7 @@
 
 -- |Close the current session and the browser associated with it.
 closeSession :: (HasCallStack, WebDriver wd) => wd ()
-closeSession = do s@WDSession {..} <- getSession
+closeSession = do s@WDSession {} <- getSession
                   noReturn $ doSessCommand methodDelete "" Null
                   putSession s { wdSessId = Nothing }
 
@@ -306,7 +305,7 @@
 windows = doSessCommand methodGet "/window_handles" Null
 
 focusWindow :: (HasCallStack, WebDriver wd) => WindowHandle -> wd ()
-focusWindow w = noReturn $ doSessCommand methodPost "/window" . single "name" $ w
+focusWindow w = noReturn $ doSessCommand methodPost "/window" . single "handle" $ w
 
 -- |Closes the given window
 closeWindow :: (HasCallStack, WebDriver wd) => WindowHandle -> wd ()
@@ -327,7 +326,7 @@
 
 -- |Set the dimensions of the current window.
 setWindowSize :: (HasCallStack, WebDriver wd) => (Word, Word) -> wd ()
-setWindowSize = noReturn . doWinCommand methodPost currentWindow "/size"
+setWindowSize = ignoreReturn . doWinCommand methodPost currentWindow "/size"
                 . pair ("width", "height")
 
 -- |Get the coordinates of the current window.
@@ -337,47 +336,7 @@
 
 -- |Set the coordinates of the current window.
 setWindowPos :: (HasCallStack, WebDriver wd) => (Int, Int) -> wd ()
-setWindowPos = noReturn . doWinCommand methodPost currentWindow "/position" . pair ("x","y")
-
--- |Cookies are delicious delicacies. When sending cookies to the server, a value
--- of Nothing indicates that the server should use a default value. When receiving
--- cookies from the server, a value of Nothing indicates that the server is unable
--- to specify the value.
-data Cookie = Cookie { cookName   :: Text
-                     , cookValue  :: Text          -- ^
-                     , cookPath   :: Maybe Text    -- ^path of this cookie.
-                                                   -- if Nothing, defaults to /
-                     , cookDomain :: Maybe Text    -- ^domain of this cookie.
-                                                   -- if Nothing, the current pages
-                                                   -- domain is used
-                     , cookSecure :: Maybe Bool    -- ^Is this cookie secure?
-                     , cookExpiry :: Maybe Double  -- ^Expiry date expressed as
-                                                   -- seconds since the Unix epoch
-                                                   -- Nothing indicates that the
-                                                   -- cookie never expires
-                     } deriving (Eq, Show)
-
--- |Creates a Cookie with only a name and value specified. All other
--- fields are set to Nothing, which tells the server to use default values.
-mkCookie :: Text -> Text -> Cookie
-mkCookie name value = Cookie { cookName = name, cookValue = value,
-                               cookPath = Nothing, cookDomain = Nothing,
-                               cookSecure = Nothing, cookExpiry = Nothing
-                             }
-
-instance FromJSON Cookie where
-  parseJSON (Object o) = Cookie <$> req "name"
-                                <*> req "value"
-                                <*> opt "path" Nothing
-                                <*> opt "domain" Nothing
-                                <*> opt "secure" Nothing
-                                <*> opt "expiry" Nothing
-    where
-      req :: FromJSON a => Text -> Parser a
-      req = (o .:)
-      opt :: FromJSON a => Text -> a -> Parser a
-      opt k d = o .:?? k .!= d
-  parseJSON v = typeMismatch "Cookie" v
+setWindowPos = ignoreReturn . doWinCommand methodPost currentWindow "/position" . pair ("x","y")
 
 -- |Retrieve all cookies visible to the current page.
 cookies :: (HasCallStack, WebDriver wd) => wd [Cookie]
@@ -816,7 +775,3 @@
 
 getApplicationCacheStatus :: (WebDriver wd) => wd ApplicationCacheStatus
 getApplicationCacheStatus = doSessCommand methodGet "/application_cache/status" Null
-
--- Moving this closer to the definition of Cookie seems to cause strange compile
--- errors, so I'm leaving it here for now.
-$( deriveToJSON (defaultOptions{fieldLabelModifier = map C.toLower . drop 4}) ''Cookie )
diff --git a/src/Test/WebDriver/Commands/Internal.hs b/src/Test/WebDriver/Commands/Internal.hs
--- a/src/Test/WebDriver/Commands/Internal.hs
+++ b/src/Test/WebDriver/Commands/Internal.hs
@@ -1,6 +1,10 @@
-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, GeneralizedNewtypeDeriving, ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
 {-# OPTIONS_HADDOCK not-home #-}
--- |Internal functions used to implement the functions exported by
+-- | Internal functions used to implement the functions exported by
 -- "Test.WebDriver.Commands". These may be useful for implementing non-standard
 -- webdriver commands.
 module Test.WebDriver.Commands.Internal
diff --git a/src/Test/WebDriver/Cookies.hs b/src/Test/WebDriver/Cookies.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/WebDriver/Cookies.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Test.WebDriver.Cookies where
+
+
+import Data.Aeson
+import Data.Aeson.TH
+import Data.Aeson.Types
+import qualified Data.Char as C
+import Data.Text (Text)
+import Test.WebDriver.JSON
+
+-- | Cookies are delicious delicacies. When sending cookies to the server, a value
+-- of Nothing indicates that the server should use a default value. When receiving
+-- cookies from the server, a value of Nothing indicates that the server is unable
+-- to specify the value.
+data Cookie = Cookie { cookName   :: Text
+                     , cookValue  :: Text          -- ^
+                     , cookPath   :: Maybe Text    -- ^path of this cookie.
+                                                   -- if Nothing, defaults to /
+                     , cookDomain :: Maybe Text    -- ^domain of this cookie.
+                                                   -- if Nothing, the current pages
+                                                   -- domain is used
+                     , cookSecure :: Maybe Bool    -- ^Is this cookie secure?
+                     , cookExpiry :: Maybe Double  -- ^Expiry date expressed as
+                                                   -- seconds since the Unix epoch
+                                                   -- Nothing indicates that the
+                                                   -- cookie never expires
+                     } deriving (Eq, Show)
+
+-- |Creates a Cookie with only a name and value specified. All other
+-- fields are set to Nothing, which tells the server to use default values.
+mkCookie :: Text -> Text -> Cookie
+mkCookie name value = Cookie { cookName = name, cookValue = value,
+                               cookPath = Nothing, cookDomain = Nothing,
+                               cookSecure = Nothing, cookExpiry = Nothing
+                             }
+
+instance FromJSON Cookie where
+  parseJSON (Object o) = Cookie <$> req "name"
+                                <*> req "value"
+                                <*> opt "path" Nothing
+                                <*> opt "domain" Nothing
+                                <*> opt "secure" Nothing
+                                <*> opt "expiry" Nothing
+    where
+      req :: FromJSON a => Text -> Parser a
+      req = (o .:) . fromText
+      opt :: FromJSON a => Text -> a -> Parser a
+      opt k d = o .:?? k .!= d
+  parseJSON v = typeMismatch "Cookie" v
+
+$( deriveToJSON (defaultOptions{omitNothingFields = True, fieldLabelModifier = map C.toLower . drop 4}) ''Cookie )
diff --git a/src/Test/WebDriver/Exceptions/Internal.hs b/src/Test/WebDriver/Exceptions/Internal.hs
--- a/src/Test/WebDriver/Exceptions/Internal.hs
+++ b/src/Test/WebDriver/Exceptions/Internal.hs
@@ -175,7 +175,7 @@
                       <*> opt "class"      Nothing
                       <*> (catMaybes <$> opt "stackTrace" [])
     where req :: FromJSON a => Text -> Parser a
-          req = (o .:)            --required key
+          req = (o .:) . fromText  --required key
           opt :: FromJSON a => Text -> a -> Parser a
           opt k d = o .:?? k .!= d --optional key
   parseJSON v = typeMismatch "FailedCommandInfo" v
@@ -186,7 +186,7 @@
                                     <*> reqStr "methodName"
                                     <*> req    "lineNumber"
     where req :: FromJSON a => Text -> Parser a
-          req = (o .:) -- all keys are required
+          req = (o .:) . fromText -- all keys are required
           reqStr :: Text -> Parser String
           reqStr k = req k >>= maybe (return "") return
   parseJSON v = typeMismatch "StackFrame" v
diff --git a/src/Test/WebDriver/IO.hs b/src/Test/WebDriver/IO.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/WebDriver/IO.hs
@@ -0,0 +1,16 @@
+module Test.WebDriver.IO where
+
+import Control.Concurrent (threadDelay)
+import Control.Monad.IO.Class (liftIO, MonadIO)
+import Test.WebDriver
+import Test.WebDriver.Class (WebDriver)
+
+
+sleepIO :: Double -> IO ()
+sleepIO seconds = Control.Concurrent.threadDelay (round (seconds * 1e6))
+
+sleepWD :: (MonadIO wd, WebDriver wd) => Double -> wd ()
+sleepWD = liftIO . sleepIO
+
+printWD :: Show s => s -> WD ()
+printWD = liftIO . print
diff --git a/src/Test/WebDriver/JSON.hs b/src/Test/WebDriver/JSON.hs
--- a/src/Test/WebDriver/JSON.hs
+++ b/src/Test/WebDriver/JSON.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE OverloadedStrings, FlexibleContexts, DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings, FlexibleContexts, DeriveDataTypeable, CPP #-}
 -- |A collection of convenience functions for using and parsing JSON values
 -- within 'WD'. All monadic parse errors are converted to asynchronous
 -- 'BadJSON' exceptions.
@@ -28,6 +28,8 @@
        , BadJSON(..)
          -- * parsing commands with no return value
        , NoReturn(..), noReturn, ignoreReturn
+
+       , fromText
        ) where
 import Test.WebDriver.Class (WebDriver)
 
@@ -37,7 +39,6 @@
 import Data.ByteString.Lazy.Char8 (ByteString)
 import Data.Attoparsec.ByteString.Lazy (Result(..))
 import qualified Data.Attoparsec.ByteString.Lazy as AP
-import qualified Data.HashMap.Strict as HM
 
 import Control.Monad (join, void)
 import Control.Applicative
@@ -48,6 +49,18 @@
 
 import Prelude -- hides some "unused import" warnings
 
+#if MIN_VERSION_aeson(2,0,0)
+import qualified Data.Aeson.Key             as A
+import qualified Data.Aeson.KeyMap          as HM
+fromText :: Text -> A.Key
+fromText = A.fromText
+#else
+import qualified Data.HashMap.Strict        as HM
+fromText :: Text -> Text
+fromText = id
+#endif
+
+
 instance Exception BadJSON
 -- |An error occured when parsing a JSON value.
 newtype BadJSON = BadJSON String
@@ -76,18 +89,18 @@
 
 -- |Construct a singleton JSON 'object' from a key and value.
 single :: ToJSON a => Text -> a -> Value
-single a x = object [a .= x]
+single a x = object [(fromText a) .= x]
 
 -- |Construct a 2-element JSON 'object' from a pair of keys and a pair of
 -- values.
 pair :: (ToJSON a, ToJSON b) => (Text,Text) -> (a,b) -> Value
-pair (a,b) (x,y) = object [a .= x, b .= y]
+pair (a,b) (x,y) = object [fromText a .= x, fromText b .= y]
 
 -- |Construct a 3-element JSON 'object' from a triple of keys and a triple of
 -- values.
 triple :: (ToJSON a, ToJSON b, ToJSON c) =>
           (Text,Text,Text) -> (a,b,c) -> Value
-triple (a,b,c) (x,y,z) = object [a .= x, b.= y, c .= z]
+triple (a,b,c) (x,y,z) = object [fromText a .= x, fromText b.= y, fromText c .= z]
 
 
 -- |Parse a lazy 'ByteString' as a top-level JSON 'Value', then convert it to an
@@ -101,12 +114,12 @@
 
 -- |This operator is a wrapper over Aeson's '.:' operator.
 (!:) :: (MonadBaseControl IO wd, FromJSON a) => Object -> Text -> wd a
-o !: k = aesonResultToWD $ parse (.: k) o
+o !: k = aesonResultToWD $ parse (.: fromText k) o
 
 -- |Due to a breaking change in the '.:?' operator of aeson 0.10 (see <https://github.com/bos/aeson/issues/287>) that was subsequently reverted, this operator
 -- was added to provide consistent behavior compatible with all aeson versions. If the field is either missing or `Null`, this operator should return a `Nothing` result.
 (.:??) :: FromJSON a => Object -> Text -> Parser (Maybe a)
-o .:?? k = fmap join (o .:? k)
+o .:?? k = fmap join (o .:? fromText k)
 
 
 -- |Parse a JSON 'Object' as a pair. The first two string arguments specify the
diff --git a/src/Test/WebDriver/Monad.hs b/src/Test/WebDriver/Monad.hs
--- a/src/Test/WebDriver/Monad.hs
+++ b/src/Test/WebDriver/Monad.hs
@@ -5,19 +5,19 @@
        ) where
 
 import Test.WebDriver.Class
-import Test.WebDriver.Session
-import Test.WebDriver.Config
 import Test.WebDriver.Commands
+import Test.WebDriver.Config
 import Test.WebDriver.Internal
+import Test.WebDriver.Session
 
 import Control.Monad.Base (MonadBase, liftBase)
-import Control.Monad.IO.Class
 import Control.Monad.Fix
+import Control.Monad.IO.Class
 import Control.Monad.Trans.Control (MonadBaseControl(..), StM)
 import Control.Monad.Trans.State.Strict (StateT, evalStateT, get, put)
 --import Control.Monad.IO.Class (MonadIO)
 import Control.Exception.Lifted
-import Control.Monad.Catch (MonadThrow, MonadCatch)
+import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
 import Control.Applicative
 
 import Prelude -- hides some "unused import" warnings
@@ -26,7 +26,7 @@
 {- |A state monad for WebDriver commands.
 -}
 newtype WD a = WD (StateT WDSession IO a)
-  deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch, MonadFix)
+  deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch, MonadFix, MonadMask)
 
 instance MonadBase IO WD where
   liftBase = WD . liftBase
@@ -92,7 +92,7 @@
 
 -- |Gets the command history for the current session.
 getSessionHistory :: WDSessionState wd => wd [SessionHistory]
-getSessionHistory = fmap wdSessHist getSession 
+getSessionHistory = fmap wdSessHist getSession
 
 -- |Prints a history of API requests to stdout after computing the given action.
 dumpSessionHistory :: WDSessionStateControl wd => wd a -> wd a
diff --git a/src/Test/WebDriver/Session.hs b/src/Test/WebDriver/Session.hs
--- a/src/Test/WebDriver/Session.hs
+++ b/src/Test/WebDriver/Session.hs
@@ -1,9 +1,8 @@
-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable, FlexibleContexts, ScopedTypeVariables,
-             GeneralizedNewtypeDeriving, RecordWildCards, ConstraintKinds, CPP #-}
-
-#ifndef CABAL_BUILD_DEVELOPER
-{-# OPTIONS_GHC  -fno-warn-warnings-deprecations #-}
-#endif
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE CPP #-}
 
 module Test.WebDriver.Session
   ( -- * WDSessionState class
@@ -19,7 +18,7 @@
 import Test.WebDriver.Session.History
 
 import Data.Aeson
-import Data.ByteString as BS(ByteString) 
+import Data.ByteString as BS(ByteString)
 import Data.Text (Text)
 import Data.Maybe (listToMaybe)
 import Data.Monoid
@@ -81,7 +80,7 @@
                            , wdSessHTTPManager :: Manager
                              -- |Number of times to retry a HTTP request if it times out
                            , wdSessHTTPRetryCount :: Int
-                             -- |Custom request headers to add to every HTTP request. 
+                             -- |Custom request headers to add to every HTTP request.
                            , wdSessRequestHeaders :: RequestHeaders
                              -- |Custom request headers to add *only* to session creation requests. This is usually done
                              --  when a WebDriver server requires HTTP auth.
@@ -108,10 +107,10 @@
 -- MonadBaseControl superclass is used for exception handling through
 -- the lifted-base package.
 class (Monad m, Applicative m) => WDSessionState m where
-  
+
   -- |Retrieves the current session state of the monad
   getSession :: m WDSession
-  
+
   -- |Sets a new session state for the monad
   putSession :: WDSession -> m ()
 
@@ -120,7 +119,7 @@
 
 -- |Constraint synonym for another common pairing of 'WDSessionState' and 'MonadBaseControl' 'IO'. This
 -- is commonly used in library types to indicate use of lifted exception handling.
-type WDSessionStateControl s = (WDSessionState s, MonadBaseControl IO s) 
+type WDSessionStateControl s = (WDSessionState s, MonadBaseControl IO s)
 
 modifySession :: WDSessionState s => (WDSession -> WDSession) -> s ()
 modifySession f = getSession >>= putSession . f
@@ -139,7 +138,7 @@
 -- |The most recent SessionHistory entry recorded by this session, if any.
 mostRecentHistory :: WDSession -> Maybe SessionHistory
 mostRecentHistory = listToMaybe . wdSessHist
-    
+
 -- |The most recent HTTP request issued by this session, if any.
 mostRecentHTTPRequest :: WDSession -> Maybe Request
 mostRecentHTTPRequest = fmap histRequest . mostRecentHistory
@@ -161,15 +160,15 @@
 withAuthHeaders wd = do
   authHeaders <- fmap wdSessAuthHeaders getSession
   withRequestHeaders authHeaders wd
-                            
+
 instance WDSessionState m => WDSessionState (LS.StateT s m) where
   getSession = lift getSession
   putSession = lift . putSession
-  
+
 instance WDSessionState m => WDSessionState (SS.StateT s m) where
   getSession = lift getSession
   putSession = lift . putSession
-    
+
 instance WDSessionState m => WDSessionState (MaybeT m) where
   getSession = lift getSession
   putSession = lift . putSession
@@ -181,7 +180,7 @@
 instance WDSessionState m => WDSessionState (ListT m) where
   getSession = lift getSession
   putSession = lift . putSession
-  
+
 instance (Monoid w, WDSessionState m) => WDSessionState (LW.WriterT w m) where
   getSession = lift getSession
   putSession = lift . putSession
@@ -189,11 +188,11 @@
 instance (Monoid w, WDSessionState m) => WDSessionState (SW.WriterT w m) where
   getSession = lift getSession
   putSession = lift . putSession
-  
+
 instance WDSessionState m => WDSessionState (ReaderT r m) where
   getSession = lift getSession
   putSession = lift . putSession
-  
+
 instance (Error e, WDSessionState m) => WDSessionState (ErrorT e m) where
   getSession = lift getSession
   putSession = lift . putSession
@@ -205,7 +204,7 @@
 instance (Monoid w, WDSessionState m) => WDSessionState (SRWS.RWST r w s m) where
   getSession = lift getSession
   putSession = lift . putSession
-  
+
 instance (Monoid w, WDSessionState wd) => WDSessionState (LRWS.RWST r w s wd) where
   getSession = lift getSession
   putSession = lift . putSession
diff --git a/webdriver.cabal b/webdriver.cabal
--- a/webdriver.cabal
+++ b/webdriver.cabal
@@ -1,95 +1,97 @@
-Name: webdriver
-Version: 0.9.0.1
-Cabal-Version: >= 1.10
-License: BSD3
-License-File: LICENSE
-Author: Adam Curtis
-Maintainer: kallisti.dev@gmail.com
-Homepage: https://github.com/kallisti-dev/hs-webdriver
-Bug-Reports: https://github.com/kallisti-dev/hs-webdriver/issues
-Category: Web, Browser, Testing, WebDriver, Selenium
-Synopsis: a Haskell client for the Selenium WebDriver protocol
-Build-Type: Simple
-Extra-Source-Files: README.md, TODO.md, CHANGELOG.md, .ghci
-Tested-With: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1
-Description:
-        A Selenium WebDriver client for Haskell.
-        You can use it to automate browser sessions
-        for testing, system administration, etc.
-        .
-        For more information about Selenium itself, see
-        <http://seleniumhq.org/>
-        .
-        To find out what's been changed in this version and others,
-        see the change log at
-        <https://github.com/kallisti-dev/hs-webdriver/blob/master/CHANGELOG.md>
-
-Source-Repository head
-  type: git
-  location: git://github.com/kallisti-dev/hs-webdriver.git
-  
-Flag network-uri
-  description: Get Network.URI from the network-uri package
-  default: True
+cabal-version: 1.12
 
-Flag developer
-  description: Package development mode
-  default: False
-  manual: True
+-- This file has been generated from package.yaml by hpack version 0.35.0.
+--
+-- see: https://github.com/sol/hpack
 
-Library
-  hs-source-dirs: src
-  default-language: Haskell2010
-  ghc-options: -Wall
-  if flag(developer)
-    cpp-options: -DCABAL_BUILD_DEVELOPER
-  build-depends:   base == 4.*
-                 , aeson >= 0.6.2.0
-                 , http-client >= 0.3
-                 , http-types >= 0.8
-                 , text >= 0.11.3
-                 , bytestring >= 0.9
-                 , attoparsec >= 0.10
-                 , base64-bytestring >= 1.0
-                 , transformers >= 0.4
-                 , monad-control >= 0.3
-                 , transformers-base >= 0.1
-                 , lifted-base >= 0.1
-                 , zip-archive >= 0.1.1.8
-                 , directory > 1.0
-                 , filepath > 1.0
-                 , directory-tree >= 0.11
-                 , temporary >= 1.0
-                 , time > 1.0
-                 , unordered-containers >= 0.1.3
-                 , vector >= 0.3
-                 , exceptions >= 0.4
-                 , scientific >= 0.2
-                 , data-default-class
-                 , call-stack
+name:           webdriver
+version:        0.10.0.0
+synopsis:       a Haskell client for the Selenium WebDriver protocol
+description:    A Selenium WebDriver client for Haskell.
+                You can use it to automate browser sessions
+                for testing, system administration, etc.
+                .
+                For more information about Selenium itself, see
+                <http://seleniumhq.org/>
+                .
+                To find out what's been changed in this version and others,
+                see the change log at
+                <https://github.com/kallisti-dev/hs-webdriver/blob/master/CHANGELOG.md>
+category:       Web, Browser, Testing, WebDriver, Selenium
+homepage:       https://github.com/codedownio/hs-webdriver#readme
+bug-reports:    https://github.com/codedownio/hs-webdriver/issues
+author:         Adam Curtis <kallisti.dev@gmail.com>
+maintainer:     Tom McLaughlin <tom@codedown.io>
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+tested-with:
+    GHC == 8.6.5
+  , GHC == 8.8.4
+  , GHC == 8.10.7
+  , GHC == 9.0.2
+  , GHC == 9.2.5
+extra-source-files:
+    README.md
+    CHANGELOG.md
 
-  if flag(network-uri)
-      build-depends: network-uri >= 2.6, network >= 2.6
-  else
-      build-depends: network-uri < 2.6, network >= 2.4 && < 2.6
+source-repository head
+  type: git
+  location: https://github.com/codedownio/hs-webdriver
 
-  exposed-modules: Test.WebDriver
-                   Test.WebDriver.Class
-                   Test.WebDriver.Monad
-                   Test.WebDriver.Session
-                   Test.WebDriver.Session.History
-                   Test.WebDriver.Config
-                   Test.WebDriver.Exceptions
-                   Test.WebDriver.Commands
-                   Test.WebDriver.Commands.Wait
-                   Test.WebDriver.Commands.Internal
-                   Test.WebDriver.Common.Profile
-                   Test.WebDriver.Common.Keys
-                   Test.WebDriver.Firefox.Profile
-                   Test.WebDriver.Chrome.Extension
-                   Test.WebDriver.Capabilities
-                   Test.WebDriver.Types
-                   Test.WebDriver.JSON
-                   Test.WebDriver.Utils
-                   Test.WebDriver.Internal
-                   Test.WebDriver.Exceptions.Internal
+library
+  exposed-modules:
+      Test.WebDriver
+      Test.WebDriver.Capabilities
+      Test.WebDriver.Chrome.Extension
+      Test.WebDriver.Class
+      Test.WebDriver.Commands
+      Test.WebDriver.Commands.Internal
+      Test.WebDriver.Commands.Wait
+      Test.WebDriver.Common.Keys
+      Test.WebDriver.Common.Profile
+      Test.WebDriver.Config
+      Test.WebDriver.Cookies
+      Test.WebDriver.Exceptions
+      Test.WebDriver.Exceptions.Internal
+      Test.WebDriver.Firefox.Profile
+      Test.WebDriver.Internal
+      Test.WebDriver.JSON
+      Test.WebDriver.Monad
+      Test.WebDriver.Session
+      Test.WebDriver.Session.History
+      Test.WebDriver.Types
+      Test.WebDriver.Utils
+  other-modules:
+      Test.WebDriver.IO
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      aeson >=0.6.2.0
+    , attoparsec >=0.10
+    , base ==4.*
+    , base64-bytestring >=1.0
+    , bytestring >=0.9
+    , call-stack
+    , data-default-class
+    , directory >1.0
+    , directory-tree >=0.11
+    , exceptions >=0.4
+    , filepath >1.0
+    , http-client >=0.3
+    , http-types >=0.8
+    , lifted-base >=0.1
+    , monad-control >=0.3
+    , network >=2.6
+    , network-uri >=2.6
+    , scientific >=0.2
+    , temporary >=1.0
+    , text >=0.11.3
+    , time >1.0
+    , transformers >=0.4
+    , transformers-base >=0.1
+    , unordered-containers >=0.1.3
+    , vector >=0.3
+    , zip-archive >=0.1.1.8
+  default-language: Haskell2010
