{"id":13804668,"url":"https://github.com/joffrey-bion/chrome-devtools-kotlin","last_synced_at":"2026-02-28T03:29:03.919Z","repository":{"id":39713177,"uuid":"304337222","full_name":"joffrey-bion/chrome-devtools-kotlin","owner":"joffrey-bion","description":"An asynchronous coroutine-based Kotllin client for the Chrome DevTools Protocol","archived":false,"fork":false,"pushed_at":"2026-02-21T22:37:35.000Z","size":6344,"stargazers_count":58,"open_issues_count":10,"forks_count":8,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-22T04:33:35.392Z","etag":null,"topics":["async","chrome","chrome-devtools","chrome-devtools-kotlin","chrome-devtools-protocol","chrome-headless","chromedp","coroutines","devtools","kotlin","kotlin-client","protocol"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joffrey-bion.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-15T13:36:09.000Z","updated_at":"2026-02-22T02:31:53.000Z","dependencies_parsed_at":"2023-12-13T05:25:18.676Z","dependency_job_id":"5bab6acb-a01a-4b96-89c4-d22671b5466a","html_url":"https://github.com/joffrey-bion/chrome-devtools-kotlin","commit_stats":null,"previous_names":[],"tags_count":136,"template":false,"template_full_name":null,"purl":"pkg:github/joffrey-bion/chrome-devtools-kotlin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joffrey-bion%2Fchrome-devtools-kotlin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joffrey-bion%2Fchrome-devtools-kotlin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joffrey-bion%2Fchrome-devtools-kotlin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joffrey-bion%2Fchrome-devtools-kotlin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joffrey-bion","download_url":"https://codeload.github.com/joffrey-bion/chrome-devtools-kotlin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joffrey-bion%2Fchrome-devtools-kotlin/sbom","scorecard":{"id":527666,"data":{"date":"2025-08-18","repo":{"name":"github.com/joffrey-bion/chrome-devtools-kotlin","commit":"3353628ac4594d12b72a2d771aae970b266ae510"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":3.6,"checks":[{"name":"Maintained","score":10,"reason":"22 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/release.yml:1","Warn: no topLevel permission defined: .github/workflows/test-chrome.yml:1","Warn: no topLevel permission defined: .github/workflows/update-gradlew.yml:1","Warn: no topLevel permission defined: .github/workflows/update-protocol.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/15 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:56: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/release.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test-chrome.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/test-chrome.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/update-gradlew.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-gradlew.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/update-gradlew.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-gradlew.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/update-gradlew.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-gradlew.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/update-protocol.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-protocol.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/update-protocol.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-protocol.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/update-protocol.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-protocol.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/update-protocol.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-protocol.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/update-protocol.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/joffrey-bion/chrome-devtools-kotlin/update-protocol.yml/main?enable=pin","Info:   0 out of  10 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   9 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":3,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: branch 'main' does not require approvers","Warn: codeowners review is not required on branch 'main'","Warn: 'up-to-date branches' is disabled on branch 'main'","Info: status check found to merge onto on branch 'main'","Warn: PRs are not required to make changes on branch 'main'; or we don't have data to detect it.If you think it might be the latter, make sure to run Scorecard with a PAT or use Repo Rules (that are always public) instead of Branch Protection settings"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"10 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc","Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T04:52:01.740Z","repository_id":39713177,"created_at":"2025-08-20T04:52:01.740Z","updated_at":"2025-08-20T04:52:01.740Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29922071,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T19:37:42.220Z","status":"ssl_error","status_checked_at":"2026-02-27T19:37:41.463Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","chrome","chrome-devtools","chrome-devtools-kotlin","chrome-devtools-protocol","chrome-headless","chromedp","coroutines","devtools","kotlin","kotlin-client","protocol"],"created_at":"2024-08-04T01:00:52.283Z","updated_at":"2026-02-28T03:29:03.902Z","avatar_url":"https://github.com/joffrey-bion.png","language":"Kotlin","funding_links":[],"categories":["Chrome DevTools Protocol"],"sub_categories":["Libraries for driving the protocol (or a layer above)"],"readme":"# Chrome Devtools Kotlin\n\n[![Maven central version](https://img.shields.io/maven-central/v/org.hildan.chrome/chrome-devtools-kotlin.svg)](http://mvnrepository.com/artifact/org.hildan.chrome/chrome-devtools-kotlin)\n[![Github Build](https://img.shields.io/github/actions/workflow/status/joffrey-bion/chrome-devtools-kotlin/build.yml?branch=main)](https://github.com/joffrey-bion/chrome-devtools-kotlin/actions/workflows/build.yml)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/joffrey-bion/chrome-devtools-kotlin/blob/master/LICENSE)\n\nAn asynchronous coroutine-based multiplatform Kotlin client for the\n[Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).\n\n## Protocol version \u0026 Code generation\n\nPart of this client is generated based on information from the \"latest\" (a.k.a \n[\"tip-of-tree\"](https://chromedevtools.github.io/devtools-protocol/tot/)) JSON descriptors found in the \n[ChromeDevTools/devtools-protocol](https://github.com/ChromeDevTools/devtools-protocol/tree/master/json) repository.\nAll the domains' commands and events defined in these protocol descriptors are therefore available in\n`chrome-devtools-kotlin`, as well as their doc and deprecated/experimental status. \n\nThe protocol definitions are automatically updated daily, but releases of `chrome-devtools-kotlin` are still manual.\nIf you're missing some APIs or updates, don't hesitate to open an issue to request a new release with updated protocol.\n\nYou can find the protocol version used by `chrome-devtools-kotlin` after the `-` in the version number.\nFor instance, version `1.3.0-861504` of `chrome-devtools-kotlin` was built using version `861504` of the Chrome \nDevTools Protocol (this is technically the Chromium revision, but it gives the tip-of-tree version number of the \nprotocol).\n\n## Concepts\n\n### Domains\n\nThe Chrome Devtools Protocol defines **_domains_** that expose some commands and events.\nThey are subsets of the protocol's API.\nYou can find the list and documentation of all domains in the \n[protocol's web page](https://chromedevtools.github.io/devtools-protocol/).\n\nThis library defines a type for each domain (e.g. `PageDomain`, `StorageDomain`...), which exposes:\n\n* a `suspend` method for each command, accepting a *request* type and returning a *response* type,\n  respectively containing the input and output parameters defined in the protocol for that command.\n* additional `suspend` functions for each command with a more convenient signature.\n* a method for each type of event, returning a `Flow` of this particular event type\n* an `events()` method, returning a `Flow` of all events of the domain\n\nThe domains usually also expose an `enable()` command which is required to enable events.\nWithout calling it, you will receive no events in the `Flow` subscriptions.\n\n### Targets\n\nClients can interact with different parts of Chrome such as pages (tabs), service workers, and extensions. \nThese parts are called **_targets_**.\nThe browser itself is also a target.\n\nEach type of target supports a different subset of the domains defined in the protocol.\n\n### Sessions\n\nThe protocol requires you to attach to a target in order to interact with it.\nAttaching to a target opens a target **_session_** of the relevant type, such as `BrowserSession` or \n`PageSession`.\n\nWhen connecting to Chrome, a browser target is automatically attached, thus you obtain a `BrowserSession`.\nYou can then use this session to attach to other targets (child targets), such as pages.\n\nEach of the supported domains are defined as properties of the session type, which provides a type-safe way to know\nif the attached target supports a given domain.\nFor instance, `PageSession.dom` gives access to the DOM domain in this page session, which allows to issue commands \nand listen to DOM events.\n\n\u003e Note: The supported set of domains for each target type is \n\u003e [not clearly defined](https://github.com/ChromeDevTools/devtools-protocol/issues/311) by the protocol, so I have to\n\u003e regularly extract this information from Chromium's source code and update my own extra definition file:\n\u003e [target_types.json](protocol-definition/target_types.json).\n\u003e This process is described in details in a dedicated [README](protocol-definition/README.md).\n\u003e \n\u003e Because of this, there might be some missing domains on some session types at some point in time that require\n\u003e manual adjustment.\n\u003e If this is the case, use the `PageSession.unsafe()` method on the session object to get full access to all domains\n\u003e (also, please open an issue so I can fix the missing domain).\n\n## Quick Start\n\n### Add the dependency\n\nThis library is available on Maven Central.\n\nIt requires a [Ktor engine](https://ktor.io/docs/http-client-engines.html) to work, so make sure to add one that\nsupports the web sockets feature (check the\n[compatibility table](https://ktor.io/docs/http-client-engines.html#limitations)).\nFor example, you can pick the CIO engine by adding this engine to your dependencies next to `chrome-devtools-kotlin`:\n\n```\ndependencies {\n    implementation(\"org.hildan.chrome:chrome-devtools-kotlin:$version\")\n    implementation(\"io.ktor:ktor-client-cio:$ktorVersion\")\n}\n```\n\n### Get a browser running\n\nThis library doesn't provide a way to start a browser programmatically.\nIt assumes a browser was started externally and exposes a debugger server.\nBelow are some options to get a browser running.\n\n#### Docker: Zenika Alpine Chrome\n\nStart a Headless Chrome browser using \n[the `zenika/alpine-chrome` Docker image](https://hub.docker.com/r/zenika/alpine-chrome) with the following command:\n\n```shell\ndocker run --rm -p 9222:9222 zenika/alpine-chrome --no-sandbox --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 about:blank\n```\n\nThis exposes the debugger server at `http://localhost:9222` (see the next section for how to connect).\n\n#### Docker: Browserless Chromium\n\nStart a Headless Chromium using the [Browserless Docker image](https://docs.browserless.io/baas/docker/quickstart):\n\n```shell\ndocker run --rm -p 3000:3000 ghcr.io/browserless/chromium\n```\n\nThis allows direct web socket connections at `ws://localhost:3000` (see the next section for how to connect).\n\n#### Browserless - Browser as a Service (BaaS)\n\n[Browserless BaaS](https://docs.browserless.io/baas/hosted-service/how-it-works) provides readily available chrome\nbrowsers so you don't have to start anything locally.\nThey have some free plan which might be useful when Docker is not an option.\n\nOnce you have registered and got your token, you will be able to connect to the web socket directly using the URL\n`wss://production-sfo.browserless.io/?token=\u003cTOKEN\u003e` (see the next section for how to connect).\n\n### Connecting to the browser\n\nOnce you got your HTTP or web socket URL to the Chrome debugger, use `ChromeDP.connect(url)` to connect to the debugger\nand start your browser session:\n\n```kotlin\nval browserSession: BrowserSession = ChromeDP.connect(\"http://localhost:9222\")\n```\n\nWhen you're done with the session, don't forget to `close()` it in order to close the underlying web socket.\nYou can also use the familiar `use { ... }` extension, which automatically closes the session in every code path\nleaving the lambda (even in case of exception).\n\n### Connecting to targets\n\nThe `BrowserSession` is attached to a browser target, but we're usually interested in more useful targets, such as pages.\nOnce you have your browser session, you can use it to create child page targets and attach to them.\nHere is an example to create a new page target and attach to it:\n\n```kotlin\nChromeDP.connect(\"http://localhost:9222\").use { browserSession -\u003e\n    browserSession.newPage().use { pageSession -\u003e\n        // goto() navigates the current page to the URL and awaits the 'load' event by default\n        pageSession.goto(\"http://example.com\")\n\n        // This page session has access to many useful protocol domains (e.g. dom, page...)\n        val doc = pageSession.dom.getDocument().root\n        val base64Img = pageSession.page.captureScreenshot {\n            format = ScreenshotFormat.jpeg\n            quality = 80\n        }\n    } // automatically closes the pageSession\n} // automatically closes the browserSession\n```\n\n### High level extensions\n\nIn addition to the generated domain commands and events, some extensions are provided for higher-level functionality.\nYou can discover them using auto-complete in your IDE.\n\nThe most useful of them are documented in this section.\n\n#### PageSession extensions\n\n* `PageSession.goto(url: String)`: navigates to a URL and also waits for the next `load` event, or other events\n* `PageSession.clickOnElement(selector: CssSelector, clickDuration: Duration, mouseButton: MouseButton)`: finds a node\n  using a selector query and simulates a click on it\n\n#### DOM Domain extensions\n\n* `DOMDomain.getNodeBySelector(selector: CssSelector): NodeId?`: finds a node using a selector query, or throws if not found\n* `DOMDomain.findNodeBySelector(selector: CssSelector): NodeId?`: finds a node using a selector query, or returns null\n* `DOMDomain.awaitNodeBySelector(selector: CssSelector, pollingPeriod: Duration): NodeId?`: suspends until a node\n  corresponding to the selector query appears in the DOM.\n* `DOMDomain.awaitNodeAbsentBySelector(selector: CssSelector, pollingPeriod: Duration): NodeId?`: suspends until a node\n  corresponding to the selector query disappears from the DOM.\n* `DOMDomain.getTypedAttributes(nodeSelector: CssSelector): DOMAttributes?`: gets the attributes of the node\n  corresponding to the given `nodeSelector`, or null if the selector didn't match any node.\n* `DOMDomain.getAttributeValue(nodeSelector: CssSelector, attributeName: String): String?`: gets the value of the \n  attribute `attributeName` of the node corresponding to the given `nodeSelector`, or null if the node wasn't found.\n* `DOMDomain.setAttributeValue(nodeSelector: CssSelector, name: String, value: String)`: sets the value of the \n  attribute `attributeName` of the node corresponding to the given `nodeSelector`.\n\n#### Page Domain extensions\n\n* `CaptureScreenshotResponse.decodeData(): ByteArray`: decodes a base64 encoded image (from `PageDomain.captureScreenshot`) into bytes\n* `PageDomain.captureScreenshotToFile(outputFile: Path, options)` (JVM-only)\n\n#### Runtime Domain extensions\n\n* `\u003cT\u003e RuntimeDomain.evaluateJs(js: String): T?`: evaluates JS and returns the result (uses Kotlinx serialization to\n  deserialize JS results)\n\n#### Examples\n\nExample usage of `Runtime.evaluateJs(js: String)`:\n\n```kotlin\n@Serializable\ndata class Person(val firstName: String, val lastName: String)\n\nval evaluatedInt = pageSession.runtime.evaluateJs\u003cInt\u003e(\"42\")\nassertEquals(42, evaluatedInt)\n\nval evaluatedPerson = pageSession.runtime.evaluateJs\u003cPerson\u003e(\"\"\"eval({firstName: \"Bob\", lastName: \"Lee Swagger\"})\"\"\")\nassertEquals(Person(\"Bob\", \"Lee Swagger\"), evaluatedPerson)\n```\n\nNote that the deserialization here uses [Kotlinx Serialization](https://github.com/Kotlin/kotlinx.serialization), \nwhich requires annotating the deserialized classes with `@Serializable` _and_ using the corresponding compiler plugin.\n\n### Troubleshooting\n\n\u003e *Host header is specified and is not an IP address or localhost*\n\nSometimes this error also appears in the form of an HTTP 500.\n\nChrome doesn't accept a `Host` header that is not an IP nor `localhost`, but in some environments it might be hard\nto provide this (e.g. docker services in a docker swarm, communicating using service names).\n\nTo work around this problem, simply set `overrideHostHeader` to true when creating `ChromeDPClient`.\nThis overrides the `Host` header to \"localhost\" in the HTTP requests to the Chrome debugger to make it happy, and\nalso replaces the host in subsequent web socket URLs (returned by Chrome) by the initial host provided in\n`remoteDebugUrl`.\nThis is necessary because Chrome uses the `Host` header to build these URLs, and it would be incorrect to keep this.\n\n## License\n\nThis project is distributed under the MIT license.\n\n## Alternatives\n\nIf you're looking for Chrome Devtools Protocol clients _in Kotlin_, I have only found one other so far, \n[chrome-reactive-kotlin](https://github.com/wendigo/chrome-reactive-kotlin).\nThis is the reactive equivalent of this project.\nThe main differences are the following:\n\n* it only supports the JVM platform\n* it uses a _reactive_ API (as opposed to coroutines and suspend functions)\n* it doesn't distinguish target types, and thus doesn't restrict the available domains at compile time\n  (it's the equivalent of always using `unsafe()` in `chrome-devtools-kotlin`)\n\nI needed a coroutine-based API instead and more type-safety, which is how this `chrome-devtools-kotlin` \nproject was born.\n\nYou can find alternative Chrome DevTools libraries in other languages in this\n[awesome list](https://github.com/ChromeDevTools/awesome-chrome-devtools).\n\n## Credits\n\nSpecial thanks to @wendigo and his project [chrome-reactive-kotlin](https://github.com/wendigo/chrome-reactive-kotlin)\nwhich inspired the creation of this project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoffrey-bion%2Fchrome-devtools-kotlin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoffrey-bion%2Fchrome-devtools-kotlin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoffrey-bion%2Fchrome-devtools-kotlin/lists"}