Code Monkey home page Code Monkey logo

cdp-hs's Introduction

build

cdp-hs

A Haskell library for the Chrome Devtools Protocol (CDP), generated from the protocol's definition files.

Example usage

Ensure Chrome is running with the remote debugging port enabled:

$ chromium --headless --remote-debugging-port=9222 https://wikipedia.com

The following program can be used to print a page to PDF, with Base64 encoded data being read in chunks:

{-# LANGUAGE OverloadedStrings   #-}

module Main where

import Data.Maybe
import Data.Default
import qualified Data.ByteString.Base64.Lazy as Base64
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL

import qualified CDP as CDP

main :: IO ()
main = CDP.runClient def printPDF

printPDF :: CDP.Handle -> IO ()
printPDF handle = do
    -- send the Page.printToPDF command
    r <- CDP.sendCommandWait handle $ CDP.pPagePrintToPDF
        { CDP.pPagePrintToPDFTransferMode = Just CDP.PPagePrintToPDFTransferModeReturnAsStream
        }

    -- obtain stream handle from which to read pdf data
    let streamHandle = fromJust . CDP.pagePrintToPDFStream $ r

    -- read pdf data 24000 bytes at a time
    let params = CDP.PIORead streamHandle Nothing $ Just 24000
    reads <- whileTrue (not . CDP.iOReadEof) $ CDP.sendCommandWait handle params
    let dat = map decode reads
    BL.writeFile "mypdf.pdf" $ BL.concat dat

decode :: CDP.IORead -> BL.ByteString
decode ior = if (CDP.iOReadBase64Encoded ior == Just True)
    then Base64.decodeLenient lbs
    else lbs
  where
    lbs = TL.encodeUtf8 . TL.fromStrict . CDP.iOReadData $ ior

whileTrue :: Monad m => (a -> Bool) -> m a -> m [a]
whileTrue f act = do
    a <- act
    if f a
        then pure . (a :) =<< whileTrue f act
        else pure [a]

More examples can be found in examples.

Generating the CDP library

cabal run cdp-gen

Current state

Project board

Commands and events for all non-deprecated domains are supported.

Sessions can also be created with a target (such as a tab), which can be used to restrict the scope of commands and events for the target. See examples/sessions.hs for example usage.

Contributing

PRs are welcome! If you would like to discuss changes or have any feedback, feel free to open an issue.

Acknowledgements

This began as a Summer of Haskell / GSoC project. Albert Krewinkel (@tarleb), Jasper Van der Jeugt (@jaspervdj) and Romain Lesur (@RLesur) provided valuable feedback and support which along with raising the library's quality, has made this all the more enjoyable to work on.

References

cdp-hs's People

Contributors

arsalan0c avatar jaspervdj avatar mulderr avatar thomasjm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

cdp-hs's Issues

Avoid generating functions on empty lists

For all commands, the catMaybes function is applied on a list of optional arguments received by the command.
This is redundantly the case, even when the list is empty.

Improve events interface - deconstruction of value

The set of all event results are represented as a tagged union (EventReturn).
For a particular event received, deconstruction is required in the user's code, in order to obtain the result.

For example:

f (CDP.EventReturnPageWindowOpen v) = v

An event handler is defined as: type Handler = EventReturn -> IO ()
The deconstruction function can be used as part of the handler:
doSomethingWithValue . f

Ideally, f would not be required of a user.

Separate generation of domains

Generate each domain into a separate module to:

  • improve compile times
  • lessen verbosity of types and functions that have the domain name prefixed

Depends on #11

Support events

  • event subscribing
  • event unsubscribing
  • events can be subscribed / unsubscribed at any point in user's program

Random command ID

Hi,

do you accept issues/PRs at this point?

If so, in CDP.Runtime:

randomGen      <- newMVar . mkStdGen $ 42
(...)

randomCommandId :: Handle -> IO CommandId
randomCommandId handle = modifyMVar (randomGen handle) $ \g -> do
    let (id, g2) = uniformR (0 :: Int, 1000 :: Int) g
    pure (g2, CommandId id)

This isn't very randomy and repeats within the first 10 elements: [560,663,95,783,208,466,913,535,651,95]

However, instead of making it properly random, reading this:

Every command that is sent over to CDP must have a unique 'id' parameter. Message responses will be delivered over websocket and will have the same 'id'.

I'm wondering why have random ids at all? Maybe an MVar Int that starts at 1 and increments for each new command?

Puppeteer seems to do just that.

Refactor code generator

  • modularly define functions, making it clear what each function is generating
  • reuse functions wherever possible

Use a sum type for domains, commands and events

A command to send is specified using Strings representing the domain name and command name. This can be represented with a type to improve type safety of the generated code for invoking commands. Commands should only be used with their respective domain.

Possible implementations:

  • separate types for domains and commands with sub typing
  • a sum type for all commands, with their respective domain name prefixed

This applies to events as well, with the additional constraint that when subscribing to an event, the given handler should correspond to the event being subscribed to.

Improve code quality

    • PageInfo could be a newtype
    • FromJSON instance for EventName could use read to shorten it (but it doesn't matter much, given that the instance is generated)
    • FromJSON instances: we should fail instead of error: the latter will cause the program to terminate, while the error can be recovered when using fail.
    • We could think about a custom error type for exceptions
    • I was wondering if ByteString or Text might be a better type for than String in some cases? Haven't thought that through yet.
    • RecordWildCards is really convenient and nice for writing, but often reduces readability. I have removed it from my projects for that reason, but its a matter of taste. The use here is perfectly fine.
    • It could make sense to use Language DataStrict; I don't think we benefit much from lazyness in this library.
    • Make the functions of type String -> String -> ... -> IO a more pleasant to use. One way would be to define an option record for each of these functions.
    • drop unnecessary module exports (eg. module CDP.Internal.Runtime (module CDP.Internal.Runtime))

Add tests for commands and events

The tests (in src/test/Main.hs) could benefit from more usage of session functionality through the following functions:

  • sendCommandForSession
  • sendCommandForSessionWait
  • subscribeForSession

Use generics for JSON instances

Manual FromJSON and ToJSON instances are generated for argument and return datatypes.

Depending on the number of fields, these instances can be lengthy.

Using generics will result in fewer lines of code generated.
It will also simplify the code generator as a result of not having to keep track of field names and their corresponding Haskell representations.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.