├── .circleci └── config.yml ├── .github └── workflows │ └── push.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bb.clj ├── deps.edn ├── docs └── api │ ├── clj-chrome-devtools.automation.fixture.html │ ├── clj-chrome-devtools.automation.html │ ├── clj-chrome-devtools.automation.launcher.html │ ├── clj-chrome-devtools.commands.accessibility.html │ ├── clj-chrome-devtools.commands.animation.html │ ├── clj-chrome-devtools.commands.application-cache.html │ ├── clj-chrome-devtools.commands.audits.html │ ├── clj-chrome-devtools.commands.background-service.html │ ├── clj-chrome-devtools.commands.browser.html │ ├── clj-chrome-devtools.commands.cache-storage.html │ ├── clj-chrome-devtools.commands.cast.html │ ├── clj-chrome-devtools.commands.console.html │ ├── clj-chrome-devtools.commands.css.html │ ├── clj-chrome-devtools.commands.database.html │ ├── clj-chrome-devtools.commands.debugger.html │ ├── clj-chrome-devtools.commands.device-orientation.html │ ├── clj-chrome-devtools.commands.dom-debugger.html │ ├── clj-chrome-devtools.commands.dom-snapshot.html │ ├── clj-chrome-devtools.commands.dom-storage.html │ ├── clj-chrome-devtools.commands.dom.html │ ├── clj-chrome-devtools.commands.domdebugger.html │ ├── clj-chrome-devtools.commands.domsnapshot.html │ ├── clj-chrome-devtools.commands.domstorage.html │ ├── clj-chrome-devtools.commands.emulation.html │ ├── clj-chrome-devtools.commands.event-breakpoints.html │ ├── clj-chrome-devtools.commands.fetch.html │ ├── clj-chrome-devtools.commands.headless-experimental.html │ ├── clj-chrome-devtools.commands.heap-profiler.html │ ├── clj-chrome-devtools.commands.indexed-db.html │ ├── clj-chrome-devtools.commands.input.html │ ├── clj-chrome-devtools.commands.inspector.html │ ├── clj-chrome-devtools.commands.io.html │ ├── clj-chrome-devtools.commands.layer-tree.html │ ├── clj-chrome-devtools.commands.log.html │ ├── clj-chrome-devtools.commands.media.html │ ├── clj-chrome-devtools.commands.memory.html │ ├── clj-chrome-devtools.commands.network.html │ ├── clj-chrome-devtools.commands.overlay.html │ ├── clj-chrome-devtools.commands.page.html │ ├── clj-chrome-devtools.commands.performance-timeline.html │ ├── clj-chrome-devtools.commands.performance.html │ ├── clj-chrome-devtools.commands.profiler.html │ ├── clj-chrome-devtools.commands.runtime.html │ ├── clj-chrome-devtools.commands.schema.html │ ├── clj-chrome-devtools.commands.security.html │ ├── clj-chrome-devtools.commands.service-worker.html │ ├── clj-chrome-devtools.commands.storage.html │ ├── clj-chrome-devtools.commands.system-info.html │ ├── clj-chrome-devtools.commands.target.html │ ├── clj-chrome-devtools.commands.tethering.html │ ├── clj-chrome-devtools.commands.tracing.html │ ├── clj-chrome-devtools.commands.web-audio.html │ ├── clj-chrome-devtools.commands.web-authn.html │ ├── clj-chrome-devtools.core.html │ ├── clj-chrome-devtools.events.html │ ├── clj-chrome-devtools.impl.command.html │ ├── clj-chrome-devtools.impl.connection.html │ ├── clj-chrome-devtools.impl.define.html │ ├── clj-chrome-devtools.impl.util.html │ ├── clj-chrome-devtools.protocol-definitions.html │ ├── css │ ├── default.css │ └── highlight.css │ ├── index.html │ └── js │ ├── highlight.min.js │ ├── jquery.min.js │ └── page_effects.js ├── project.clj ├── resources ├── clj_chrome_devtools_runner.tpl └── test-page.tpl ├── src └── clj_chrome_devtools │ ├── automation.cljc │ ├── automation │ ├── fixture.clj │ └── launcher.clj │ ├── cljs │ └── test.clj │ ├── commands │ ├── accessibility.clj │ ├── animation.clj │ ├── application_cache.clj │ ├── audits.clj │ ├── background_service.clj │ ├── browser.clj │ ├── cache_storage.clj │ ├── cast.clj │ ├── console.clj │ ├── css.clj │ ├── database.clj │ ├── debugger.clj │ ├── device_orientation.clj │ ├── dom.clj │ ├── dom_debugger.clj │ ├── dom_snapshot.clj │ ├── dom_storage.clj │ ├── emulation.clj │ ├── event_breakpoints.clj │ ├── fetch.clj │ ├── headless_experimental.clj │ ├── heap_profiler.clj │ ├── indexed_db.clj │ ├── input.clj │ ├── inspector.clj │ ├── io.clj │ ├── layer_tree.clj │ ├── log.clj │ ├── media.clj │ ├── memory.clj │ ├── network.clj │ ├── overlay.clj │ ├── page.clj │ ├── performance.clj │ ├── performance_timeline.clj │ ├── profiler.clj │ ├── runtime.clj │ ├── schema.clj │ ├── security.clj │ ├── service_worker.clj │ ├── storage.clj │ ├── system_info.clj │ ├── target.clj │ ├── tethering.clj │ ├── tracing.clj │ ├── web_audio.clj │ └── web_authn.clj │ ├── core.clj │ ├── events.clj │ ├── impl │ ├── command.clj │ ├── connection.cljc │ ├── define.clj │ └── util.clj │ └── protocol_definitions.clj ├── test ├── clj_chrome_devtools │ ├── automation_test.clj │ ├── chrome_test.clj │ ├── domain_test.clj │ └── impl │ │ └── connection_test.clj └── test-page.html └── update-devtools-protocol.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Clojure CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-clojure/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/clojure:lein-2.7.1-browsers 11 | 12 | working_directory: ~/repo 13 | 14 | environment: 15 | LEIN_ROOT: "true" 16 | # Customize the JVM maximum heap limit 17 | JVM_OPTS: -Xmx3200m 18 | 19 | steps: 20 | - checkout 21 | 22 | - run: git submodule update --init 23 | 24 | # Download and cache dependencies 25 | - restore_cache: 26 | keys: 27 | - v1-dependencies-{{ checksum "project.clj" }} 28 | # fallback to using the latest cache if no exact match is found 29 | - v1-dependencies- 30 | 31 | - run: lein deps 32 | 33 | - save_cache: 34 | paths: 35 | - ~/.m2 36 | key: v1-dependencies-{{ checksum "project.clj" }} 37 | 38 | # run tests! 39 | - run: lein test -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: Push 2 | on: push 3 | jobs: 4 | Test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - name: Install Clojure 9 | # So we’ll have the `clojure` command and tools.deps. 10 | run: | 11 | curl -O https://download.clojure.org/install/linux-install-1.10.1.469.sh 12 | chmod +x linux-install-1.10.1.469.sh 13 | sudo ./linux-install-1.10.1.469.sh 14 | # Once GitHub adds a dependency caching feature to Actions we’ll be able to use it here to speed 15 | # up this job. In the meantime, the following step will download all the deps every time it is 16 | # run. 17 | - name: Run tests 18 | run: clojure -A:test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *.jar 4 | *.class 5 | /lib/ 6 | /classes/ 7 | /target/ 8 | /checkouts/ 9 | .cpcache 10 | .lein-deps-sum 11 | .lein-repl-history 12 | .lein-plugins/ 13 | .lein-failures 14 | .nrepl-port 15 | .idea 16 | *.iml 17 | /CLOJARS_deploy_token 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatut/clj-chrome-devtools/7c895ab355b17a630b38070a334eef2196b0f91a/.gitmodules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 20220405 2 | - Major changes under the hood 3 | - Add support for using with Babashka 4 | - Remove jetty (use JDK11 HTTP library native WebSocket) 5 | - Remove timbre (prefer clojure.tools.logging in libraries) 6 | - Remove core.async (use simpler promises and callbacks) 7 | 8 | ## 20200423 9 | - Avoid blocking IO in go-block (#27) 10 | 11 | ## 20190601 12 | - Change test output video to APNG for better quality 13 | - Generate videos even if cljs tests fail 14 | - Add :loop-video? option to control looping 15 | 16 | ## 20190530.1 17 | - Add :verbose? flag to test runner (for better troubleshooting) 18 | - Add :ring-handler flag to test runner (if tests need to load some resources) 19 | - Detect if test page has error before tests are started 20 | 21 | ## 20190530 22 | - Ability to take screenshots in cljs tests (use `(js/screenshot)`) 23 | - Combine multiple screenshots to GIF animation 24 | 25 | 26 | ## 20190529 27 | - More reliable project.clj loading in cljs test runner 28 | 29 | ## 20190502 30 | - Deps update release 31 | 32 | ## 20190329 33 | - Made ws client optionally configurable 34 | 35 | ## 20180528 36 | - Add `:no-sandbox?` option (mikkoronkkomaki) 37 | 38 | ## 20180310 39 | - ClojureScript test runner as a Clojure test (see `clj-chrome-devtools.cljs.test` namespace) 40 | - Moved to date based versioning 41 | 42 | ## [0.2.4] - 2018-01-04 43 | - Fixed connection timeout (hagmonk) 44 | - Changed generated API inflections (hagmonk) 45 | 46 | ## [0.2.3] - 2017-12-20 47 | - Started this change log 48 | - Improved connection handling (thanks hagmonk) 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # New automation functionality 2 | 3 | ​The higher level automation utilities is where most of the work is done and new automation utilities are welcome. 4 | PRs welcome, just follow the common pattern of providing two arities: one for using the global automation context 5 | and one where it is provided as the first parameter. 6 | 7 | # Compatibility 8 | 9 | Please add tests for new functionality. Breaking existing tests or changing published API will not be merged. 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tatu Tarvainen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clj-chrome-devtools 2 | 3 | [![Clojars Project](https://img.shields.io/clojars/v/clj-chrome-devtools.svg)](https://clojars.org/clj-chrome-devtools) ![Tests](https://github.com/tatut/clj-chrome-devtools/workflows/Push/badge.svg) 4 | 5 | clj-chrome-devtools is a simple library for controlling a headless Chrome with the 6 | Chrome DevTools Protocol. The protocol is based on a websocket connection between 7 | clojure and chrome. All the functions are automatically generated from the protocol 8 | specification JSON files. 9 | 10 | ## Quick start with Babashka 11 | 12 | See [bb.clj](https://github.com/tatut/clj-chrome-devtools/blob/master/bb.clj) file for a quick 13 | script that can be used from [Babashka](https://babashka.org). 14 | 15 | ## Goals 16 | 17 | The goal of the project is to provide a general purpose library for using headless chrome, both in 18 | production use and in testing scenarios. 19 | 20 | The CDP part, which is autogenerated, should be stable and will be updated when CDP protocol changes. 21 | 22 | The higher level automation utilities is where most of the work is done and new automation utilities are welcome. 23 | PRs welcome, just follow the common pattern of providing two arities: one for using the global automation context and 24 | one where it is provided as the first parameter. 25 | 26 | 27 | ## API Docs 28 | 29 | See codox generated [API docs](https://tatut.github.io/clj-chrome-devtools/api/index.html). 30 | 31 | All the low-level auto-generated commands from Chrome Devtools Protocol are in `clj-chrome-devtools.command.*` 32 | namespaces. Note: to use the low-level API you need to implement event handlers for listening to data Chrome sends 33 | the client. 34 | 35 | There is the beginnings of a rudimentary higher level API in `clj-chrome-devtools.automation`. 36 | There is also a `clojure.test` fixture to run tests with a fresh headless chrome in `clj-chrome-devtools.automation.fixture`. 37 | 38 | ## Example usage 39 | 40 | For using the higher level API for screen scraping and browser testing, see [chrome_test.clj](https://github.com/tatut/clj-chrome-devtools/blob/master/test/clj_chrome_devtools/chrome_test.clj) file. 41 | 42 | The following shows a simple REPL usage, navigating to a page and inspecting content. 43 | The connection is the first parameter of all calls. You can also set the connection to use with 44 | `set-current-connection!` and omit the connection parameter for convenience. 45 | 46 | 47 | ```clojure 48 | clj-chrome-devtools.core> (def c (connect "localhost" 9222)) 49 | #'clj-chrome-devtools.core/c 50 | clj-chrome-devtools.core> (require '[clj-chrome-devtools.commands.page :as page] 51 | '[clj-chrome-devtools.commands.dom :as dom]) 52 | nil 53 | clj-chrome-devtools.core> (page/navigate c {:url "http://webjure.org/"}) 54 | {:frame-id "68439.1"} 55 | clj-chrome-devtools.core> (dom/get-document c {:depth 1}) 56 | {:root 57 | {:children 58 | [{:node-type 1, 59 | :node-id 2, 60 | :backend-node-id 4, 61 | :parent-id 1, 62 | :node-name "HTML", 63 | :node-value "", 64 | :frame-id "68439.1", 65 | :local-name "html", 66 | :child-node-count 2, 67 | :attributes []}], 68 | :document-url "http://webjure.org/", 69 | :node-type 9, 70 | :base-url "http://webjure.org/", 71 | :node-id 1, 72 | :backend-node-id 3, 73 | :node-name "#document", 74 | :node-value "", 75 | :xml-version "", 76 | :local-name "", 77 | :child-node-count 1}} 78 | clj-chrome-devtools.core> (use 'clojure.repl) 79 | nil 80 | clj-chrome-devtools.core> (doc dom/get-outer-html) 81 | ------------------------- 82 | clj-chrome-devtools.commands.dom/get-outer-html 83 | ([] [{:as params, :keys [node-id]}] [connection {:as params, :keys [node-id]}]) 84 | Returns node's HTML markup. 85 | 86 | Parameters map keys: 87 | :node-id Id of the node to get markup for. 88 | 89 | Return map keys: 90 | :outer-html Outer HTML markup. 91 | nil 92 | clj-chrome-devtools.core> (dom/get-outer-html c {:node-id 1}) 93 | {:outer-html 94 | "\n Webjure\n \n \n Coming soon-ish!\n \n\n"} 95 | ``` 96 | 97 | ## Running ClojureScript test suite with `clj-chrome-devtools` 98 | 99 | The `clj-chrome-devtools.cljs.test` contains a function that can be used to build and run 100 | ClojureScript tests as part of your Clojure test without needing `doo` plugin or `karma` installed. 101 | 102 | See [example in Tuck project](https://github.com/tatut/tuck/blob/master/test/tuck/chrome_test.clj). 103 | 104 | The `build-and-test` function is meant to be called inside your test and it will build the specified 105 | ClojureScript build that is defined in `project.clj` (e.g. "test") with an added test runner that 106 | requires the specified namespaces and runs tests in them. 107 | -------------------------------------------------------------------------------- /bb.clj: -------------------------------------------------------------------------------- 1 | ;; Example of using clj-chrome-devtools with babashka 2 | ;; 3 | ;; Start Chrome in the background: 4 | ;; /some/chrome-binary --remote-debugging-port=9222 --headless & 5 | ;; 6 | ;; Run with: 7 | ;; $ bb bb.clj 8 | 9 | (require '[babashka.deps :as deps]) 10 | (deps/add-deps 11 | '{:deps {tatut/devtools {:git/url "https://github.com/tatut/clj-chrome-devtools" 12 | :git/sha "cc96255433ca406aaba8bcee17d0d0b3b16dc423"} 13 | org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha" 14 | :git/sha "1a841c4cc1d4f6dab7505a98ed2d532dd9d56b78"}}}) 15 | 16 | (require '[clj-chrome-devtools.core :as core] 17 | '[clj-chrome-devtools.automation :as auto]) 18 | 19 | (println "Printing to bb.pdf") 20 | (def a (auto/create-automation (core/connect))) 21 | (auto/to a "https://babashka.org") 22 | (auto/print-pdf a "bb.pdf" 23 | {:print-background true 24 | :paper-width 8.3 25 | :paper-height 11.7}) 26 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/clojure {:mvn/version "1.9.0"} 2 | cheshire/cheshire {:mvn/version "5.10.2"} 3 | org.clojure/tools.logging {:mvn/version "1.2.4"} 4 | java-http-clj/java-http-clj {:mvn/version "0.4.3"} 5 | } 6 | :paths ["src" "resources"] 7 | :aliases 8 | {:bb {:extra-deps {org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha" 9 | :git/sha "1a841c4cc1d4f6dab7505a98ed2d532dd9d56b78"}}} 10 | :test {:extra-paths ["test"] 11 | ; This is a simplistic test runner but it’s sufficient for now. 12 | :extra-deps {test-runner/test-runner {:git/url "https://github.com/cognitect-labs/test-runner" 13 | :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"}} 14 | :main-opts ["-m" "cognitect.test-runner"]}}} 15 | -------------------------------------------------------------------------------- /docs/api/css/default.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | font-size: 15px; 4 | } 5 | 6 | pre, code { 7 | font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; 8 | font-size: 9pt; 9 | margin: 15px 0; 10 | } 11 | 12 | h1 { 13 | font-weight: normal; 14 | font-size: 29px; 15 | margin: 10px 0 2px 0; 16 | padding: 0; 17 | } 18 | 19 | h2 { 20 | font-weight: normal; 21 | font-size: 25px; 22 | } 23 | 24 | h5.license { 25 | margin: 9px 0 22px 0; 26 | color: #555; 27 | font-weight: normal; 28 | font-size: 12px; 29 | font-style: italic; 30 | } 31 | 32 | .document h1, .namespace-index h1 { 33 | font-size: 32px; 34 | margin-top: 12px; 35 | } 36 | 37 | #header, #content, .sidebar { 38 | position: fixed; 39 | } 40 | 41 | #header { 42 | top: 0; 43 | left: 0; 44 | right: 0; 45 | height: 22px; 46 | color: #f5f5f5; 47 | padding: 5px 7px; 48 | } 49 | 50 | #content { 51 | top: 32px; 52 | right: 0; 53 | bottom: 0; 54 | overflow: auto; 55 | background: #fff; 56 | color: #333; 57 | padding: 0 18px; 58 | } 59 | 60 | .sidebar { 61 | position: fixed; 62 | top: 32px; 63 | bottom: 0; 64 | overflow: auto; 65 | } 66 | 67 | .sidebar.primary { 68 | background: #e2e2e2; 69 | border-right: solid 1px #cccccc; 70 | left: 0; 71 | width: 250px; 72 | } 73 | 74 | .sidebar.secondary { 75 | background: #f2f2f2; 76 | border-right: solid 1px #d7d7d7; 77 | left: 251px; 78 | width: 200px; 79 | } 80 | 81 | #content.namespace-index, #content.document { 82 | left: 251px; 83 | } 84 | 85 | #content.namespace-docs { 86 | left: 452px; 87 | } 88 | 89 | #content.document { 90 | padding-bottom: 10%; 91 | } 92 | 93 | #header { 94 | background: #3f3f3f; 95 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 96 | z-index: 100; 97 | } 98 | 99 | #header h1 { 100 | margin: 0; 101 | padding: 0; 102 | font-size: 18px; 103 | font-weight: lighter; 104 | text-shadow: -1px -1px 0px #333; 105 | } 106 | 107 | #header h1 .project-version { 108 | font-weight: normal; 109 | } 110 | 111 | .project-version { 112 | padding-left: 0.15em; 113 | } 114 | 115 | #header a, .sidebar a { 116 | display: block; 117 | text-decoration: none; 118 | } 119 | 120 | #header a { 121 | color: #f5f5f5; 122 | } 123 | 124 | .sidebar a { 125 | color: #333; 126 | } 127 | 128 | #header h2 { 129 | float: right; 130 | font-size: 9pt; 131 | font-weight: normal; 132 | margin: 4px 3px; 133 | padding: 0; 134 | color: #bbb; 135 | } 136 | 137 | #header h2 a { 138 | display: inline; 139 | } 140 | 141 | .sidebar h3 { 142 | margin: 0; 143 | padding: 10px 13px 0 13px; 144 | font-size: 19px; 145 | font-weight: lighter; 146 | } 147 | 148 | .sidebar h3 a { 149 | color: #444; 150 | } 151 | 152 | .sidebar h3.no-link { 153 | color: #636363; 154 | } 155 | 156 | .sidebar ul { 157 | padding: 7px 0 6px 0; 158 | margin: 0; 159 | } 160 | 161 | .sidebar ul.index-link { 162 | padding-bottom: 4px; 163 | } 164 | 165 | .sidebar li { 166 | display: block; 167 | vertical-align: middle; 168 | } 169 | 170 | .sidebar li a, .sidebar li .no-link { 171 | border-left: 3px solid transparent; 172 | padding: 0 10px; 173 | white-space: nowrap; 174 | } 175 | 176 | .sidebar li .no-link { 177 | display: block; 178 | color: #777; 179 | font-style: italic; 180 | } 181 | 182 | .sidebar li .inner { 183 | display: inline-block; 184 | padding-top: 7px; 185 | height: 24px; 186 | } 187 | 188 | .sidebar li a, .sidebar li .tree { 189 | height: 31px; 190 | } 191 | 192 | .depth-1 .inner { padding-left: 2px; } 193 | .depth-2 .inner { padding-left: 6px; } 194 | .depth-3 .inner { padding-left: 20px; } 195 | .depth-4 .inner { padding-left: 34px; } 196 | .depth-5 .inner { padding-left: 48px; } 197 | .depth-6 .inner { padding-left: 62px; } 198 | 199 | .sidebar li .tree { 200 | display: block; 201 | float: left; 202 | position: relative; 203 | top: -10px; 204 | margin: 0 4px 0 0; 205 | padding: 0; 206 | } 207 | 208 | .sidebar li.depth-1 .tree { 209 | display: none; 210 | } 211 | 212 | .sidebar li .tree .top, .sidebar li .tree .bottom { 213 | display: block; 214 | margin: 0; 215 | padding: 0; 216 | width: 7px; 217 | } 218 | 219 | .sidebar li .tree .top { 220 | border-left: 1px solid #aaa; 221 | border-bottom: 1px solid #aaa; 222 | height: 19px; 223 | } 224 | 225 | .sidebar li .tree .bottom { 226 | height: 22px; 227 | } 228 | 229 | .sidebar li.branch .tree .bottom { 230 | border-left: 1px solid #aaa; 231 | } 232 | 233 | .sidebar.primary li.current a { 234 | border-left: 3px solid #a33; 235 | color: #a33; 236 | } 237 | 238 | .sidebar.secondary li.current a { 239 | border-left: 3px solid #33a; 240 | color: #33a; 241 | } 242 | 243 | .namespace-index h2 { 244 | margin: 30px 0 0 0; 245 | } 246 | 247 | .namespace-index h3 { 248 | font-size: 16px; 249 | font-weight: bold; 250 | margin-bottom: 0; 251 | } 252 | 253 | .namespace-index .topics { 254 | padding-left: 30px; 255 | margin: 11px 0 0 0; 256 | } 257 | 258 | .namespace-index .topics li { 259 | padding: 5px 0; 260 | } 261 | 262 | .namespace-docs h3 { 263 | font-size: 18px; 264 | font-weight: bold; 265 | } 266 | 267 | .public h3 { 268 | margin: 0; 269 | float: left; 270 | } 271 | 272 | .usage { 273 | clear: both; 274 | } 275 | 276 | .public { 277 | margin: 0; 278 | border-top: 1px solid #e0e0e0; 279 | padding-top: 14px; 280 | padding-bottom: 6px; 281 | } 282 | 283 | .public:last-child { 284 | margin-bottom: 20%; 285 | } 286 | 287 | .members .public:last-child { 288 | margin-bottom: 0; 289 | } 290 | 291 | .members { 292 | margin: 15px 0; 293 | } 294 | 295 | .members h4 { 296 | color: #555; 297 | font-weight: normal; 298 | font-variant: small-caps; 299 | margin: 0 0 5px 0; 300 | } 301 | 302 | .members .inner { 303 | padding-top: 5px; 304 | padding-left: 12px; 305 | margin-top: 2px; 306 | margin-left: 7px; 307 | border-left: 1px solid #bbb; 308 | } 309 | 310 | #content .members .inner h3 { 311 | font-size: 12pt; 312 | } 313 | 314 | .members .public { 315 | border-top: none; 316 | margin-top: 0; 317 | padding-top: 6px; 318 | padding-bottom: 0; 319 | } 320 | 321 | .members .public:first-child { 322 | padding-top: 0; 323 | } 324 | 325 | h4.type, 326 | h4.dynamic, 327 | h4.added, 328 | h4.deprecated { 329 | float: left; 330 | margin: 3px 10px 15px 0; 331 | font-size: 15px; 332 | font-weight: bold; 333 | font-variant: small-caps; 334 | } 335 | 336 | .public h4.type, 337 | .public h4.dynamic, 338 | .public h4.added, 339 | .public h4.deprecated { 340 | font-size: 13px; 341 | font-weight: bold; 342 | margin: 3px 0 0 10px; 343 | } 344 | 345 | .members h4.type, 346 | .members h4.added, 347 | .members h4.deprecated { 348 | margin-top: 1px; 349 | } 350 | 351 | h4.type { 352 | color: #717171; 353 | } 354 | 355 | h4.dynamic { 356 | color: #9933aa; 357 | } 358 | 359 | h4.added { 360 | color: #508820; 361 | } 362 | 363 | h4.deprecated { 364 | color: #880000; 365 | } 366 | 367 | .namespace { 368 | margin-bottom: 30px; 369 | } 370 | 371 | .namespace:last-child { 372 | margin-bottom: 10%; 373 | } 374 | 375 | .index { 376 | padding: 0; 377 | font-size: 80%; 378 | margin: 15px 0; 379 | line-height: 16px; 380 | } 381 | 382 | .index * { 383 | display: inline; 384 | } 385 | 386 | .index p { 387 | padding-right: 3px; 388 | } 389 | 390 | .index li { 391 | padding-right: 5px; 392 | } 393 | 394 | .index ul { 395 | padding-left: 0; 396 | } 397 | 398 | .type-sig { 399 | clear: both; 400 | color: #088; 401 | } 402 | 403 | .type-sig pre { 404 | padding-top: 10px; 405 | margin: 0; 406 | } 407 | 408 | .usage code { 409 | display: block; 410 | color: #008; 411 | margin: 2px 0; 412 | } 413 | 414 | .usage code:first-child { 415 | padding-top: 10px; 416 | } 417 | 418 | p { 419 | margin: 15px 0; 420 | } 421 | 422 | .public p:first-child, .public pre.plaintext { 423 | margin-top: 12px; 424 | } 425 | 426 | .doc { 427 | margin: 0 0 26px 0; 428 | clear: both; 429 | } 430 | 431 | .public .doc { 432 | margin: 0; 433 | } 434 | 435 | .namespace-index .doc { 436 | margin-bottom: 20px; 437 | } 438 | 439 | .namespace-index .namespace .doc { 440 | margin-bottom: 10px; 441 | } 442 | 443 | .markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { 444 | line-height: 22px; 445 | } 446 | 447 | .markdown li { 448 | padding: 2px 0; 449 | } 450 | 451 | .markdown h2 { 452 | font-weight: normal; 453 | font-size: 25px; 454 | margin: 30px 0 10px 0; 455 | } 456 | 457 | .markdown h3 { 458 | font-weight: normal; 459 | font-size: 20px; 460 | margin: 30px 0 0 0; 461 | } 462 | 463 | .markdown h4 { 464 | font-size: 15px; 465 | margin: 22px 0 -4px 0; 466 | } 467 | 468 | .doc, .public, .namespace .index { 469 | max-width: 680px; 470 | overflow-x: visible; 471 | } 472 | 473 | .markdown pre > code { 474 | display: block; 475 | padding: 10px; 476 | } 477 | 478 | .markdown pre > code, .src-link a { 479 | border: 1px solid #e4e4e4; 480 | border-radius: 2px; 481 | } 482 | 483 | .markdown code:not(.hljs), .src-link a { 484 | background: #f6f6f6; 485 | } 486 | 487 | pre.deps { 488 | display: inline-block; 489 | margin: 0 10px; 490 | border: 1px solid #e4e4e4; 491 | border-radius: 2px; 492 | padding: 10px; 493 | background-color: #f6f6f6; 494 | } 495 | 496 | .markdown hr { 497 | border-style: solid; 498 | border-top: none; 499 | color: #ccc; 500 | } 501 | 502 | .doc ul, .doc ol { 503 | padding-left: 30px; 504 | } 505 | 506 | .doc table { 507 | border-collapse: collapse; 508 | margin: 0 10px; 509 | } 510 | 511 | .doc table td, .doc table th { 512 | border: 1px solid #dddddd; 513 | padding: 4px 6px; 514 | } 515 | 516 | .doc table th { 517 | background: #f2f2f2; 518 | } 519 | 520 | .doc dl { 521 | margin: 0 10px 20px 10px; 522 | } 523 | 524 | .doc dl dt { 525 | font-weight: bold; 526 | margin: 0; 527 | padding: 3px 0; 528 | border-bottom: 1px solid #ddd; 529 | } 530 | 531 | .doc dl dd { 532 | padding: 5px 0; 533 | margin: 0 0 5px 10px; 534 | } 535 | 536 | .doc abbr { 537 | border-bottom: 1px dotted #333; 538 | font-variant: none; 539 | cursor: help; 540 | } 541 | 542 | .src-link { 543 | margin-bottom: 15px; 544 | } 545 | 546 | .src-link a { 547 | font-size: 70%; 548 | padding: 1px 4px; 549 | text-decoration: none; 550 | color: #5555bb; 551 | } 552 | -------------------------------------------------------------------------------- /docs/api/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | github.com style (c) Vasily Polovnyov 3 | */ 4 | 5 | .hljs { 6 | display: block; 7 | overflow-x: auto; 8 | padding: 0.5em; 9 | color: #333; 10 | background: #f8f8f8; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #998; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-keyword, 20 | .hljs-selector-tag, 21 | .hljs-subst { 22 | color: #333; 23 | font-weight: bold; 24 | } 25 | 26 | .hljs-number, 27 | .hljs-literal, 28 | .hljs-variable, 29 | .hljs-template-variable, 30 | .hljs-tag .hljs-attr { 31 | color: #008080; 32 | } 33 | 34 | .hljs-string, 35 | .hljs-doctag { 36 | color: #d14; 37 | } 38 | 39 | .hljs-title, 40 | .hljs-section, 41 | .hljs-selector-id { 42 | color: #900; 43 | font-weight: bold; 44 | } 45 | 46 | .hljs-subst { 47 | font-weight: normal; 48 | } 49 | 50 | .hljs-type, 51 | .hljs-class .hljs-title { 52 | color: #458; 53 | font-weight: bold; 54 | } 55 | 56 | .hljs-tag, 57 | .hljs-name, 58 | .hljs-attribute { 59 | color: #000080; 60 | font-weight: normal; 61 | } 62 | 63 | .hljs-regexp, 64 | .hljs-link { 65 | color: #009926; 66 | } 67 | 68 | .hljs-symbol, 69 | .hljs-bullet { 70 | color: #990073; 71 | } 72 | 73 | .hljs-built_in, 74 | .hljs-builtin-name { 75 | color: #0086b3; 76 | } 77 | 78 | .hljs-meta { 79 | color: #999; 80 | font-weight: bold; 81 | } 82 | 83 | .hljs-deletion { 84 | background: #fdd; 85 | } 86 | 87 | .hljs-addition { 88 | background: #dfd; 89 | } 90 | 91 | .hljs-emphasis { 92 | font-style: italic; 93 | } 94 | 95 | .hljs-strong { 96 | font-weight: bold; 97 | } 98 | -------------------------------------------------------------------------------- /docs/api/js/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.6.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"
":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("clojure",function(e){var t={"builtin-name":"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"symbol",b:"[:]{1,2}"+n},f={b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"name",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"meta",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure"}}]}}); -------------------------------------------------------------------------------- /docs/api/js/page_effects.js: -------------------------------------------------------------------------------- 1 | function visibleInParent(element) { 2 | var position = $(element).position().top 3 | return position > -50 && position < ($(element).offsetParent().height() - 50) 4 | } 5 | 6 | function hasFragment(link, fragment) { 7 | return $(link).attr("href").indexOf("#" + fragment) != -1 8 | } 9 | 10 | function findLinkByFragment(elements, fragment) { 11 | return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first() 12 | } 13 | 14 | function scrollToCurrentVarLink(elements) { 15 | var elements = $(elements); 16 | var parent = elements.offsetParent(); 17 | 18 | if (elements.length == 0) return; 19 | 20 | var top = elements.first().position().top; 21 | var bottom = elements.last().position().top + elements.last().height(); 22 | 23 | if (top >= 0 && bottom <= parent.height()) return; 24 | 25 | if (top < 0) { 26 | parent.scrollTop(parent.scrollTop() + top); 27 | } 28 | else if (bottom > parent.height()) { 29 | parent.scrollTop(parent.scrollTop() + bottom - parent.height()); 30 | } 31 | } 32 | 33 | function setCurrentVarLink() { 34 | $('.secondary a').parent().removeClass('current') 35 | $('.anchor'). 36 | filter(function(index) { return visibleInParent(this) }). 37 | each(function(index, element) { 38 | findLinkByFragment(".secondary a", element.id). 39 | parent(). 40 | addClass('current') 41 | }); 42 | scrollToCurrentVarLink('.secondary .current'); 43 | } 44 | 45 | var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }()) 46 | 47 | function scrollPositionId(element) { 48 | var directory = window.location.href.replace(/[^\/]+\.html$/, '') 49 | return 'scroll::' + $(element).attr('id') + '::' + directory 50 | } 51 | 52 | function storeScrollPosition(element) { 53 | if (!hasStorage) return; 54 | localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft()) 55 | localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop()) 56 | } 57 | 58 | function recallScrollPosition(element) { 59 | if (!hasStorage) return; 60 | $(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x")) 61 | $(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y")) 62 | } 63 | 64 | function persistScrollPosition(element) { 65 | recallScrollPosition(element) 66 | $(element).scroll(function() { storeScrollPosition(element) }) 67 | } 68 | 69 | function sidebarContentWidth(element) { 70 | var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() }) 71 | return Math.max.apply(Math, widths) 72 | } 73 | 74 | function calculateSize(width, snap, margin, minimum) { 75 | if (width == 0) { 76 | return 0 77 | } 78 | else { 79 | return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2)) 80 | } 81 | } 82 | 83 | function resizeSidebars() { 84 | var primaryWidth = sidebarContentWidth('.primary') 85 | var secondaryWidth = 0 86 | 87 | if ($('.secondary').length != 0) { 88 | secondaryWidth = sidebarContentWidth('.secondary') 89 | } 90 | 91 | // snap to grid 92 | primaryWidth = calculateSize(primaryWidth, 32, 13, 160) 93 | secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160) 94 | 95 | $('.primary').css('width', primaryWidth) 96 | $('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1) 97 | 98 | if (secondaryWidth > 0) { 99 | $('#content').css('left', primaryWidth + secondaryWidth + 2) 100 | } 101 | else { 102 | $('#content').css('left', primaryWidth + 1) 103 | } 104 | } 105 | 106 | $(window).ready(resizeSidebars) 107 | $(window).ready(setCurrentVarLink) 108 | $(window).ready(function() { persistScrollPosition('.primary')}) 109 | $(window).ready(function() { 110 | $('#content').scroll(setCurrentVarLink) 111 | $(window).resize(setCurrentVarLink) 112 | }) 113 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject clj-chrome-devtools "20220405" 2 | :description "Clojure API for Chrome DevTools remote" 3 | :license {:name "MIT License"} 4 | :url "https://github.com/tatut/clj-chrome-devtools" 5 | :plugins [[lein-codox "0.10.8"] 6 | [lein-tools-deps "0.4.5"]] 7 | :codox {:output-path "docs/api" 8 | :metadata {:doc/format :markdown}} 9 | :middleware [lein-tools-deps.plugin/resolve-dependencies-with-deps-edn] 10 | :lein-tools-deps/config {:config-files [:install :user :project]}) 11 | -------------------------------------------------------------------------------- /resources/clj_chrome_devtools_runner.tpl: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools-runner 2 | (:require [cljs.test :refer [run-tests]] 3 | __REQUIRE_NAMESPACES__)) 4 | 5 | (def PRINTED (atom [])) 6 | 7 | (defn get-printed [] 8 | (let [v @PRINTED] 9 | (reset! PRINTED []) 10 | (clj->js v))) 11 | 12 | (def screenshot-number (atom {:name nil :number 0})) 13 | 14 | (defn screenshot-file-name [] 15 | (let [test-name (-> (cljs.test/get-current-env) :testing-vars first meta :name)] 16 | (str test-name 17 | "-" 18 | (:number (swap! screenshot-number 19 | (fn [{:keys [name number]}] 20 | {:name test-name 21 | :number (if (= name test-name) 22 | (inc number) 23 | 0)}))) 24 | ".png"))) 25 | 26 | (defn screenshot [& file-name] 27 | (let [file-name (or file-name (screenshot-file-name))] 28 | (js/Promise. 29 | (fn [resolve _] 30 | (aset js/window "CLJ_SCREENSHOT_NAME" file-name) 31 | (aset js/window "CLJ_SCREENSHOT_RESOLVE" resolve))))) 32 | 33 | (aset js/window "screenshot" screenshot) 34 | (aset js/window "CLJ_TEST_GET_PRINTED" get-printed) 35 | 36 | (defn run-chrome-tests [] 37 | (aset js/window "CLJ_TESTS_STARTED" true) 38 | (set! *print-fn* (fn [& msg] (swap! PRINTED conj (apply str msg)))) 39 | (run-tests __TEST_NAMESPACES__ )) 40 | -------------------------------------------------------------------------------- /resources/test-page.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/automation/fixture.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.automation.fixture 2 | "Provides a `clojure.test` fixture for starting a new Chrome headless instance 3 | and an automation context for it." 4 | (:require [clj-chrome-devtools.automation :as automation] 5 | [clj-chrome-devtools.automation.launcher :as launcher])) 6 | 7 | ;; Define previously defined public vars here for backwards compatibility 8 | (def possible-chrome-binaries launcher/possible-chrome-binaries) 9 | (def binary-path launcher/binary-path) 10 | (def find-chrome-binary launcher/find-chrome-binary) 11 | (def launch-chrome launcher/launch-chrome) 12 | (def default-options launcher/default-options) 13 | 14 | (defn create-chrome-fixture 15 | ([] (create-chrome-fixture {})) 16 | ([options] 17 | (fn [tests] 18 | (let [prev-current-automation @automation/current-automation] 19 | (try 20 | (with-open [^java.lang.AutoCloseable automation (launcher/launch-automation options)] 21 | (reset! automation/current-automation automation) 22 | (tests)) 23 | (finally 24 | (reset! automation/current-automation prev-current-automation))))))) 25 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/automation/launcher.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.automation.launcher 2 | "Launch a headless Chrome for automation purposes." 3 | (:require [clojure.java.shell :as sh] 4 | [clojure.string :as str] 5 | [clojure.tools.logging :as log] 6 | [clj-chrome-devtools.automation :as automation] 7 | [clj-chrome-devtools.impl.util :as util] 8 | [clj-chrome-devtools.impl.connection :as connection])) 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | (def possible-chrome-binaries 13 | ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" 14 | "google-chrome-stable" 15 | "google-chrome" 16 | "chromium-browser" 17 | "chromium" 18 | "chrome.exe"]) 19 | 20 | (defn- is-windows? [] 21 | (when-let [os (System/getenv "os")] 22 | (str/starts-with? os "Windows"))) 23 | 24 | (defn binary-path [candidate] 25 | (let [{:keys [exit out]} 26 | (if (is-windows?) 27 | ;; Note: usually this is the default location 28 | ;; "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe" 29 | ;; "C:/Program Files (x86)/Google/Chrome Beta/Application/chrome.exe" 30 | (sh/sh "where.exe" candidate) 31 | ;; Assume Linux/MacOS 32 | (sh/sh "which" candidate))] 33 | (when (= exit 0) 34 | (str/trim-newline out)))) 35 | 36 | (defn find-chrome-binary [] 37 | (some binary-path possible-chrome-binaries)) 38 | 39 | (defn launch-chrome [binary-path remote-debugging-port options] 40 | (log/trace "Launching Chrome headless, binary: " binary-path 41 | ", remote debugging port: " remote-debugging-port 42 | ", options: " (pr-str options)) 43 | (let [args (remove nil? (concat [binary-path 44 | (when (:headless? options) "--headless") 45 | (when (:no-sandbox? options) "--no-sandbox") 46 | (when-not (:enable-gpu? options) "--disable-gpu") 47 | (str "--remote-debugging-port=" remote-debugging-port)] 48 | (:args options)))] 49 | (.exec (Runtime/getRuntime) 50 | ^"[Ljava.lang.String;" (into-array String args)))) 51 | 52 | (defn default-options [] 53 | {:chrome-binary (find-chrome-binary) 54 | :remote-debugging-port nil 55 | :headless? true}) 56 | 57 | (defn launch-automation [options] 58 | (let [options (merge (default-options) options) 59 | {:keys [chrome-binary remote-debugging-port headless?]} options 60 | port (or remote-debugging-port (util/random-free-port)) 61 | ^java.lang.Process process (launch-chrome chrome-binary port options)] 62 | (automation/create-automation 63 | (connection/connect "localhost" port 30000) 64 | (fn [_] 65 | (.destroy process))))) 66 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/cljs/test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.cljs.test 2 | "Build a ClojureScript build and run its tests as a clojure 3 | test. This can hook your cljs app tests into the normal 4 | clojure testing run." 5 | (:require [cljs.build.api :as cljs-build] 6 | [clojure.java.io :as io] 7 | [clj-chrome-devtools.automation.fixture :refer [create-chrome-fixture]] 8 | [clj-chrome-devtools.automation :as automation] 9 | [clj-chrome-devtools.impl.util :refer [random-free-port]] 10 | [org.httpkit.server :as http-server] 11 | [clojure.string :as str] 12 | [clojure.test :refer [is]] 13 | [clojure.java.shell :as sh]) 14 | (:import (java.io File))) 15 | 16 | 17 | 18 | (defn no-op-log [& _args] 19 | nil) 20 | 21 | (defn println-log [& args] 22 | (println (str/join " " args))) 23 | 24 | (def ^:dynamic log no-op-log) 25 | 26 | (defn- find-defproject [file] 27 | (log "Loading leiningen project from: " file) 28 | (with-open [in (java.io.PushbackReader. (io/reader file))] 29 | (loop [form (read in false ::eof)] 30 | (cond 31 | (= ::eof form) 32 | (throw (ex-info "Can't find defproject form" {:file file})) 33 | 34 | 35 | (and (coll? form) (= (first form) 'defproject)) 36 | (do 37 | (log "Found defproject: " (nth form 1) (nth form 2)) 38 | form) 39 | 40 | :else 41 | (recur (read in)))))) 42 | 43 | (defn- load-project-clj 44 | "Load project.clj file and turn it into a map." 45 | [] 46 | (->> "project.clj" find-defproject 47 | (drop 3) ;; remove defproject, name and version 48 | (partition 2) ;; take top level :key val pairs 49 | (map vec) 50 | (into {}))) 51 | 52 | (defn- build-by-id [project-clj build-id] 53 | (->> project-clj :cljsbuild :builds 54 | (some #(when (= build-id (:id %)) 55 | %)))) 56 | 57 | (defn- test-runner-forms 58 | "ClojureScript forms for test runner" 59 | [namespaces] 60 | (log "Generating test runner forms for namespaces: " namespaces) 61 | (-> "clj_chrome_devtools_runner.tpl" 62 | io/resource slurp 63 | (str/replace "__REQUIRE_NAMESPACES__" 64 | (str/join "\n " namespaces)) 65 | (str/replace "__TEST_NAMESPACES__" 66 | (str/join "\n " 67 | (map #(str "'" %) namespaces))))) 68 | 69 | (defn- with-test-runner-source [namespaces source-path fun] 70 | ;; Create a test runner source file in the given source path 71 | ;; We have to put this in an existing source path as 72 | ;; we can't add a new source path dynamically (files therein 73 | ;; won't be found with io/resource). It is simpler to add 74 | ;; it to an existing source path and remove afterwards. 75 | (let [runner (io/file source-path 76 | "clj_chrome_devtools_runner.cljs")] 77 | (spit runner (test-runner-forms namespaces)) 78 | (try 79 | (fun) 80 | (finally 81 | (io/delete-file runner))))) 82 | 83 | (defn build [project-clj build-id test-runner-namespaces] 84 | (let [{:keys [source-paths compiler] :as _build} 85 | (build-by-id project-clj build-id)] 86 | (log "Building ClojureScript, source paths: " source-paths) 87 | (with-test-runner-source test-runner-namespaces (last source-paths) 88 | #(cljs-build/build 89 | (cljs-build/inputs source-paths) 90 | (assoc compiler 91 | :main "clj-chrome-devtools-runner" 92 | :warnings {:single-segment-namespace false}))) 93 | (assert (.exists (io/file (:output-to compiler))) 94 | "build output file exists") 95 | {:js (:output-to compiler) 96 | :js-directory (:output-dir compiler)})) 97 | 98 | (defn- test-page [{:keys [js runner] 99 | :or {runner "clj_chrome_devtools_runner.run_chrome_tests();"}}] 100 | (-> "test-page.tpl" io/resource slurp 101 | (str/replace "__RUNNER__" runner) 102 | (str/replace "__JS__" (slurp js)))) 103 | 104 | (defn- file-handler [root-paths {:keys [uri request-method] :as _req}] 105 | (log "REQUEST: " uri) 106 | (let [path (subs uri 1) 107 | file (some (fn [root-path] 108 | (let [f (io/file root-path path)] 109 | (when (.exists f) 110 | f))) 111 | root-paths)] 112 | (if (and (= request-method :get) (.canRead file)) 113 | {:status 200 114 | :headers {"Content-Type" (cond 115 | (str/ends-with? uri ".html") 116 | "text/html" 117 | 118 | (str/ends-with? uri ".js") 119 | "application/javascript" 120 | 121 | :else 122 | "application/octet-stream")} 123 | :body (slurp file)} 124 | 125 | {:status 404}))) 126 | 127 | (def ^{:doc "cljs.test failure/error report regex" 128 | :private true} 129 | final-test-report-pattern #"(\d+) failures, (\d+) errors.") 130 | 131 | (defn- test-result [msg] 132 | (let [[match errors failures] (re-matches final-test-report-pattern msg)] 133 | (when match 134 | (if (= "0" errors failures) 135 | :ok 136 | :fail)))) 137 | 138 | (defn- poll-test-execution [] 139 | (loop [started? false 140 | screenshots []] 141 | (if (not started?) 142 | ;; Tests have not started yet, check for fatal error or start flag 143 | (if-let [fatal-error (automation/evaluate "window['CLJ_FATAL_ERROR'] || null")] 144 | (throw (ex-info "Test page had error before tests were started." {:error fatal-error})) 145 | 146 | (if (automation/evaluate "window['CLJ_TESTS_STARTED'] || false") 147 | (recur true screenshots) 148 | (do 149 | (Thread/sleep 100) 150 | (recur false screenshots)))) 151 | 152 | ;; Tests have started, poll for screenshots and printed output 153 | (if-let [screenshot-file-name (automation/evaluate "window['CLJ_SCREENSHOT_NAME'] || null")] 154 | (do 155 | ;; Take screenshot if requested 156 | (log "Taking screenshot to:" screenshot-file-name) 157 | (automation/screenshot @automation/current-automation screenshot-file-name) 158 | (automation/evaluate "window.CLJ_SCREENSHOT_NAME = null") 159 | (automation/evaluate "window.CLJ_SCREENSHOT_RESOLVE(true)") 160 | (recur started? (conj screenshots screenshot-file-name))) 161 | (let [msgs (automation/evaluate "CLJ_TEST_GET_PRINTED()")] 162 | (doseq [m (mapcat #(str/split % #"\n") msgs)] 163 | (println "[CLJS]" m)) 164 | 165 | (if-let [result (some test-result msgs)] 166 | {:result result 167 | :screenshots screenshots} 168 | (do 169 | (Thread/sleep 100) 170 | (recur started? screenshots)))))))) 171 | 172 | (defn output-screenshot-videos [screenshots framerate loop-video?] 173 | (let [video-names (into #{} 174 | (keep #(second (re-matches #"^([^\d]+)-(\d+)\.png$" %))) 175 | screenshots)] 176 | (loop [[video-name & video-names] video-names] 177 | (when video-name 178 | (let [input (str video-name "-%d.png") 179 | output (str video-name ".png") 180 | cmd ["ffmpeg" 181 | "-framerate" (str framerate) 182 | "-y" "-i" input "-f" "apng" 183 | "-plays" (if loop-video? "0" "1") 184 | output]] 185 | (println "Generate video " output) 186 | (let [{exit :exit err :err} (apply sh/sh cmd)] 187 | (if (zero? exit) 188 | (recur video-names) 189 | (println "Failed to generate video with command: \n > " 190 | (str/join " " cmd) 191 | "\n Check ffmpeg installation, subprocess err: \n" 192 | err)))))))) 193 | 194 | (defn run-tests 195 | ([build-output] 196 | (run-tests build-output nil)) 197 | ([{:keys [js runner]} {:keys [headless? no-sandbox? 198 | screenshot-video? framerate loop-video? 199 | ring-handler on-test-result 200 | root-paths] 201 | :or {root-paths #{"."}}}] 202 | (log "Run compiled js test file:" js) 203 | (let [chrome-fixture (create-chrome-fixture {:headless? (if (some? headless?) 204 | headless? 205 | true) 206 | :no-sandbox? no-sandbox?})] 207 | (chrome-fixture 208 | (fn [] 209 | (log "Chrome launched") 210 | (let [port (random-free-port) 211 | file-handler (partial file-handler root-paths) 212 | server (http-server/run-server (fn [req] 213 | (or (and ring-handler (ring-handler req)) 214 | (file-handler req))) 215 | {:port port}) 216 | f (File/createTempFile "test" ".html" (io/file ".")) 217 | url (str "http://localhost:" port "/" (.getName f))] 218 | (try 219 | (spit f (test-page (merge 220 | {:js js} 221 | (when runner 222 | {:runner runner})))) 223 | 224 | (log "Navigate to:" url) 225 | (automation/to url) 226 | 227 | (log "Wait for test output") 228 | (let [{:keys [result screenshots] :as test-result} (poll-test-execution)] 229 | (when screenshot-video? 230 | (output-screenshot-videos screenshots (or framerate 2) loop-video?)) 231 | (if on-test-result 232 | (on-test-result test-result) 233 | (is (= result :ok) 234 | "ClojureScript tests had failures or errors, see previous output for details "))) 235 | 236 | (log "Tests done, cleanup") 237 | (server) 238 | (finally 239 | (io/delete-file f))))))))) 240 | 241 | (defn build-and-test 242 | ([build-id namespaces] 243 | (build-and-test build-id namespaces nil)) 244 | ([build-id namespaces options] 245 | (binding [log (if (:verbose? options) 246 | println-log 247 | no-op-log)] 248 | (let [project-clj (load-project-clj) 249 | build-output (build project-clj build-id namespaces)] 250 | (run-tests build-output options))))) 251 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/animation.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.animation 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::animation 8 | (s/keys 9 | :req-un 10 | [::id 11 | ::name 12 | ::paused-state 13 | ::play-state 14 | ::playback-rate 15 | ::start-time 16 | ::current-time 17 | ::type] 18 | :opt-un 19 | [::source 20 | ::css-id])) 21 | 22 | (s/def 23 | ::animation-effect 24 | (s/keys 25 | :req-un 26 | [::delay 27 | ::end-delay 28 | ::iteration-start 29 | ::iterations 30 | ::duration 31 | ::direction 32 | ::fill 33 | ::easing] 34 | :opt-un 35 | [::backend-node-id 36 | ::keyframes-rule])) 37 | 38 | (s/def 39 | ::keyframes-rule 40 | (s/keys 41 | :req-un 42 | [::keyframes] 43 | :opt-un 44 | [::name])) 45 | 46 | (s/def 47 | ::keyframe-style 48 | (s/keys 49 | :req-un 50 | [::offset 51 | ::easing])) 52 | (defn 53 | disable 54 | "Disables animation domain notifications." 55 | ([] 56 | (disable 57 | (c/get-current-connection) 58 | {})) 59 | ([{:as params, :keys []}] 60 | (disable 61 | (c/get-current-connection) 62 | params)) 63 | ([connection {:as params, :keys []}] 64 | (cmd/command 65 | connection 66 | "Animation" 67 | "disable" 68 | params 69 | {}))) 70 | 71 | (s/fdef 72 | disable 73 | :args 74 | (s/or 75 | :no-args 76 | (s/cat) 77 | :just-params 78 | (s/cat :params (s/keys)) 79 | :connection-and-params 80 | (s/cat 81 | :connection 82 | (s/? 83 | c/connection?) 84 | :params 85 | (s/keys))) 86 | :ret 87 | (s/keys)) 88 | 89 | (defn 90 | enable 91 | "Enables animation domain notifications." 92 | ([] 93 | (enable 94 | (c/get-current-connection) 95 | {})) 96 | ([{:as params, :keys []}] 97 | (enable 98 | (c/get-current-connection) 99 | params)) 100 | ([connection {:as params, :keys []}] 101 | (cmd/command 102 | connection 103 | "Animation" 104 | "enable" 105 | params 106 | {}))) 107 | 108 | (s/fdef 109 | enable 110 | :args 111 | (s/or 112 | :no-args 113 | (s/cat) 114 | :just-params 115 | (s/cat :params (s/keys)) 116 | :connection-and-params 117 | (s/cat 118 | :connection 119 | (s/? 120 | c/connection?) 121 | :params 122 | (s/keys))) 123 | :ret 124 | (s/keys)) 125 | 126 | (defn 127 | get-current-time 128 | "Returns the current time of the an animation.\n\nParameters map keys:\n\n\n Key | Description \n ----|------------ \n :id | Id of animation.\n\nReturn map keys:\n\n\n Key | Description \n --------------|------------ \n :current-time | Current time of the page." 129 | ([] 130 | (get-current-time 131 | (c/get-current-connection) 132 | {})) 133 | ([{:as params, :keys [id]}] 134 | (get-current-time 135 | (c/get-current-connection) 136 | params)) 137 | ([connection {:as params, :keys [id]}] 138 | (cmd/command 139 | connection 140 | "Animation" 141 | "getCurrentTime" 142 | params 143 | {:id "id"}))) 144 | 145 | (s/fdef 146 | get-current-time 147 | :args 148 | (s/or 149 | :no-args 150 | (s/cat) 151 | :just-params 152 | (s/cat 153 | :params 154 | (s/keys 155 | :req-un 156 | [::id])) 157 | :connection-and-params 158 | (s/cat 159 | :connection 160 | (s/? 161 | c/connection?) 162 | :params 163 | (s/keys 164 | :req-un 165 | [::id]))) 166 | :ret 167 | (s/keys 168 | :req-un 169 | [::current-time])) 170 | 171 | (defn 172 | get-playback-rate 173 | "Gets the playback rate of the document timeline.\n\nReturn map keys:\n\n\n Key | Description \n ---------------|------------ \n :playback-rate | Playback rate for animations on page." 174 | ([] 175 | (get-playback-rate 176 | (c/get-current-connection) 177 | {})) 178 | ([{:as params, :keys []}] 179 | (get-playback-rate 180 | (c/get-current-connection) 181 | params)) 182 | ([connection {:as params, :keys []}] 183 | (cmd/command 184 | connection 185 | "Animation" 186 | "getPlaybackRate" 187 | params 188 | {}))) 189 | 190 | (s/fdef 191 | get-playback-rate 192 | :args 193 | (s/or 194 | :no-args 195 | (s/cat) 196 | :just-params 197 | (s/cat :params (s/keys)) 198 | :connection-and-params 199 | (s/cat 200 | :connection 201 | (s/? 202 | c/connection?) 203 | :params 204 | (s/keys))) 205 | :ret 206 | (s/keys 207 | :req-un 208 | [::playback-rate])) 209 | 210 | (defn 211 | release-animations 212 | "Releases a set of animations to no longer be manipulated.\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :animations | List of animation ids to seek." 213 | ([] 214 | (release-animations 215 | (c/get-current-connection) 216 | {})) 217 | ([{:as params, :keys [animations]}] 218 | (release-animations 219 | (c/get-current-connection) 220 | params)) 221 | ([connection {:as params, :keys [animations]}] 222 | (cmd/command 223 | connection 224 | "Animation" 225 | "releaseAnimations" 226 | params 227 | {:animations "animations"}))) 228 | 229 | (s/fdef 230 | release-animations 231 | :args 232 | (s/or 233 | :no-args 234 | (s/cat) 235 | :just-params 236 | (s/cat 237 | :params 238 | (s/keys 239 | :req-un 240 | [::animations])) 241 | :connection-and-params 242 | (s/cat 243 | :connection 244 | (s/? 245 | c/connection?) 246 | :params 247 | (s/keys 248 | :req-un 249 | [::animations]))) 250 | :ret 251 | (s/keys)) 252 | 253 | (defn 254 | resolve-animation 255 | "Gets the remote object of the Animation.\n\nParameters map keys:\n\n\n Key | Description \n --------------|------------ \n :animation-id | Animation id.\n\nReturn map keys:\n\n\n Key | Description \n ---------------|------------ \n :remote-object | Corresponding remote object." 256 | ([] 257 | (resolve-animation 258 | (c/get-current-connection) 259 | {})) 260 | ([{:as params, :keys [animation-id]}] 261 | (resolve-animation 262 | (c/get-current-connection) 263 | params)) 264 | ([connection {:as params, :keys [animation-id]}] 265 | (cmd/command 266 | connection 267 | "Animation" 268 | "resolveAnimation" 269 | params 270 | {:animation-id "animationId"}))) 271 | 272 | (s/fdef 273 | resolve-animation 274 | :args 275 | (s/or 276 | :no-args 277 | (s/cat) 278 | :just-params 279 | (s/cat 280 | :params 281 | (s/keys 282 | :req-un 283 | [::animation-id])) 284 | :connection-and-params 285 | (s/cat 286 | :connection 287 | (s/? 288 | c/connection?) 289 | :params 290 | (s/keys 291 | :req-un 292 | [::animation-id]))) 293 | :ret 294 | (s/keys 295 | :req-un 296 | [::remote-object])) 297 | 298 | (defn 299 | seek-animations 300 | "Seek a set of animations to a particular time within each animation.\n\nParameters map keys:\n\n\n Key | Description \n --------------|------------ \n :animations | List of animation ids to seek.\n :current-time | Set the current time of each animation." 301 | ([] 302 | (seek-animations 303 | (c/get-current-connection) 304 | {})) 305 | ([{:as params, :keys [animations current-time]}] 306 | (seek-animations 307 | (c/get-current-connection) 308 | params)) 309 | ([connection {:as params, :keys [animations current-time]}] 310 | (cmd/command 311 | connection 312 | "Animation" 313 | "seekAnimations" 314 | params 315 | {:animations "animations", :current-time "currentTime"}))) 316 | 317 | (s/fdef 318 | seek-animations 319 | :args 320 | (s/or 321 | :no-args 322 | (s/cat) 323 | :just-params 324 | (s/cat 325 | :params 326 | (s/keys 327 | :req-un 328 | [::animations 329 | ::current-time])) 330 | :connection-and-params 331 | (s/cat 332 | :connection 333 | (s/? 334 | c/connection?) 335 | :params 336 | (s/keys 337 | :req-un 338 | [::animations 339 | ::current-time]))) 340 | :ret 341 | (s/keys)) 342 | 343 | (defn 344 | set-paused 345 | "Sets the paused state of a set of animations.\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :animations | Animations to set the pause state of.\n :paused | Paused state to set to." 346 | ([] 347 | (set-paused 348 | (c/get-current-connection) 349 | {})) 350 | ([{:as params, :keys [animations paused]}] 351 | (set-paused 352 | (c/get-current-connection) 353 | params)) 354 | ([connection {:as params, :keys [animations paused]}] 355 | (cmd/command 356 | connection 357 | "Animation" 358 | "setPaused" 359 | params 360 | {:animations "animations", :paused "paused"}))) 361 | 362 | (s/fdef 363 | set-paused 364 | :args 365 | (s/or 366 | :no-args 367 | (s/cat) 368 | :just-params 369 | (s/cat 370 | :params 371 | (s/keys 372 | :req-un 373 | [::animations 374 | ::paused])) 375 | :connection-and-params 376 | (s/cat 377 | :connection 378 | (s/? 379 | c/connection?) 380 | :params 381 | (s/keys 382 | :req-un 383 | [::animations 384 | ::paused]))) 385 | :ret 386 | (s/keys)) 387 | 388 | (defn 389 | set-playback-rate 390 | "Sets the playback rate of the document timeline.\n\nParameters map keys:\n\n\n Key | Description \n ---------------|------------ \n :playback-rate | Playback rate for animations on page" 391 | ([] 392 | (set-playback-rate 393 | (c/get-current-connection) 394 | {})) 395 | ([{:as params, :keys [playback-rate]}] 396 | (set-playback-rate 397 | (c/get-current-connection) 398 | params)) 399 | ([connection {:as params, :keys [playback-rate]}] 400 | (cmd/command 401 | connection 402 | "Animation" 403 | "setPlaybackRate" 404 | params 405 | {:playback-rate "playbackRate"}))) 406 | 407 | (s/fdef 408 | set-playback-rate 409 | :args 410 | (s/or 411 | :no-args 412 | (s/cat) 413 | :just-params 414 | (s/cat 415 | :params 416 | (s/keys 417 | :req-un 418 | [::playback-rate])) 419 | :connection-and-params 420 | (s/cat 421 | :connection 422 | (s/? 423 | c/connection?) 424 | :params 425 | (s/keys 426 | :req-un 427 | [::playback-rate]))) 428 | :ret 429 | (s/keys)) 430 | 431 | (defn 432 | set-timing 433 | "Sets the timing of an animation node.\n\nParameters map keys:\n\n\n Key | Description \n --------------|------------ \n :animation-id | Animation id.\n :duration | Duration of the animation.\n :delay | Delay of the animation." 434 | ([] 435 | (set-timing 436 | (c/get-current-connection) 437 | {})) 438 | ([{:as params, :keys [animation-id duration delay]}] 439 | (set-timing 440 | (c/get-current-connection) 441 | params)) 442 | ([connection {:as params, :keys [animation-id duration delay]}] 443 | (cmd/command 444 | connection 445 | "Animation" 446 | "setTiming" 447 | params 448 | {:animation-id "animationId", 449 | :duration "duration", 450 | :delay "delay"}))) 451 | 452 | (s/fdef 453 | set-timing 454 | :args 455 | (s/or 456 | :no-args 457 | (s/cat) 458 | :just-params 459 | (s/cat 460 | :params 461 | (s/keys 462 | :req-un 463 | [::animation-id 464 | ::duration 465 | ::delay])) 466 | :connection-and-params 467 | (s/cat 468 | :connection 469 | (s/? 470 | c/connection?) 471 | :params 472 | (s/keys 473 | :req-un 474 | [::animation-id 475 | ::duration 476 | ::delay]))) 477 | :ret 478 | (s/keys)) 479 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/application_cache.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.application-cache 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::application-cache-resource 8 | (s/keys 9 | :req-un 10 | [::url 11 | ::size 12 | ::type])) 13 | 14 | (s/def 15 | ::application-cache 16 | (s/keys 17 | :req-un 18 | [::manifest-url 19 | ::size 20 | ::creation-time 21 | ::update-time 22 | ::resources])) 23 | 24 | (s/def 25 | ::frame-with-manifest 26 | (s/keys 27 | :req-un 28 | [::frame-id 29 | ::manifest-url 30 | ::status])) 31 | (defn 32 | enable 33 | "Enables application cache domain notifications." 34 | ([] 35 | (enable 36 | (c/get-current-connection) 37 | {})) 38 | ([{:as params, :keys []}] 39 | (enable 40 | (c/get-current-connection) 41 | params)) 42 | ([connection {:as params, :keys []}] 43 | (cmd/command 44 | connection 45 | "ApplicationCache" 46 | "enable" 47 | params 48 | {}))) 49 | 50 | (s/fdef 51 | enable 52 | :args 53 | (s/or 54 | :no-args 55 | (s/cat) 56 | :just-params 57 | (s/cat :params (s/keys)) 58 | :connection-and-params 59 | (s/cat 60 | :connection 61 | (s/? 62 | c/connection?) 63 | :params 64 | (s/keys))) 65 | :ret 66 | (s/keys)) 67 | 68 | (defn 69 | get-application-cache-for-frame 70 | "Returns relevant application cache data for the document in given frame.\n\nParameters map keys:\n\n\n Key | Description \n ----------|------------ \n :frame-id | Identifier of the frame containing document whose application cache is retrieved.\n\nReturn map keys:\n\n\n Key | Description \n -------------------|------------ \n :application-cache | Relevant application cache data for the document in given frame." 71 | ([] 72 | (get-application-cache-for-frame 73 | (c/get-current-connection) 74 | {})) 75 | ([{:as params, :keys [frame-id]}] 76 | (get-application-cache-for-frame 77 | (c/get-current-connection) 78 | params)) 79 | ([connection {:as params, :keys [frame-id]}] 80 | (cmd/command 81 | connection 82 | "ApplicationCache" 83 | "getApplicationCacheForFrame" 84 | params 85 | {:frame-id "frameId"}))) 86 | 87 | (s/fdef 88 | get-application-cache-for-frame 89 | :args 90 | (s/or 91 | :no-args 92 | (s/cat) 93 | :just-params 94 | (s/cat 95 | :params 96 | (s/keys 97 | :req-un 98 | [::frame-id])) 99 | :connection-and-params 100 | (s/cat 101 | :connection 102 | (s/? 103 | c/connection?) 104 | :params 105 | (s/keys 106 | :req-un 107 | [::frame-id]))) 108 | :ret 109 | (s/keys 110 | :req-un 111 | [::application-cache])) 112 | 113 | (defn 114 | get-frames-with-manifests 115 | "Returns array of frame identifiers with manifest urls for each frame containing a document\nassociated with some application cache.\n\nReturn map keys:\n\n\n Key | Description \n -----------|------------ \n :frame-ids | Array of frame identifiers with manifest urls for each frame containing a document\nassociated with some application cache." 116 | ([] 117 | (get-frames-with-manifests 118 | (c/get-current-connection) 119 | {})) 120 | ([{:as params, :keys []}] 121 | (get-frames-with-manifests 122 | (c/get-current-connection) 123 | params)) 124 | ([connection {:as params, :keys []}] 125 | (cmd/command 126 | connection 127 | "ApplicationCache" 128 | "getFramesWithManifests" 129 | params 130 | {}))) 131 | 132 | (s/fdef 133 | get-frames-with-manifests 134 | :args 135 | (s/or 136 | :no-args 137 | (s/cat) 138 | :just-params 139 | (s/cat :params (s/keys)) 140 | :connection-and-params 141 | (s/cat 142 | :connection 143 | (s/? 144 | c/connection?) 145 | :params 146 | (s/keys))) 147 | :ret 148 | (s/keys 149 | :req-un 150 | [::frame-ids])) 151 | 152 | (defn 153 | get-manifest-for-frame 154 | "Returns manifest URL for document in the given frame.\n\nParameters map keys:\n\n\n Key | Description \n ----------|------------ \n :frame-id | Identifier of the frame containing document whose manifest is retrieved.\n\nReturn map keys:\n\n\n Key | Description \n --------------|------------ \n :manifest-url | Manifest URL for document in the given frame." 155 | ([] 156 | (get-manifest-for-frame 157 | (c/get-current-connection) 158 | {})) 159 | ([{:as params, :keys [frame-id]}] 160 | (get-manifest-for-frame 161 | (c/get-current-connection) 162 | params)) 163 | ([connection {:as params, :keys [frame-id]}] 164 | (cmd/command 165 | connection 166 | "ApplicationCache" 167 | "getManifestForFrame" 168 | params 169 | {:frame-id "frameId"}))) 170 | 171 | (s/fdef 172 | get-manifest-for-frame 173 | :args 174 | (s/or 175 | :no-args 176 | (s/cat) 177 | :just-params 178 | (s/cat 179 | :params 180 | (s/keys 181 | :req-un 182 | [::frame-id])) 183 | :connection-and-params 184 | (s/cat 185 | :connection 186 | (s/? 187 | c/connection?) 188 | :params 189 | (s/keys 190 | :req-un 191 | [::frame-id]))) 192 | :ret 193 | (s/keys 194 | :req-un 195 | [::manifest-url])) 196 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/background_service.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.background-service 2 | "Defines events for background web platform features." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::service-name 9 | #{"pushMessaging" "paymentHandler" "notifications" "backgroundSync" 10 | "periodicBackgroundSync" "backgroundFetch"}) 11 | 12 | (s/def 13 | ::event-metadata 14 | (s/keys 15 | :req-un 16 | [::key 17 | ::value])) 18 | 19 | (s/def 20 | ::background-service-event 21 | (s/keys 22 | :req-un 23 | [::timestamp 24 | ::origin 25 | ::service-worker-registration-id 26 | ::service 27 | ::event-name 28 | ::instance-id 29 | ::event-metadata])) 30 | (defn 31 | start-observing 32 | "Enables event updates for the service.\n\nParameters map keys:\n\n\n Key | Description \n ---------|------------ \n :service | null" 33 | ([] 34 | (start-observing 35 | (c/get-current-connection) 36 | {})) 37 | ([{:as params, :keys [service]}] 38 | (start-observing 39 | (c/get-current-connection) 40 | params)) 41 | ([connection {:as params, :keys [service]}] 42 | (cmd/command 43 | connection 44 | "BackgroundService" 45 | "startObserving" 46 | params 47 | {:service "service"}))) 48 | 49 | (s/fdef 50 | start-observing 51 | :args 52 | (s/or 53 | :no-args 54 | (s/cat) 55 | :just-params 56 | (s/cat 57 | :params 58 | (s/keys 59 | :req-un 60 | [::service])) 61 | :connection-and-params 62 | (s/cat 63 | :connection 64 | (s/? 65 | c/connection?) 66 | :params 67 | (s/keys 68 | :req-un 69 | [::service]))) 70 | :ret 71 | (s/keys)) 72 | 73 | (defn 74 | stop-observing 75 | "Disables event updates for the service.\n\nParameters map keys:\n\n\n Key | Description \n ---------|------------ \n :service | null" 76 | ([] 77 | (stop-observing 78 | (c/get-current-connection) 79 | {})) 80 | ([{:as params, :keys [service]}] 81 | (stop-observing 82 | (c/get-current-connection) 83 | params)) 84 | ([connection {:as params, :keys [service]}] 85 | (cmd/command 86 | connection 87 | "BackgroundService" 88 | "stopObserving" 89 | params 90 | {:service "service"}))) 91 | 92 | (s/fdef 93 | stop-observing 94 | :args 95 | (s/or 96 | :no-args 97 | (s/cat) 98 | :just-params 99 | (s/cat 100 | :params 101 | (s/keys 102 | :req-un 103 | [::service])) 104 | :connection-and-params 105 | (s/cat 106 | :connection 107 | (s/? 108 | c/connection?) 109 | :params 110 | (s/keys 111 | :req-un 112 | [::service]))) 113 | :ret 114 | (s/keys)) 115 | 116 | (defn 117 | set-recording 118 | "Set the recording state for the service.\n\nParameters map keys:\n\n\n Key | Description \n ---------------|------------ \n :should-record | null\n :service | null" 119 | ([] 120 | (set-recording 121 | (c/get-current-connection) 122 | {})) 123 | ([{:as params, :keys [should-record service]}] 124 | (set-recording 125 | (c/get-current-connection) 126 | params)) 127 | ([connection {:as params, :keys [should-record service]}] 128 | (cmd/command 129 | connection 130 | "BackgroundService" 131 | "setRecording" 132 | params 133 | {:should-record "shouldRecord", :service "service"}))) 134 | 135 | (s/fdef 136 | set-recording 137 | :args 138 | (s/or 139 | :no-args 140 | (s/cat) 141 | :just-params 142 | (s/cat 143 | :params 144 | (s/keys 145 | :req-un 146 | [::should-record 147 | ::service])) 148 | :connection-and-params 149 | (s/cat 150 | :connection 151 | (s/? 152 | c/connection?) 153 | :params 154 | (s/keys 155 | :req-un 156 | [::should-record 157 | ::service]))) 158 | :ret 159 | (s/keys)) 160 | 161 | (defn 162 | clear-events 163 | "Clears all stored data for the service.\n\nParameters map keys:\n\n\n Key | Description \n ---------|------------ \n :service | null" 164 | ([] 165 | (clear-events 166 | (c/get-current-connection) 167 | {})) 168 | ([{:as params, :keys [service]}] 169 | (clear-events 170 | (c/get-current-connection) 171 | params)) 172 | ([connection {:as params, :keys [service]}] 173 | (cmd/command 174 | connection 175 | "BackgroundService" 176 | "clearEvents" 177 | params 178 | {:service "service"}))) 179 | 180 | (s/fdef 181 | clear-events 182 | :args 183 | (s/or 184 | :no-args 185 | (s/cat) 186 | :just-params 187 | (s/cat 188 | :params 189 | (s/keys 190 | :req-un 191 | [::service])) 192 | :connection-and-params 193 | (s/cat 194 | :connection 195 | (s/? 196 | c/connection?) 197 | :params 198 | (s/keys 199 | :req-un 200 | [::service]))) 201 | :ret 202 | (s/keys)) 203 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/cache_storage.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.cache-storage 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::cache-id 8 | string?) 9 | 10 | (s/def 11 | ::cached-response-type 12 | #{"opaqueResponse" "error" "cors" "default" "opaqueRedirect" "basic"}) 13 | 14 | (s/def 15 | ::data-entry 16 | (s/keys 17 | :req-un 18 | [::request-url 19 | ::request-method 20 | ::request-headers 21 | ::response-time 22 | ::response-status 23 | ::response-status-text 24 | ::response-type 25 | ::response-headers])) 26 | 27 | (s/def 28 | ::cache 29 | (s/keys 30 | :req-un 31 | [::cache-id 32 | ::security-origin 33 | ::cache-name])) 34 | 35 | (s/def 36 | ::header 37 | (s/keys 38 | :req-un 39 | [::name 40 | ::value])) 41 | 42 | (s/def 43 | ::cached-response 44 | (s/keys 45 | :req-un 46 | [::body])) 47 | (defn 48 | delete-cache 49 | "Deletes a cache.\n\nParameters map keys:\n\n\n Key | Description \n ----------|------------ \n :cache-id | Id of cache for deletion." 50 | ([] 51 | (delete-cache 52 | (c/get-current-connection) 53 | {})) 54 | ([{:as params, :keys [cache-id]}] 55 | (delete-cache 56 | (c/get-current-connection) 57 | params)) 58 | ([connection {:as params, :keys [cache-id]}] 59 | (cmd/command 60 | connection 61 | "CacheStorage" 62 | "deleteCache" 63 | params 64 | {:cache-id "cacheId"}))) 65 | 66 | (s/fdef 67 | delete-cache 68 | :args 69 | (s/or 70 | :no-args 71 | (s/cat) 72 | :just-params 73 | (s/cat 74 | :params 75 | (s/keys 76 | :req-un 77 | [::cache-id])) 78 | :connection-and-params 79 | (s/cat 80 | :connection 81 | (s/? 82 | c/connection?) 83 | :params 84 | (s/keys 85 | :req-un 86 | [::cache-id]))) 87 | :ret 88 | (s/keys)) 89 | 90 | (defn 91 | delete-entry 92 | "Deletes a cache entry.\n\nParameters map keys:\n\n\n Key | Description \n ----------|------------ \n :cache-id | Id of cache where the entry will be deleted.\n :request | URL spec of the request." 93 | ([] 94 | (delete-entry 95 | (c/get-current-connection) 96 | {})) 97 | ([{:as params, :keys [cache-id request]}] 98 | (delete-entry 99 | (c/get-current-connection) 100 | params)) 101 | ([connection {:as params, :keys [cache-id request]}] 102 | (cmd/command 103 | connection 104 | "CacheStorage" 105 | "deleteEntry" 106 | params 107 | {:cache-id "cacheId", :request "request"}))) 108 | 109 | (s/fdef 110 | delete-entry 111 | :args 112 | (s/or 113 | :no-args 114 | (s/cat) 115 | :just-params 116 | (s/cat 117 | :params 118 | (s/keys 119 | :req-un 120 | [::cache-id 121 | ::request])) 122 | :connection-and-params 123 | (s/cat 124 | :connection 125 | (s/? 126 | c/connection?) 127 | :params 128 | (s/keys 129 | :req-un 130 | [::cache-id 131 | ::request]))) 132 | :ret 133 | (s/keys)) 134 | 135 | (defn 136 | request-cache-names 137 | "Requests cache names.\n\nParameters map keys:\n\n\n Key | Description \n -----------------|------------ \n :security-origin | Security origin.\n\nReturn map keys:\n\n\n Key | Description \n --------|------------ \n :caches | Caches for the security origin." 138 | ([] 139 | (request-cache-names 140 | (c/get-current-connection) 141 | {})) 142 | ([{:as params, :keys [security-origin]}] 143 | (request-cache-names 144 | (c/get-current-connection) 145 | params)) 146 | ([connection {:as params, :keys [security-origin]}] 147 | (cmd/command 148 | connection 149 | "CacheStorage" 150 | "requestCacheNames" 151 | params 152 | {:security-origin "securityOrigin"}))) 153 | 154 | (s/fdef 155 | request-cache-names 156 | :args 157 | (s/or 158 | :no-args 159 | (s/cat) 160 | :just-params 161 | (s/cat 162 | :params 163 | (s/keys 164 | :req-un 165 | [::security-origin])) 166 | :connection-and-params 167 | (s/cat 168 | :connection 169 | (s/? 170 | c/connection?) 171 | :params 172 | (s/keys 173 | :req-un 174 | [::security-origin]))) 175 | :ret 176 | (s/keys 177 | :req-un 178 | [::caches])) 179 | 180 | (defn 181 | request-cached-response 182 | "Fetches cache entry.\n\nParameters map keys:\n\n\n Key | Description \n -----------------|------------ \n :cache-id | Id of cache that contains the entry.\n :request-url | URL spec of the request.\n :request-headers | headers of the request.\n\nReturn map keys:\n\n\n Key | Description \n ----------|------------ \n :response | Response read from the cache." 183 | ([] 184 | (request-cached-response 185 | (c/get-current-connection) 186 | {})) 187 | ([{:as params, :keys [cache-id request-url request-headers]}] 188 | (request-cached-response 189 | (c/get-current-connection) 190 | params)) 191 | ([connection 192 | {:as params, :keys [cache-id request-url request-headers]}] 193 | (cmd/command 194 | connection 195 | "CacheStorage" 196 | "requestCachedResponse" 197 | params 198 | {:cache-id "cacheId", 199 | :request-url "requestURL", 200 | :request-headers "requestHeaders"}))) 201 | 202 | (s/fdef 203 | request-cached-response 204 | :args 205 | (s/or 206 | :no-args 207 | (s/cat) 208 | :just-params 209 | (s/cat 210 | :params 211 | (s/keys 212 | :req-un 213 | [::cache-id 214 | ::request-url 215 | ::request-headers])) 216 | :connection-and-params 217 | (s/cat 218 | :connection 219 | (s/? 220 | c/connection?) 221 | :params 222 | (s/keys 223 | :req-un 224 | [::cache-id 225 | ::request-url 226 | ::request-headers]))) 227 | :ret 228 | (s/keys 229 | :req-un 230 | [::response])) 231 | 232 | (defn 233 | request-entries 234 | "Requests data from cache.\n\nParameters map keys:\n\n\n Key | Description \n -------------|------------ \n :cache-id | ID of cache to get entries from.\n :skip-count | Number of records to skip. (optional)\n :page-size | Number of records to fetch. (optional)\n :path-filter | If present, only return the entries containing this substring in the path (optional)\n\nReturn map keys:\n\n\n Key | Description \n --------------------|------------ \n :cache-data-entries | Array of object store data entries.\n :return-count | Count of returned entries from this storage. If pathFilter is empty, it\nis the count of all entries from this storage." 235 | ([] 236 | (request-entries 237 | (c/get-current-connection) 238 | {})) 239 | ([{:as params, :keys [cache-id skip-count page-size path-filter]}] 240 | (request-entries 241 | (c/get-current-connection) 242 | params)) 243 | ([connection 244 | {:as params, :keys [cache-id skip-count page-size path-filter]}] 245 | (cmd/command 246 | connection 247 | "CacheStorage" 248 | "requestEntries" 249 | params 250 | {:cache-id "cacheId", 251 | :skip-count "skipCount", 252 | :page-size "pageSize", 253 | :path-filter "pathFilter"}))) 254 | 255 | (s/fdef 256 | request-entries 257 | :args 258 | (s/or 259 | :no-args 260 | (s/cat) 261 | :just-params 262 | (s/cat 263 | :params 264 | (s/keys 265 | :req-un 266 | [::cache-id] 267 | :opt-un 268 | [::skip-count 269 | ::page-size 270 | ::path-filter])) 271 | :connection-and-params 272 | (s/cat 273 | :connection 274 | (s/? 275 | c/connection?) 276 | :params 277 | (s/keys 278 | :req-un 279 | [::cache-id] 280 | :opt-un 281 | [::skip-count 282 | ::page-size 283 | ::path-filter]))) 284 | :ret 285 | (s/keys 286 | :req-un 287 | [::cache-data-entries 288 | ::return-count])) 289 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/cast.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.cast 2 | "A domain for interacting with Cast, Presentation API, and Remote Playback API\nfunctionalities." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::sink 9 | (s/keys 10 | :req-un 11 | [::name 12 | ::id] 13 | :opt-un 14 | [::session])) 15 | (defn 16 | enable 17 | "Starts observing for sinks that can be used for tab mirroring, and if set,\nsinks compatible with |presentationUrl| as well. When sinks are found, a\n|sinksUpdated| event is fired.\nAlso starts observing for issue messages. When an issue is added or removed,\nan |issueUpdated| event is fired.\n\nParameters map keys:\n\n\n Key | Description \n ------------------|------------ \n :presentation-url | null (optional)" 18 | ([] 19 | (enable 20 | (c/get-current-connection) 21 | {})) 22 | ([{:as params, :keys [presentation-url]}] 23 | (enable 24 | (c/get-current-connection) 25 | params)) 26 | ([connection {:as params, :keys [presentation-url]}] 27 | (cmd/command 28 | connection 29 | "Cast" 30 | "enable" 31 | params 32 | {:presentation-url "presentationUrl"}))) 33 | 34 | (s/fdef 35 | enable 36 | :args 37 | (s/or 38 | :no-args 39 | (s/cat) 40 | :just-params 41 | (s/cat 42 | :params 43 | (s/keys 44 | :opt-un 45 | [::presentation-url])) 46 | :connection-and-params 47 | (s/cat 48 | :connection 49 | (s/? 50 | c/connection?) 51 | :params 52 | (s/keys 53 | :opt-un 54 | [::presentation-url]))) 55 | :ret 56 | (s/keys)) 57 | 58 | (defn 59 | disable 60 | "Stops observing for sinks and issues." 61 | ([] 62 | (disable 63 | (c/get-current-connection) 64 | {})) 65 | ([{:as params, :keys []}] 66 | (disable 67 | (c/get-current-connection) 68 | params)) 69 | ([connection {:as params, :keys []}] 70 | (cmd/command 71 | connection 72 | "Cast" 73 | "disable" 74 | params 75 | {}))) 76 | 77 | (s/fdef 78 | disable 79 | :args 80 | (s/or 81 | :no-args 82 | (s/cat) 83 | :just-params 84 | (s/cat :params (s/keys)) 85 | :connection-and-params 86 | (s/cat 87 | :connection 88 | (s/? 89 | c/connection?) 90 | :params 91 | (s/keys))) 92 | :ret 93 | (s/keys)) 94 | 95 | (defn 96 | set-sink-to-use 97 | "Sets a sink to be used when the web page requests the browser to choose a\nsink via Presentation API, Remote Playback API, or Cast SDK.\n\nParameters map keys:\n\n\n Key | Description \n -----------|------------ \n :sink-name | null" 98 | ([] 99 | (set-sink-to-use 100 | (c/get-current-connection) 101 | {})) 102 | ([{:as params, :keys [sink-name]}] 103 | (set-sink-to-use 104 | (c/get-current-connection) 105 | params)) 106 | ([connection {:as params, :keys [sink-name]}] 107 | (cmd/command 108 | connection 109 | "Cast" 110 | "setSinkToUse" 111 | params 112 | {:sink-name "sinkName"}))) 113 | 114 | (s/fdef 115 | set-sink-to-use 116 | :args 117 | (s/or 118 | :no-args 119 | (s/cat) 120 | :just-params 121 | (s/cat 122 | :params 123 | (s/keys 124 | :req-un 125 | [::sink-name])) 126 | :connection-and-params 127 | (s/cat 128 | :connection 129 | (s/? 130 | c/connection?) 131 | :params 132 | (s/keys 133 | :req-un 134 | [::sink-name]))) 135 | :ret 136 | (s/keys)) 137 | 138 | (defn 139 | start-desktop-mirroring 140 | "Starts mirroring the desktop to the sink.\n\nParameters map keys:\n\n\n Key | Description \n -----------|------------ \n :sink-name | null" 141 | ([] 142 | (start-desktop-mirroring 143 | (c/get-current-connection) 144 | {})) 145 | ([{:as params, :keys [sink-name]}] 146 | (start-desktop-mirroring 147 | (c/get-current-connection) 148 | params)) 149 | ([connection {:as params, :keys [sink-name]}] 150 | (cmd/command 151 | connection 152 | "Cast" 153 | "startDesktopMirroring" 154 | params 155 | {:sink-name "sinkName"}))) 156 | 157 | (s/fdef 158 | start-desktop-mirroring 159 | :args 160 | (s/or 161 | :no-args 162 | (s/cat) 163 | :just-params 164 | (s/cat 165 | :params 166 | (s/keys 167 | :req-un 168 | [::sink-name])) 169 | :connection-and-params 170 | (s/cat 171 | :connection 172 | (s/? 173 | c/connection?) 174 | :params 175 | (s/keys 176 | :req-un 177 | [::sink-name]))) 178 | :ret 179 | (s/keys)) 180 | 181 | (defn 182 | start-tab-mirroring 183 | "Starts mirroring the tab to the sink.\n\nParameters map keys:\n\n\n Key | Description \n -----------|------------ \n :sink-name | null" 184 | ([] 185 | (start-tab-mirroring 186 | (c/get-current-connection) 187 | {})) 188 | ([{:as params, :keys [sink-name]}] 189 | (start-tab-mirroring 190 | (c/get-current-connection) 191 | params)) 192 | ([connection {:as params, :keys [sink-name]}] 193 | (cmd/command 194 | connection 195 | "Cast" 196 | "startTabMirroring" 197 | params 198 | {:sink-name "sinkName"}))) 199 | 200 | (s/fdef 201 | start-tab-mirroring 202 | :args 203 | (s/or 204 | :no-args 205 | (s/cat) 206 | :just-params 207 | (s/cat 208 | :params 209 | (s/keys 210 | :req-un 211 | [::sink-name])) 212 | :connection-and-params 213 | (s/cat 214 | :connection 215 | (s/? 216 | c/connection?) 217 | :params 218 | (s/keys 219 | :req-un 220 | [::sink-name]))) 221 | :ret 222 | (s/keys)) 223 | 224 | (defn 225 | stop-casting 226 | "Stops the active Cast session on the sink.\n\nParameters map keys:\n\n\n Key | Description \n -----------|------------ \n :sink-name | null" 227 | ([] 228 | (stop-casting 229 | (c/get-current-connection) 230 | {})) 231 | ([{:as params, :keys [sink-name]}] 232 | (stop-casting 233 | (c/get-current-connection) 234 | params)) 235 | ([connection {:as params, :keys [sink-name]}] 236 | (cmd/command 237 | connection 238 | "Cast" 239 | "stopCasting" 240 | params 241 | {:sink-name "sinkName"}))) 242 | 243 | (s/fdef 244 | stop-casting 245 | :args 246 | (s/or 247 | :no-args 248 | (s/cat) 249 | :just-params 250 | (s/cat 251 | :params 252 | (s/keys 253 | :req-un 254 | [::sink-name])) 255 | :connection-and-params 256 | (s/cat 257 | :connection 258 | (s/? 259 | c/connection?) 260 | :params 261 | (s/keys 262 | :req-un 263 | [::sink-name]))) 264 | :ret 265 | (s/keys)) 266 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/console.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.console 2 | "This domain is deprecated - use Runtime or Log instead." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::console-message 9 | (s/keys 10 | :req-un 11 | [::source 12 | ::level 13 | ::text] 14 | :opt-un 15 | [::url 16 | ::line 17 | ::column])) 18 | (defn 19 | clear-messages 20 | "Does nothing." 21 | ([] 22 | (clear-messages 23 | (c/get-current-connection) 24 | {})) 25 | ([{:as params, :keys []}] 26 | (clear-messages 27 | (c/get-current-connection) 28 | params)) 29 | ([connection {:as params, :keys []}] 30 | (cmd/command 31 | connection 32 | "Console" 33 | "clearMessages" 34 | params 35 | {}))) 36 | 37 | (s/fdef 38 | clear-messages 39 | :args 40 | (s/or 41 | :no-args 42 | (s/cat) 43 | :just-params 44 | (s/cat :params (s/keys)) 45 | :connection-and-params 46 | (s/cat 47 | :connection 48 | (s/? 49 | c/connection?) 50 | :params 51 | (s/keys))) 52 | :ret 53 | (s/keys)) 54 | 55 | (defn 56 | disable 57 | "Disables console domain, prevents further console messages from being reported to the client." 58 | ([] 59 | (disable 60 | (c/get-current-connection) 61 | {})) 62 | ([{:as params, :keys []}] 63 | (disable 64 | (c/get-current-connection) 65 | params)) 66 | ([connection {:as params, :keys []}] 67 | (cmd/command 68 | connection 69 | "Console" 70 | "disable" 71 | params 72 | {}))) 73 | 74 | (s/fdef 75 | disable 76 | :args 77 | (s/or 78 | :no-args 79 | (s/cat) 80 | :just-params 81 | (s/cat :params (s/keys)) 82 | :connection-and-params 83 | (s/cat 84 | :connection 85 | (s/? 86 | c/connection?) 87 | :params 88 | (s/keys))) 89 | :ret 90 | (s/keys)) 91 | 92 | (defn 93 | enable 94 | "Enables console domain, sends the messages collected so far to the client by means of the\n`messageAdded` notification." 95 | ([] 96 | (enable 97 | (c/get-current-connection) 98 | {})) 99 | ([{:as params, :keys []}] 100 | (enable 101 | (c/get-current-connection) 102 | params)) 103 | ([connection {:as params, :keys []}] 104 | (cmd/command 105 | connection 106 | "Console" 107 | "enable" 108 | params 109 | {}))) 110 | 111 | (s/fdef 112 | enable 113 | :args 114 | (s/or 115 | :no-args 116 | (s/cat) 117 | :just-params 118 | (s/cat :params (s/keys)) 119 | :connection-and-params 120 | (s/cat 121 | :connection 122 | (s/? 123 | c/connection?) 124 | :params 125 | (s/keys))) 126 | :ret 127 | (s/keys)) 128 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/database.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.database 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::database-id 8 | string?) 9 | 10 | (s/def 11 | ::database 12 | (s/keys 13 | :req-un 14 | [::id 15 | ::domain 16 | ::name 17 | ::version])) 18 | 19 | (s/def 20 | ::error 21 | (s/keys 22 | :req-un 23 | [::message 24 | ::code])) 25 | (defn 26 | disable 27 | "Disables database tracking, prevents database events from being sent to the client." 28 | ([] 29 | (disable 30 | (c/get-current-connection) 31 | {})) 32 | ([{:as params, :keys []}] 33 | (disable 34 | (c/get-current-connection) 35 | params)) 36 | ([connection {:as params, :keys []}] 37 | (cmd/command 38 | connection 39 | "Database" 40 | "disable" 41 | params 42 | {}))) 43 | 44 | (s/fdef 45 | disable 46 | :args 47 | (s/or 48 | :no-args 49 | (s/cat) 50 | :just-params 51 | (s/cat :params (s/keys)) 52 | :connection-and-params 53 | (s/cat 54 | :connection 55 | (s/? 56 | c/connection?) 57 | :params 58 | (s/keys))) 59 | :ret 60 | (s/keys)) 61 | 62 | (defn 63 | enable 64 | "Enables database tracking, database events will now be delivered to the client." 65 | ([] 66 | (enable 67 | (c/get-current-connection) 68 | {})) 69 | ([{:as params, :keys []}] 70 | (enable 71 | (c/get-current-connection) 72 | params)) 73 | ([connection {:as params, :keys []}] 74 | (cmd/command 75 | connection 76 | "Database" 77 | "enable" 78 | params 79 | {}))) 80 | 81 | (s/fdef 82 | enable 83 | :args 84 | (s/or 85 | :no-args 86 | (s/cat) 87 | :just-params 88 | (s/cat :params (s/keys)) 89 | :connection-and-params 90 | (s/cat 91 | :connection 92 | (s/? 93 | c/connection?) 94 | :params 95 | (s/keys))) 96 | :ret 97 | (s/keys)) 98 | 99 | (defn 100 | execute-sql 101 | "\n\nParameters map keys:\n\n\n Key | Description \n -------------|------------ \n :database-id | null\n :query | null\n\nReturn map keys:\n\n\n Key | Description \n --------------|------------ \n :column-names | null (optional)\n :values | null (optional)\n :sql-error | null (optional)" 102 | ([] 103 | (execute-sql 104 | (c/get-current-connection) 105 | {})) 106 | ([{:as params, :keys [database-id query]}] 107 | (execute-sql 108 | (c/get-current-connection) 109 | params)) 110 | ([connection {:as params, :keys [database-id query]}] 111 | (cmd/command 112 | connection 113 | "Database" 114 | "executeSQL" 115 | params 116 | {:database-id "databaseId", :query "query"}))) 117 | 118 | (s/fdef 119 | execute-sql 120 | :args 121 | (s/or 122 | :no-args 123 | (s/cat) 124 | :just-params 125 | (s/cat 126 | :params 127 | (s/keys 128 | :req-un 129 | [::database-id 130 | ::query])) 131 | :connection-and-params 132 | (s/cat 133 | :connection 134 | (s/? 135 | c/connection?) 136 | :params 137 | (s/keys 138 | :req-un 139 | [::database-id 140 | ::query]))) 141 | :ret 142 | (s/keys 143 | :opt-un 144 | [::column-names 145 | ::values 146 | ::sql-error])) 147 | 148 | (defn 149 | get-database-table-names 150 | "\n\nParameters map keys:\n\n\n Key | Description \n -------------|------------ \n :database-id | null\n\nReturn map keys:\n\n\n Key | Description \n -------------|------------ \n :table-names | null" 151 | ([] 152 | (get-database-table-names 153 | (c/get-current-connection) 154 | {})) 155 | ([{:as params, :keys [database-id]}] 156 | (get-database-table-names 157 | (c/get-current-connection) 158 | params)) 159 | ([connection {:as params, :keys [database-id]}] 160 | (cmd/command 161 | connection 162 | "Database" 163 | "getDatabaseTableNames" 164 | params 165 | {:database-id "databaseId"}))) 166 | 167 | (s/fdef 168 | get-database-table-names 169 | :args 170 | (s/or 171 | :no-args 172 | (s/cat) 173 | :just-params 174 | (s/cat 175 | :params 176 | (s/keys 177 | :req-un 178 | [::database-id])) 179 | :connection-and-params 180 | (s/cat 181 | :connection 182 | (s/? 183 | c/connection?) 184 | :params 185 | (s/keys 186 | :req-un 187 | [::database-id]))) 188 | :ret 189 | (s/keys 190 | :req-un 191 | [::table-names])) 192 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/device_orientation.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.device-orientation 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (defn 7 | clear-device-orientation-override 8 | "Clears the overridden Device Orientation." 9 | ([] 10 | (clear-device-orientation-override 11 | (c/get-current-connection) 12 | {})) 13 | ([{:as params, :keys []}] 14 | (clear-device-orientation-override 15 | (c/get-current-connection) 16 | params)) 17 | ([connection {:as params, :keys []}] 18 | (cmd/command 19 | connection 20 | "DeviceOrientation" 21 | "clearDeviceOrientationOverride" 22 | params 23 | {}))) 24 | 25 | (s/fdef 26 | clear-device-orientation-override 27 | :args 28 | (s/or 29 | :no-args 30 | (s/cat) 31 | :just-params 32 | (s/cat :params (s/keys)) 33 | :connection-and-params 34 | (s/cat 35 | :connection 36 | (s/? 37 | c/connection?) 38 | :params 39 | (s/keys))) 40 | :ret 41 | (s/keys)) 42 | 43 | (defn 44 | set-device-orientation-override 45 | "Overrides the Device Orientation.\n\nParameters map keys:\n\n\n Key | Description \n -------|------------ \n :alpha | Mock alpha\n :beta | Mock beta\n :gamma | Mock gamma" 46 | ([] 47 | (set-device-orientation-override 48 | (c/get-current-connection) 49 | {})) 50 | ([{:as params, :keys [alpha beta gamma]}] 51 | (set-device-orientation-override 52 | (c/get-current-connection) 53 | params)) 54 | ([connection {:as params, :keys [alpha beta gamma]}] 55 | (cmd/command 56 | connection 57 | "DeviceOrientation" 58 | "setDeviceOrientationOverride" 59 | params 60 | {:alpha "alpha", :beta "beta", :gamma "gamma"}))) 61 | 62 | (s/fdef 63 | set-device-orientation-override 64 | :args 65 | (s/or 66 | :no-args 67 | (s/cat) 68 | :just-params 69 | (s/cat 70 | :params 71 | (s/keys 72 | :req-un 73 | [::alpha 74 | ::beta 75 | ::gamma])) 76 | :connection-and-params 77 | (s/cat 78 | :connection 79 | (s/? 80 | c/connection?) 81 | :params 82 | (s/keys 83 | :req-un 84 | [::alpha 85 | ::beta 86 | ::gamma]))) 87 | :ret 88 | (s/keys)) 89 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/dom_snapshot.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.dom-snapshot 2 | "This domain facilitates obtaining document snapshots with DOM, layout, and style information." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::dom-node 9 | (s/keys 10 | :req-un 11 | [::node-type 12 | ::node-name 13 | ::node-value 14 | ::backend-node-id] 15 | :opt-un 16 | [::text-value 17 | ::input-value 18 | ::input-checked 19 | ::option-selected 20 | ::child-node-indexes 21 | ::attributes 22 | ::pseudo-element-indexes 23 | ::layout-node-index 24 | ::document-url 25 | ::base-url 26 | ::content-language 27 | ::document-encoding 28 | ::public-id 29 | ::system-id 30 | ::frame-id 31 | ::content-document-index 32 | ::pseudo-type 33 | ::shadow-root-type 34 | ::is-clickable 35 | ::event-listeners 36 | ::current-source-url 37 | ::origin-url 38 | ::scroll-offset-x 39 | ::scroll-offset-y])) 40 | 41 | (s/def 42 | ::inline-text-box 43 | (s/keys 44 | :req-un 45 | [::bounding-box 46 | ::start-character-index 47 | ::num-characters])) 48 | 49 | (s/def 50 | ::layout-tree-node 51 | (s/keys 52 | :req-un 53 | [::dom-node-index 54 | ::bounding-box] 55 | :opt-un 56 | [::layout-text 57 | ::inline-text-nodes 58 | ::style-index 59 | ::paint-order 60 | ::is-stacking-context])) 61 | 62 | (s/def 63 | ::computed-style 64 | (s/keys 65 | :req-un 66 | [::properties])) 67 | 68 | (s/def 69 | ::name-value 70 | (s/keys 71 | :req-un 72 | [::name 73 | ::value])) 74 | 75 | (s/def 76 | ::string-index 77 | integer?) 78 | 79 | (s/def 80 | ::array-of-strings 81 | (s/coll-of any?)) 82 | 83 | (s/def 84 | ::rare-string-data 85 | (s/keys 86 | :req-un 87 | [::index 88 | ::value])) 89 | 90 | (s/def 91 | ::rare-boolean-data 92 | (s/keys 93 | :req-un 94 | [::index])) 95 | 96 | (s/def 97 | ::rare-integer-data 98 | (s/keys 99 | :req-un 100 | [::index 101 | ::value])) 102 | 103 | (s/def 104 | ::rectangle 105 | (s/coll-of number?)) 106 | 107 | (s/def 108 | ::document-snapshot 109 | (s/keys 110 | :req-un 111 | [::document-url 112 | ::title 113 | ::base-url 114 | ::content-language 115 | ::encoding-name 116 | ::public-id 117 | ::system-id 118 | ::frame-id 119 | ::nodes 120 | ::layout 121 | ::text-boxes] 122 | :opt-un 123 | [::scroll-offset-x 124 | ::scroll-offset-y 125 | ::content-width 126 | ::content-height])) 127 | 128 | (s/def 129 | ::node-tree-snapshot 130 | (s/keys 131 | :opt-un 132 | [::parent-index 133 | ::node-type 134 | ::shadow-root-type 135 | ::node-name 136 | ::node-value 137 | ::backend-node-id 138 | ::attributes 139 | ::text-value 140 | ::input-value 141 | ::input-checked 142 | ::option-selected 143 | ::content-document-index 144 | ::pseudo-type 145 | ::is-clickable 146 | ::current-source-url 147 | ::origin-url])) 148 | 149 | (s/def 150 | ::layout-tree-snapshot 151 | (s/keys 152 | :req-un 153 | [::node-index 154 | ::styles 155 | ::bounds 156 | ::text 157 | ::stacking-contexts] 158 | :opt-un 159 | [::paint-orders 160 | ::offset-rects 161 | ::scroll-rects 162 | ::client-rects 163 | ::blended-background-colors 164 | ::text-color-opacities])) 165 | 166 | (s/def 167 | ::text-box-snapshot 168 | (s/keys 169 | :req-un 170 | [::layout-index 171 | ::bounds 172 | ::start 173 | ::length])) 174 | (defn 175 | disable 176 | "Disables DOM snapshot agent for the given page." 177 | ([] 178 | (disable 179 | (c/get-current-connection) 180 | {})) 181 | ([{:as params, :keys []}] 182 | (disable 183 | (c/get-current-connection) 184 | params)) 185 | ([connection {:as params, :keys []}] 186 | (cmd/command 187 | connection 188 | "DOMSnapshot" 189 | "disable" 190 | params 191 | {}))) 192 | 193 | (s/fdef 194 | disable 195 | :args 196 | (s/or 197 | :no-args 198 | (s/cat) 199 | :just-params 200 | (s/cat :params (s/keys)) 201 | :connection-and-params 202 | (s/cat 203 | :connection 204 | (s/? 205 | c/connection?) 206 | :params 207 | (s/keys))) 208 | :ret 209 | (s/keys)) 210 | 211 | (defn 212 | enable 213 | "Enables DOM snapshot agent for the given page." 214 | ([] 215 | (enable 216 | (c/get-current-connection) 217 | {})) 218 | ([{:as params, :keys []}] 219 | (enable 220 | (c/get-current-connection) 221 | params)) 222 | ([connection {:as params, :keys []}] 223 | (cmd/command 224 | connection 225 | "DOMSnapshot" 226 | "enable" 227 | params 228 | {}))) 229 | 230 | (s/fdef 231 | enable 232 | :args 233 | (s/or 234 | :no-args 235 | (s/cat) 236 | :just-params 237 | (s/cat :params (s/keys)) 238 | :connection-and-params 239 | (s/cat 240 | :connection 241 | (s/? 242 | c/connection?) 243 | :params 244 | (s/keys))) 245 | :ret 246 | (s/keys)) 247 | 248 | (defn 249 | get-snapshot 250 | "Returns a document snapshot, including the full DOM tree of the root node (including iframes,\ntemplate contents, and imported documents) in a flattened array, as well as layout and\nwhite-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is\nflattened.\n\nParameters map keys:\n\n\n Key | Description \n --------------------------------|------------ \n :computed-style-whitelist | Whitelist of computed styles to return.\n :include-event-listeners | Whether or not to retrieve details of DOM listeners (default false). (optional)\n :include-paint-order | Whether to determine and include the paint order index of LayoutTreeNodes (default false). (optional)\n :include-user-agent-shadow-tree | Whether to include UA shadow tree in the snapshot (default false). (optional)\n\nReturn map keys:\n\n\n Key | Description \n -------------------|------------ \n :dom-nodes | The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document.\n :layout-tree-nodes | The nodes in the layout tree.\n :computed-styles | Whitelisted ComputedStyle properties for each node in the layout tree." 251 | ([] 252 | (get-snapshot 253 | (c/get-current-connection) 254 | {})) 255 | ([{:as params, 256 | :keys 257 | [computed-style-whitelist 258 | include-event-listeners 259 | include-paint-order 260 | include-user-agent-shadow-tree]}] 261 | (get-snapshot 262 | (c/get-current-connection) 263 | params)) 264 | ([connection 265 | {:as params, 266 | :keys 267 | [computed-style-whitelist 268 | include-event-listeners 269 | include-paint-order 270 | include-user-agent-shadow-tree]}] 271 | (cmd/command 272 | connection 273 | "DOMSnapshot" 274 | "getSnapshot" 275 | params 276 | {:computed-style-whitelist "computedStyleWhitelist", 277 | :include-event-listeners "includeEventListeners", 278 | :include-paint-order "includePaintOrder", 279 | :include-user-agent-shadow-tree "includeUserAgentShadowTree"}))) 280 | 281 | (s/fdef 282 | get-snapshot 283 | :args 284 | (s/or 285 | :no-args 286 | (s/cat) 287 | :just-params 288 | (s/cat 289 | :params 290 | (s/keys 291 | :req-un 292 | [::computed-style-whitelist] 293 | :opt-un 294 | [::include-event-listeners 295 | ::include-paint-order 296 | ::include-user-agent-shadow-tree])) 297 | :connection-and-params 298 | (s/cat 299 | :connection 300 | (s/? 301 | c/connection?) 302 | :params 303 | (s/keys 304 | :req-un 305 | [::computed-style-whitelist] 306 | :opt-un 307 | [::include-event-listeners 308 | ::include-paint-order 309 | ::include-user-agent-shadow-tree]))) 310 | :ret 311 | (s/keys 312 | :req-un 313 | [::dom-nodes 314 | ::layout-tree-nodes 315 | ::computed-styles])) 316 | 317 | (defn 318 | capture-snapshot 319 | "Returns a document snapshot, including the full DOM tree of the root node (including iframes,\ntemplate contents, and imported documents) in a flattened array, as well as layout and\nwhite-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is\nflattened.\n\nParameters map keys:\n\n\n Key | Description \n -----------------------------------|------------ \n :computed-styles | Whitelist of computed styles to return.\n :include-paint-order | Whether to include layout object paint orders into the snapshot. (optional)\n :include-dom-rects | Whether to include DOM rectangles (offsetRects, clientRects, scrollRects) into the snapshot (optional)\n :include-blended-background-colors | Whether to include blended background colors in the snapshot (default: false).\nBlended background color is achieved by blending background colors of all elements\nthat overlap with the current element. (optional)\n :include-text-color-opacities | Whether to include text color opacity in the snapshot (default: false).\nAn element might have the opacity property set that affects the text color of the element.\nThe final text color opacity is computed based on the opacity of all overlapping elements. (optional)\n\nReturn map keys:\n\n\n Key | Description \n -----------|------------ \n :documents | The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document.\n :strings | Shared string table that all string properties refer to with indexes." 320 | ([] 321 | (capture-snapshot 322 | (c/get-current-connection) 323 | {})) 324 | ([{:as params, 325 | :keys 326 | [computed-styles 327 | include-paint-order 328 | include-dom-rects 329 | include-blended-background-colors 330 | include-text-color-opacities]}] 331 | (capture-snapshot 332 | (c/get-current-connection) 333 | params)) 334 | ([connection 335 | {:as params, 336 | :keys 337 | [computed-styles 338 | include-paint-order 339 | include-dom-rects 340 | include-blended-background-colors 341 | include-text-color-opacities]}] 342 | (cmd/command 343 | connection 344 | "DOMSnapshot" 345 | "captureSnapshot" 346 | params 347 | {:computed-styles "computedStyles", 348 | :include-paint-order "includePaintOrder", 349 | :include-dom-rects "includeDOMRects", 350 | :include-blended-background-colors 351 | "includeBlendedBackgroundColors", 352 | :include-text-color-opacities "includeTextColorOpacities"}))) 353 | 354 | (s/fdef 355 | capture-snapshot 356 | :args 357 | (s/or 358 | :no-args 359 | (s/cat) 360 | :just-params 361 | (s/cat 362 | :params 363 | (s/keys 364 | :req-un 365 | [::computed-styles] 366 | :opt-un 367 | [::include-paint-order 368 | ::include-dom-rects 369 | ::include-blended-background-colors 370 | ::include-text-color-opacities])) 371 | :connection-and-params 372 | (s/cat 373 | :connection 374 | (s/? 375 | c/connection?) 376 | :params 377 | (s/keys 378 | :req-un 379 | [::computed-styles] 380 | :opt-un 381 | [::include-paint-order 382 | ::include-dom-rects 383 | ::include-blended-background-colors 384 | ::include-text-color-opacities]))) 385 | :ret 386 | (s/keys 387 | :req-un 388 | [::documents 389 | ::strings])) 390 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/dom_storage.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.dom-storage 2 | "Query and modify DOM storage." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::storage-id 9 | (s/keys 10 | :req-un 11 | [::security-origin 12 | ::is-local-storage])) 13 | 14 | (s/def 15 | ::item 16 | (s/coll-of string?)) 17 | (defn 18 | clear 19 | "\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :storage-id | null" 20 | ([] 21 | (clear 22 | (c/get-current-connection) 23 | {})) 24 | ([{:as params, :keys [storage-id]}] 25 | (clear 26 | (c/get-current-connection) 27 | params)) 28 | ([connection {:as params, :keys [storage-id]}] 29 | (cmd/command 30 | connection 31 | "DOMStorage" 32 | "clear" 33 | params 34 | {:storage-id "storageId"}))) 35 | 36 | (s/fdef 37 | clear 38 | :args 39 | (s/or 40 | :no-args 41 | (s/cat) 42 | :just-params 43 | (s/cat 44 | :params 45 | (s/keys 46 | :req-un 47 | [::storage-id])) 48 | :connection-and-params 49 | (s/cat 50 | :connection 51 | (s/? 52 | c/connection?) 53 | :params 54 | (s/keys 55 | :req-un 56 | [::storage-id]))) 57 | :ret 58 | (s/keys)) 59 | 60 | (defn 61 | disable 62 | "Disables storage tracking, prevents storage events from being sent to the client." 63 | ([] 64 | (disable 65 | (c/get-current-connection) 66 | {})) 67 | ([{:as params, :keys []}] 68 | (disable 69 | (c/get-current-connection) 70 | params)) 71 | ([connection {:as params, :keys []}] 72 | (cmd/command 73 | connection 74 | "DOMStorage" 75 | "disable" 76 | params 77 | {}))) 78 | 79 | (s/fdef 80 | disable 81 | :args 82 | (s/or 83 | :no-args 84 | (s/cat) 85 | :just-params 86 | (s/cat :params (s/keys)) 87 | :connection-and-params 88 | (s/cat 89 | :connection 90 | (s/? 91 | c/connection?) 92 | :params 93 | (s/keys))) 94 | :ret 95 | (s/keys)) 96 | 97 | (defn 98 | enable 99 | "Enables storage tracking, storage events will now be delivered to the client." 100 | ([] 101 | (enable 102 | (c/get-current-connection) 103 | {})) 104 | ([{:as params, :keys []}] 105 | (enable 106 | (c/get-current-connection) 107 | params)) 108 | ([connection {:as params, :keys []}] 109 | (cmd/command 110 | connection 111 | "DOMStorage" 112 | "enable" 113 | params 114 | {}))) 115 | 116 | (s/fdef 117 | enable 118 | :args 119 | (s/or 120 | :no-args 121 | (s/cat) 122 | :just-params 123 | (s/cat :params (s/keys)) 124 | :connection-and-params 125 | (s/cat 126 | :connection 127 | (s/? 128 | c/connection?) 129 | :params 130 | (s/keys))) 131 | :ret 132 | (s/keys)) 133 | 134 | (defn 135 | get-dom-storage-items 136 | "\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :storage-id | null\n\nReturn map keys:\n\n\n Key | Description \n ---------|------------ \n :entries | null" 137 | ([] 138 | (get-dom-storage-items 139 | (c/get-current-connection) 140 | {})) 141 | ([{:as params, :keys [storage-id]}] 142 | (get-dom-storage-items 143 | (c/get-current-connection) 144 | params)) 145 | ([connection {:as params, :keys [storage-id]}] 146 | (cmd/command 147 | connection 148 | "DOMStorage" 149 | "getDOMStorageItems" 150 | params 151 | {:storage-id "storageId"}))) 152 | 153 | (s/fdef 154 | get-dom-storage-items 155 | :args 156 | (s/or 157 | :no-args 158 | (s/cat) 159 | :just-params 160 | (s/cat 161 | :params 162 | (s/keys 163 | :req-un 164 | [::storage-id])) 165 | :connection-and-params 166 | (s/cat 167 | :connection 168 | (s/? 169 | c/connection?) 170 | :params 171 | (s/keys 172 | :req-un 173 | [::storage-id]))) 174 | :ret 175 | (s/keys 176 | :req-un 177 | [::entries])) 178 | 179 | (defn 180 | remove-dom-storage-item 181 | "\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :storage-id | null\n :key | null" 182 | ([] 183 | (remove-dom-storage-item 184 | (c/get-current-connection) 185 | {})) 186 | ([{:as params, :keys [storage-id key]}] 187 | (remove-dom-storage-item 188 | (c/get-current-connection) 189 | params)) 190 | ([connection {:as params, :keys [storage-id key]}] 191 | (cmd/command 192 | connection 193 | "DOMStorage" 194 | "removeDOMStorageItem" 195 | params 196 | {:storage-id "storageId", :key "key"}))) 197 | 198 | (s/fdef 199 | remove-dom-storage-item 200 | :args 201 | (s/or 202 | :no-args 203 | (s/cat) 204 | :just-params 205 | (s/cat 206 | :params 207 | (s/keys 208 | :req-un 209 | [::storage-id 210 | ::key])) 211 | :connection-and-params 212 | (s/cat 213 | :connection 214 | (s/? 215 | c/connection?) 216 | :params 217 | (s/keys 218 | :req-un 219 | [::storage-id 220 | ::key]))) 221 | :ret 222 | (s/keys)) 223 | 224 | (defn 225 | set-dom-storage-item 226 | "\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :storage-id | null\n :key | null\n :value | null" 227 | ([] 228 | (set-dom-storage-item 229 | (c/get-current-connection) 230 | {})) 231 | ([{:as params, :keys [storage-id key value]}] 232 | (set-dom-storage-item 233 | (c/get-current-connection) 234 | params)) 235 | ([connection {:as params, :keys [storage-id key value]}] 236 | (cmd/command 237 | connection 238 | "DOMStorage" 239 | "setDOMStorageItem" 240 | params 241 | {:storage-id "storageId", :key "key", :value "value"}))) 242 | 243 | (s/fdef 244 | set-dom-storage-item 245 | :args 246 | (s/or 247 | :no-args 248 | (s/cat) 249 | :just-params 250 | (s/cat 251 | :params 252 | (s/keys 253 | :req-un 254 | [::storage-id 255 | ::key 256 | ::value])) 257 | :connection-and-params 258 | (s/cat 259 | :connection 260 | (s/? 261 | c/connection?) 262 | :params 263 | (s/keys 264 | :req-un 265 | [::storage-id 266 | ::key 267 | ::value]))) 268 | :ret 269 | (s/keys)) 270 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/event_breakpoints.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.event-breakpoints 2 | "EventBreakpoints permits setting breakpoints on particular operations and\nevents in targets that run JavaScript but do not have a DOM.\nJavaScript execution will stop on these operations as if there was a regular\nbreakpoint set." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (defn 8 | set-instrumentation-breakpoint 9 | "Sets breakpoint on particular native event.\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :event-name | Instrumentation name to stop on." 10 | ([] 11 | (set-instrumentation-breakpoint 12 | (c/get-current-connection) 13 | {})) 14 | ([{:as params, :keys [event-name]}] 15 | (set-instrumentation-breakpoint 16 | (c/get-current-connection) 17 | params)) 18 | ([connection {:as params, :keys [event-name]}] 19 | (cmd/command 20 | connection 21 | "EventBreakpoints" 22 | "setInstrumentationBreakpoint" 23 | params 24 | {:event-name "eventName"}))) 25 | 26 | (s/fdef 27 | set-instrumentation-breakpoint 28 | :args 29 | (s/or 30 | :no-args 31 | (s/cat) 32 | :just-params 33 | (s/cat 34 | :params 35 | (s/keys 36 | :req-un 37 | [::event-name])) 38 | :connection-and-params 39 | (s/cat 40 | :connection 41 | (s/? 42 | c/connection?) 43 | :params 44 | (s/keys 45 | :req-un 46 | [::event-name]))) 47 | :ret 48 | (s/keys)) 49 | 50 | (defn 51 | remove-instrumentation-breakpoint 52 | "Removes breakpoint on particular native event.\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :event-name | Instrumentation name to stop on." 53 | ([] 54 | (remove-instrumentation-breakpoint 55 | (c/get-current-connection) 56 | {})) 57 | ([{:as params, :keys [event-name]}] 58 | (remove-instrumentation-breakpoint 59 | (c/get-current-connection) 60 | params)) 61 | ([connection {:as params, :keys [event-name]}] 62 | (cmd/command 63 | connection 64 | "EventBreakpoints" 65 | "removeInstrumentationBreakpoint" 66 | params 67 | {:event-name "eventName"}))) 68 | 69 | (s/fdef 70 | remove-instrumentation-breakpoint 71 | :args 72 | (s/or 73 | :no-args 74 | (s/cat) 75 | :just-params 76 | (s/cat 77 | :params 78 | (s/keys 79 | :req-un 80 | [::event-name])) 81 | :connection-and-params 82 | (s/cat 83 | :connection 84 | (s/? 85 | c/connection?) 86 | :params 87 | (s/keys 88 | :req-un 89 | [::event-name]))) 90 | :ret 91 | (s/keys)) 92 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/headless_experimental.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.headless-experimental 2 | "This domain provides experimental commands only supported in headless mode." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::screenshot-params 9 | (s/keys 10 | :opt-un 11 | [::format 12 | ::quality])) 13 | (defn 14 | begin-frame 15 | "Sends a BeginFrame to the target and returns when the frame was completed. Optionally captures a\nscreenshot from the resulting frame. Requires that the target was created with enabled\nBeginFrameControl. Designed for use with --run-all-compositor-stages-before-draw, see also\nhttps://goo.gl/3zHXhB for more background.\n\nParameters map keys:\n\n\n Key | Description \n --------------------|------------ \n :frame-time-ticks | Timestamp of this BeginFrame in Renderer TimeTicks (milliseconds of uptime). If not set,\nthe current time will be used. (optional)\n :interval | The interval between BeginFrames that is reported to the compositor, in milliseconds.\nDefaults to a 60 frames/second interval, i.e. about 16.666 milliseconds. (optional)\n :no-display-updates | Whether updates should not be committed and drawn onto the display. False by default. If\ntrue, only side effects of the BeginFrame will be run, such as layout and animations, but\nany visual updates may not be visible on the display or in screenshots. (optional)\n :screenshot | If set, a screenshot of the frame will be captured and returned in the response. Otherwise,\nno screenshot will be captured. Note that capturing a screenshot can fail, for example,\nduring renderer initialization. In such a case, no screenshot data will be returned. (optional)\n\nReturn map keys:\n\n\n Key | Description \n -----------------|------------ \n :has-damage | Whether the BeginFrame resulted in damage and, thus, a new frame was committed to the\ndisplay. Reported for diagnostic uses, may be removed in the future.\n :screenshot-data | Base64-encoded image data of the screenshot, if one was requested and successfully taken. (Encoded as a base64 string when passed over JSON) (optional)" 16 | ([] 17 | (begin-frame 18 | (c/get-current-connection) 19 | {})) 20 | ([{:as params, 21 | :keys [frame-time-ticks interval no-display-updates screenshot]}] 22 | (begin-frame 23 | (c/get-current-connection) 24 | params)) 25 | ([connection 26 | {:as params, 27 | :keys [frame-time-ticks interval no-display-updates screenshot]}] 28 | (cmd/command 29 | connection 30 | "HeadlessExperimental" 31 | "beginFrame" 32 | params 33 | {:frame-time-ticks "frameTimeTicks", 34 | :interval "interval", 35 | :no-display-updates "noDisplayUpdates", 36 | :screenshot "screenshot"}))) 37 | 38 | (s/fdef 39 | begin-frame 40 | :args 41 | (s/or 42 | :no-args 43 | (s/cat) 44 | :just-params 45 | (s/cat 46 | :params 47 | (s/keys 48 | :opt-un 49 | [::frame-time-ticks 50 | ::interval 51 | ::no-display-updates 52 | ::screenshot])) 53 | :connection-and-params 54 | (s/cat 55 | :connection 56 | (s/? 57 | c/connection?) 58 | :params 59 | (s/keys 60 | :opt-un 61 | [::frame-time-ticks 62 | ::interval 63 | ::no-display-updates 64 | ::screenshot]))) 65 | :ret 66 | (s/keys 67 | :req-un 68 | [::has-damage] 69 | :opt-un 70 | [::screenshot-data])) 71 | 72 | (defn 73 | disable 74 | "Disables headless events for the target." 75 | ([] 76 | (disable 77 | (c/get-current-connection) 78 | {})) 79 | ([{:as params, :keys []}] 80 | (disable 81 | (c/get-current-connection) 82 | params)) 83 | ([connection {:as params, :keys []}] 84 | (cmd/command 85 | connection 86 | "HeadlessExperimental" 87 | "disable" 88 | params 89 | {}))) 90 | 91 | (s/fdef 92 | disable 93 | :args 94 | (s/or 95 | :no-args 96 | (s/cat) 97 | :just-params 98 | (s/cat :params (s/keys)) 99 | :connection-and-params 100 | (s/cat 101 | :connection 102 | (s/? 103 | c/connection?) 104 | :params 105 | (s/keys))) 106 | :ret 107 | (s/keys)) 108 | 109 | (defn 110 | enable 111 | "Enables headless events for the target." 112 | ([] 113 | (enable 114 | (c/get-current-connection) 115 | {})) 116 | ([{:as params, :keys []}] 117 | (enable 118 | (c/get-current-connection) 119 | params)) 120 | ([connection {:as params, :keys []}] 121 | (cmd/command 122 | connection 123 | "HeadlessExperimental" 124 | "enable" 125 | params 126 | {}))) 127 | 128 | (s/fdef 129 | enable 130 | :args 131 | (s/or 132 | :no-args 133 | (s/cat) 134 | :just-params 135 | (s/cat :params (s/keys)) 136 | :connection-and-params 137 | (s/cat 138 | :connection 139 | (s/? 140 | c/connection?) 141 | :params 142 | (s/keys))) 143 | :ret 144 | (s/keys)) 145 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/inspector.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.inspector 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (defn 7 | disable 8 | "Disables inspector domain notifications." 9 | ([] 10 | (disable 11 | (c/get-current-connection) 12 | {})) 13 | ([{:as params, :keys []}] 14 | (disable 15 | (c/get-current-connection) 16 | params)) 17 | ([connection {:as params, :keys []}] 18 | (cmd/command 19 | connection 20 | "Inspector" 21 | "disable" 22 | params 23 | {}))) 24 | 25 | (s/fdef 26 | disable 27 | :args 28 | (s/or 29 | :no-args 30 | (s/cat) 31 | :just-params 32 | (s/cat :params (s/keys)) 33 | :connection-and-params 34 | (s/cat 35 | :connection 36 | (s/? 37 | c/connection?) 38 | :params 39 | (s/keys))) 40 | :ret 41 | (s/keys)) 42 | 43 | (defn 44 | enable 45 | "Enables inspector domain notifications." 46 | ([] 47 | (enable 48 | (c/get-current-connection) 49 | {})) 50 | ([{:as params, :keys []}] 51 | (enable 52 | (c/get-current-connection) 53 | params)) 54 | ([connection {:as params, :keys []}] 55 | (cmd/command 56 | connection 57 | "Inspector" 58 | "enable" 59 | params 60 | {}))) 61 | 62 | (s/fdef 63 | enable 64 | :args 65 | (s/or 66 | :no-args 67 | (s/cat) 68 | :just-params 69 | (s/cat :params (s/keys)) 70 | :connection-and-params 71 | (s/cat 72 | :connection 73 | (s/? 74 | c/connection?) 75 | :params 76 | (s/keys))) 77 | :ret 78 | (s/keys)) 79 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/io.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.io 2 | "Input/Output operations for streams produced by DevTools." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::stream-handle 9 | string?) 10 | (defn 11 | close 12 | "Close the stream, discard any temporary backing storage.\n\nParameters map keys:\n\n\n Key | Description \n --------|------------ \n :handle | Handle of the stream to close." 13 | ([] 14 | (close 15 | (c/get-current-connection) 16 | {})) 17 | ([{:as params, :keys [handle]}] 18 | (close 19 | (c/get-current-connection) 20 | params)) 21 | ([connection {:as params, :keys [handle]}] 22 | (cmd/command 23 | connection 24 | "IO" 25 | "close" 26 | params 27 | {:handle "handle"}))) 28 | 29 | (s/fdef 30 | close 31 | :args 32 | (s/or 33 | :no-args 34 | (s/cat) 35 | :just-params 36 | (s/cat 37 | :params 38 | (s/keys 39 | :req-un 40 | [::handle])) 41 | :connection-and-params 42 | (s/cat 43 | :connection 44 | (s/? 45 | c/connection?) 46 | :params 47 | (s/keys 48 | :req-un 49 | [::handle]))) 50 | :ret 51 | (s/keys)) 52 | 53 | (defn 54 | read 55 | "Read a chunk of the stream\n\nParameters map keys:\n\n\n Key | Description \n --------|------------ \n :handle | Handle of the stream to read.\n :offset | Seek to the specified offset before reading (if not specificed, proceed with offset\nfollowing the last read). Some types of streams may only support sequential reads. (optional)\n :size | Maximum number of bytes to read (left upon the agent discretion if not specified). (optional)\n\nReturn map keys:\n\n\n Key | Description \n ----------------|------------ \n :base64-encoded | Set if the data is base64-encoded (optional)\n :data | Data that were read.\n :eof | Set if the end-of-file condition occurred while reading." 56 | ([] 57 | (read 58 | (c/get-current-connection) 59 | {})) 60 | ([{:as params, :keys [handle offset size]}] 61 | (read 62 | (c/get-current-connection) 63 | params)) 64 | ([connection {:as params, :keys [handle offset size]}] 65 | (cmd/command 66 | connection 67 | "IO" 68 | "read" 69 | params 70 | {:handle "handle", :offset "offset", :size "size"}))) 71 | 72 | (s/fdef 73 | read 74 | :args 75 | (s/or 76 | :no-args 77 | (s/cat) 78 | :just-params 79 | (s/cat 80 | :params 81 | (s/keys 82 | :req-un 83 | [::handle] 84 | :opt-un 85 | [::offset 86 | ::size])) 87 | :connection-and-params 88 | (s/cat 89 | :connection 90 | (s/? 91 | c/connection?) 92 | :params 93 | (s/keys 94 | :req-un 95 | [::handle] 96 | :opt-un 97 | [::offset 98 | ::size]))) 99 | :ret 100 | (s/keys 101 | :req-un 102 | [::data 103 | ::eof] 104 | :opt-un 105 | [::base64-encoded])) 106 | 107 | (defn 108 | resolve-blob 109 | "Return UUID of Blob object specified by a remote object id.\n\nParameters map keys:\n\n\n Key | Description \n -----------|------------ \n :object-id | Object id of a Blob object wrapper.\n\nReturn map keys:\n\n\n Key | Description \n ------|------------ \n :uuid | UUID of the specified Blob." 110 | ([] 111 | (resolve-blob 112 | (c/get-current-connection) 113 | {})) 114 | ([{:as params, :keys [object-id]}] 115 | (resolve-blob 116 | (c/get-current-connection) 117 | params)) 118 | ([connection {:as params, :keys [object-id]}] 119 | (cmd/command 120 | connection 121 | "IO" 122 | "resolveBlob" 123 | params 124 | {:object-id "objectId"}))) 125 | 126 | (s/fdef 127 | resolve-blob 128 | :args 129 | (s/or 130 | :no-args 131 | (s/cat) 132 | :just-params 133 | (s/cat 134 | :params 135 | (s/keys 136 | :req-un 137 | [::object-id])) 138 | :connection-and-params 139 | (s/cat 140 | :connection 141 | (s/? 142 | c/connection?) 143 | :params 144 | (s/keys 145 | :req-un 146 | [::object-id]))) 147 | :ret 148 | (s/keys 149 | :req-un 150 | [::uuid])) 151 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/log.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.log 2 | "Provides access to log entries." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::log-entry 9 | (s/keys 10 | :req-un 11 | [::source 12 | ::level 13 | ::text 14 | ::timestamp] 15 | :opt-un 16 | [::category 17 | ::url 18 | ::line-number 19 | ::stack-trace 20 | ::network-request-id 21 | ::worker-id 22 | ::args])) 23 | 24 | (s/def 25 | ::violation-setting 26 | (s/keys 27 | :req-un 28 | [::name 29 | ::threshold])) 30 | (defn 31 | clear 32 | "Clears the log." 33 | ([] 34 | (clear 35 | (c/get-current-connection) 36 | {})) 37 | ([{:as params, :keys []}] 38 | (clear 39 | (c/get-current-connection) 40 | params)) 41 | ([connection {:as params, :keys []}] 42 | (cmd/command 43 | connection 44 | "Log" 45 | "clear" 46 | params 47 | {}))) 48 | 49 | (s/fdef 50 | clear 51 | :args 52 | (s/or 53 | :no-args 54 | (s/cat) 55 | :just-params 56 | (s/cat :params (s/keys)) 57 | :connection-and-params 58 | (s/cat 59 | :connection 60 | (s/? 61 | c/connection?) 62 | :params 63 | (s/keys))) 64 | :ret 65 | (s/keys)) 66 | 67 | (defn 68 | disable 69 | "Disables log domain, prevents further log entries from being reported to the client." 70 | ([] 71 | (disable 72 | (c/get-current-connection) 73 | {})) 74 | ([{:as params, :keys []}] 75 | (disable 76 | (c/get-current-connection) 77 | params)) 78 | ([connection {:as params, :keys []}] 79 | (cmd/command 80 | connection 81 | "Log" 82 | "disable" 83 | params 84 | {}))) 85 | 86 | (s/fdef 87 | disable 88 | :args 89 | (s/or 90 | :no-args 91 | (s/cat) 92 | :just-params 93 | (s/cat :params (s/keys)) 94 | :connection-and-params 95 | (s/cat 96 | :connection 97 | (s/? 98 | c/connection?) 99 | :params 100 | (s/keys))) 101 | :ret 102 | (s/keys)) 103 | 104 | (defn 105 | enable 106 | "Enables log domain, sends the entries collected so far to the client by means of the\n`entryAdded` notification." 107 | ([] 108 | (enable 109 | (c/get-current-connection) 110 | {})) 111 | ([{:as params, :keys []}] 112 | (enable 113 | (c/get-current-connection) 114 | params)) 115 | ([connection {:as params, :keys []}] 116 | (cmd/command 117 | connection 118 | "Log" 119 | "enable" 120 | params 121 | {}))) 122 | 123 | (s/fdef 124 | enable 125 | :args 126 | (s/or 127 | :no-args 128 | (s/cat) 129 | :just-params 130 | (s/cat :params (s/keys)) 131 | :connection-and-params 132 | (s/cat 133 | :connection 134 | (s/? 135 | c/connection?) 136 | :params 137 | (s/keys))) 138 | :ret 139 | (s/keys)) 140 | 141 | (defn 142 | start-violations-report 143 | "start violation reporting.\n\nParameters map keys:\n\n\n Key | Description \n --------|------------ \n :config | Configuration for violations." 144 | ([] 145 | (start-violations-report 146 | (c/get-current-connection) 147 | {})) 148 | ([{:as params, :keys [config]}] 149 | (start-violations-report 150 | (c/get-current-connection) 151 | params)) 152 | ([connection {:as params, :keys [config]}] 153 | (cmd/command 154 | connection 155 | "Log" 156 | "startViolationsReport" 157 | params 158 | {:config "config"}))) 159 | 160 | (s/fdef 161 | start-violations-report 162 | :args 163 | (s/or 164 | :no-args 165 | (s/cat) 166 | :just-params 167 | (s/cat 168 | :params 169 | (s/keys 170 | :req-un 171 | [::config])) 172 | :connection-and-params 173 | (s/cat 174 | :connection 175 | (s/? 176 | c/connection?) 177 | :params 178 | (s/keys 179 | :req-un 180 | [::config]))) 181 | :ret 182 | (s/keys)) 183 | 184 | (defn 185 | stop-violations-report 186 | "Stop violation reporting." 187 | ([] 188 | (stop-violations-report 189 | (c/get-current-connection) 190 | {})) 191 | ([{:as params, :keys []}] 192 | (stop-violations-report 193 | (c/get-current-connection) 194 | params)) 195 | ([connection {:as params, :keys []}] 196 | (cmd/command 197 | connection 198 | "Log" 199 | "stopViolationsReport" 200 | params 201 | {}))) 202 | 203 | (s/fdef 204 | stop-violations-report 205 | :args 206 | (s/or 207 | :no-args 208 | (s/cat) 209 | :just-params 210 | (s/cat :params (s/keys)) 211 | :connection-and-params 212 | (s/cat 213 | :connection 214 | (s/? 215 | c/connection?) 216 | :params 217 | (s/keys))) 218 | :ret 219 | (s/keys)) 220 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/media.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.media 2 | "This domain allows detailed inspection of media elements" 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::player-id 9 | string?) 10 | 11 | (s/def 12 | ::timestamp 13 | number?) 14 | 15 | (s/def 16 | ::player-message 17 | (s/keys 18 | :req-un 19 | [::level 20 | ::message])) 21 | 22 | (s/def 23 | ::player-property 24 | (s/keys 25 | :req-un 26 | [::name 27 | ::value])) 28 | 29 | (s/def 30 | ::player-event 31 | (s/keys 32 | :req-un 33 | [::timestamp 34 | ::value])) 35 | 36 | (s/def 37 | ::player-error 38 | (s/keys 39 | :req-un 40 | [::type 41 | ::error-code])) 42 | (defn 43 | enable 44 | "Enables the Media domain" 45 | ([] 46 | (enable 47 | (c/get-current-connection) 48 | {})) 49 | ([{:as params, :keys []}] 50 | (enable 51 | (c/get-current-connection) 52 | params)) 53 | ([connection {:as params, :keys []}] 54 | (cmd/command 55 | connection 56 | "Media" 57 | "enable" 58 | params 59 | {}))) 60 | 61 | (s/fdef 62 | enable 63 | :args 64 | (s/or 65 | :no-args 66 | (s/cat) 67 | :just-params 68 | (s/cat :params (s/keys)) 69 | :connection-and-params 70 | (s/cat 71 | :connection 72 | (s/? 73 | c/connection?) 74 | :params 75 | (s/keys))) 76 | :ret 77 | (s/keys)) 78 | 79 | (defn 80 | disable 81 | "Disables the Media domain." 82 | ([] 83 | (disable 84 | (c/get-current-connection) 85 | {})) 86 | ([{:as params, :keys []}] 87 | (disable 88 | (c/get-current-connection) 89 | params)) 90 | ([connection {:as params, :keys []}] 91 | (cmd/command 92 | connection 93 | "Media" 94 | "disable" 95 | params 96 | {}))) 97 | 98 | (s/fdef 99 | disable 100 | :args 101 | (s/or 102 | :no-args 103 | (s/cat) 104 | :just-params 105 | (s/cat :params (s/keys)) 106 | :connection-and-params 107 | (s/cat 108 | :connection 109 | (s/? 110 | c/connection?) 111 | :params 112 | (s/keys))) 113 | :ret 114 | (s/keys)) 115 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/memory.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.memory 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::pressure-level 8 | #{"critical" "moderate"}) 9 | 10 | (s/def 11 | ::sampling-profile-node 12 | (s/keys 13 | :req-un 14 | [::size 15 | ::total 16 | ::stack])) 17 | 18 | (s/def 19 | ::sampling-profile 20 | (s/keys 21 | :req-un 22 | [::samples 23 | ::modules])) 24 | 25 | (s/def 26 | ::module 27 | (s/keys 28 | :req-un 29 | [::name 30 | ::uuid 31 | ::base-address 32 | ::size])) 33 | (defn 34 | get-dom-counters 35 | "\n\nReturn map keys:\n\n\n Key | Description \n --------------------|------------ \n :documents | null\n :nodes | null\n :js-event-listeners | null" 36 | ([] 37 | (get-dom-counters 38 | (c/get-current-connection) 39 | {})) 40 | ([{:as params, :keys []}] 41 | (get-dom-counters 42 | (c/get-current-connection) 43 | params)) 44 | ([connection {:as params, :keys []}] 45 | (cmd/command 46 | connection 47 | "Memory" 48 | "getDOMCounters" 49 | params 50 | {}))) 51 | 52 | (s/fdef 53 | get-dom-counters 54 | :args 55 | (s/or 56 | :no-args 57 | (s/cat) 58 | :just-params 59 | (s/cat :params (s/keys)) 60 | :connection-and-params 61 | (s/cat 62 | :connection 63 | (s/? 64 | c/connection?) 65 | :params 66 | (s/keys))) 67 | :ret 68 | (s/keys 69 | :req-un 70 | [::documents 71 | ::nodes 72 | ::js-event-listeners])) 73 | 74 | (defn 75 | prepare-for-leak-detection 76 | "" 77 | ([] 78 | (prepare-for-leak-detection 79 | (c/get-current-connection) 80 | {})) 81 | ([{:as params, :keys []}] 82 | (prepare-for-leak-detection 83 | (c/get-current-connection) 84 | params)) 85 | ([connection {:as params, :keys []}] 86 | (cmd/command 87 | connection 88 | "Memory" 89 | "prepareForLeakDetection" 90 | params 91 | {}))) 92 | 93 | (s/fdef 94 | prepare-for-leak-detection 95 | :args 96 | (s/or 97 | :no-args 98 | (s/cat) 99 | :just-params 100 | (s/cat :params (s/keys)) 101 | :connection-and-params 102 | (s/cat 103 | :connection 104 | (s/? 105 | c/connection?) 106 | :params 107 | (s/keys))) 108 | :ret 109 | (s/keys)) 110 | 111 | (defn 112 | forcibly-purge-java-script-memory 113 | "Simulate OomIntervention by purging V8 memory." 114 | ([] 115 | (forcibly-purge-java-script-memory 116 | (c/get-current-connection) 117 | {})) 118 | ([{:as params, :keys []}] 119 | (forcibly-purge-java-script-memory 120 | (c/get-current-connection) 121 | params)) 122 | ([connection {:as params, :keys []}] 123 | (cmd/command 124 | connection 125 | "Memory" 126 | "forciblyPurgeJavaScriptMemory" 127 | params 128 | {}))) 129 | 130 | (s/fdef 131 | forcibly-purge-java-script-memory 132 | :args 133 | (s/or 134 | :no-args 135 | (s/cat) 136 | :just-params 137 | (s/cat :params (s/keys)) 138 | :connection-and-params 139 | (s/cat 140 | :connection 141 | (s/? 142 | c/connection?) 143 | :params 144 | (s/keys))) 145 | :ret 146 | (s/keys)) 147 | 148 | (defn 149 | set-pressure-notifications-suppressed 150 | "Enable/disable suppressing memory pressure notifications in all processes.\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :suppressed | If true, memory pressure notifications will be suppressed." 151 | ([] 152 | (set-pressure-notifications-suppressed 153 | (c/get-current-connection) 154 | {})) 155 | ([{:as params, :keys [suppressed]}] 156 | (set-pressure-notifications-suppressed 157 | (c/get-current-connection) 158 | params)) 159 | ([connection {:as params, :keys [suppressed]}] 160 | (cmd/command 161 | connection 162 | "Memory" 163 | "setPressureNotificationsSuppressed" 164 | params 165 | {:suppressed "suppressed"}))) 166 | 167 | (s/fdef 168 | set-pressure-notifications-suppressed 169 | :args 170 | (s/or 171 | :no-args 172 | (s/cat) 173 | :just-params 174 | (s/cat 175 | :params 176 | (s/keys 177 | :req-un 178 | [::suppressed])) 179 | :connection-and-params 180 | (s/cat 181 | :connection 182 | (s/? 183 | c/connection?) 184 | :params 185 | (s/keys 186 | :req-un 187 | [::suppressed]))) 188 | :ret 189 | (s/keys)) 190 | 191 | (defn 192 | simulate-pressure-notification 193 | "Simulate a memory pressure notification in all processes.\n\nParameters map keys:\n\n\n Key | Description \n -------|------------ \n :level | Memory pressure level of the notification." 194 | ([] 195 | (simulate-pressure-notification 196 | (c/get-current-connection) 197 | {})) 198 | ([{:as params, :keys [level]}] 199 | (simulate-pressure-notification 200 | (c/get-current-connection) 201 | params)) 202 | ([connection {:as params, :keys [level]}] 203 | (cmd/command 204 | connection 205 | "Memory" 206 | "simulatePressureNotification" 207 | params 208 | {:level "level"}))) 209 | 210 | (s/fdef 211 | simulate-pressure-notification 212 | :args 213 | (s/or 214 | :no-args 215 | (s/cat) 216 | :just-params 217 | (s/cat 218 | :params 219 | (s/keys 220 | :req-un 221 | [::level])) 222 | :connection-and-params 223 | (s/cat 224 | :connection 225 | (s/? 226 | c/connection?) 227 | :params 228 | (s/keys 229 | :req-un 230 | [::level]))) 231 | :ret 232 | (s/keys)) 233 | 234 | (defn 235 | start-sampling 236 | "Start collecting native memory profile.\n\nParameters map keys:\n\n\n Key | Description \n ---------------------|------------ \n :sampling-interval | Average number of bytes between samples. (optional)\n :suppress-randomness | Do not randomize intervals between samples. (optional)" 237 | ([] 238 | (start-sampling 239 | (c/get-current-connection) 240 | {})) 241 | ([{:as params, :keys [sampling-interval suppress-randomness]}] 242 | (start-sampling 243 | (c/get-current-connection) 244 | params)) 245 | ([connection 246 | {:as params, :keys [sampling-interval suppress-randomness]}] 247 | (cmd/command 248 | connection 249 | "Memory" 250 | "startSampling" 251 | params 252 | {:sampling-interval "samplingInterval", 253 | :suppress-randomness "suppressRandomness"}))) 254 | 255 | (s/fdef 256 | start-sampling 257 | :args 258 | (s/or 259 | :no-args 260 | (s/cat) 261 | :just-params 262 | (s/cat 263 | :params 264 | (s/keys 265 | :opt-un 266 | [::sampling-interval 267 | ::suppress-randomness])) 268 | :connection-and-params 269 | (s/cat 270 | :connection 271 | (s/? 272 | c/connection?) 273 | :params 274 | (s/keys 275 | :opt-un 276 | [::sampling-interval 277 | ::suppress-randomness]))) 278 | :ret 279 | (s/keys)) 280 | 281 | (defn 282 | stop-sampling 283 | "Stop collecting native memory profile." 284 | ([] 285 | (stop-sampling 286 | (c/get-current-connection) 287 | {})) 288 | ([{:as params, :keys []}] 289 | (stop-sampling 290 | (c/get-current-connection) 291 | params)) 292 | ([connection {:as params, :keys []}] 293 | (cmd/command 294 | connection 295 | "Memory" 296 | "stopSampling" 297 | params 298 | {}))) 299 | 300 | (s/fdef 301 | stop-sampling 302 | :args 303 | (s/or 304 | :no-args 305 | (s/cat) 306 | :just-params 307 | (s/cat :params (s/keys)) 308 | :connection-and-params 309 | (s/cat 310 | :connection 311 | (s/? 312 | c/connection?) 313 | :params 314 | (s/keys))) 315 | :ret 316 | (s/keys)) 317 | 318 | (defn 319 | get-all-time-sampling-profile 320 | "Retrieve native memory allocations profile\ncollected since renderer process startup.\n\nReturn map keys:\n\n\n Key | Description \n ---------|------------ \n :profile | null" 321 | ([] 322 | (get-all-time-sampling-profile 323 | (c/get-current-connection) 324 | {})) 325 | ([{:as params, :keys []}] 326 | (get-all-time-sampling-profile 327 | (c/get-current-connection) 328 | params)) 329 | ([connection {:as params, :keys []}] 330 | (cmd/command 331 | connection 332 | "Memory" 333 | "getAllTimeSamplingProfile" 334 | params 335 | {}))) 336 | 337 | (s/fdef 338 | get-all-time-sampling-profile 339 | :args 340 | (s/or 341 | :no-args 342 | (s/cat) 343 | :just-params 344 | (s/cat :params (s/keys)) 345 | :connection-and-params 346 | (s/cat 347 | :connection 348 | (s/? 349 | c/connection?) 350 | :params 351 | (s/keys))) 352 | :ret 353 | (s/keys 354 | :req-un 355 | [::profile])) 356 | 357 | (defn 358 | get-browser-sampling-profile 359 | "Retrieve native memory allocations profile\ncollected since browser process startup.\n\nReturn map keys:\n\n\n Key | Description \n ---------|------------ \n :profile | null" 360 | ([] 361 | (get-browser-sampling-profile 362 | (c/get-current-connection) 363 | {})) 364 | ([{:as params, :keys []}] 365 | (get-browser-sampling-profile 366 | (c/get-current-connection) 367 | params)) 368 | ([connection {:as params, :keys []}] 369 | (cmd/command 370 | connection 371 | "Memory" 372 | "getBrowserSamplingProfile" 373 | params 374 | {}))) 375 | 376 | (s/fdef 377 | get-browser-sampling-profile 378 | :args 379 | (s/or 380 | :no-args 381 | (s/cat) 382 | :just-params 383 | (s/cat :params (s/keys)) 384 | :connection-and-params 385 | (s/cat 386 | :connection 387 | (s/? 388 | c/connection?) 389 | :params 390 | (s/keys))) 391 | :ret 392 | (s/keys 393 | :req-un 394 | [::profile])) 395 | 396 | (defn 397 | get-sampling-profile 398 | "Retrieve native memory allocations profile collected since last\n`startSampling` call.\n\nReturn map keys:\n\n\n Key | Description \n ---------|------------ \n :profile | null" 399 | ([] 400 | (get-sampling-profile 401 | (c/get-current-connection) 402 | {})) 403 | ([{:as params, :keys []}] 404 | (get-sampling-profile 405 | (c/get-current-connection) 406 | params)) 407 | ([connection {:as params, :keys []}] 408 | (cmd/command 409 | connection 410 | "Memory" 411 | "getSamplingProfile" 412 | params 413 | {}))) 414 | 415 | (s/fdef 416 | get-sampling-profile 417 | :args 418 | (s/or 419 | :no-args 420 | (s/cat) 421 | :just-params 422 | (s/cat :params (s/keys)) 423 | :connection-and-params 424 | (s/cat 425 | :connection 426 | (s/? 427 | c/connection?) 428 | :params 429 | (s/keys))) 430 | :ret 431 | (s/keys 432 | :req-un 433 | [::profile])) 434 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/performance.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.performance 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::metric 8 | (s/keys 9 | :req-un 10 | [::name 11 | ::value])) 12 | (defn 13 | disable 14 | "Disable collecting and reporting metrics." 15 | ([] 16 | (disable 17 | (c/get-current-connection) 18 | {})) 19 | ([{:as params, :keys []}] 20 | (disable 21 | (c/get-current-connection) 22 | params)) 23 | ([connection {:as params, :keys []}] 24 | (cmd/command 25 | connection 26 | "Performance" 27 | "disable" 28 | params 29 | {}))) 30 | 31 | (s/fdef 32 | disable 33 | :args 34 | (s/or 35 | :no-args 36 | (s/cat) 37 | :just-params 38 | (s/cat :params (s/keys)) 39 | :connection-and-params 40 | (s/cat 41 | :connection 42 | (s/? 43 | c/connection?) 44 | :params 45 | (s/keys))) 46 | :ret 47 | (s/keys)) 48 | 49 | (defn 50 | enable 51 | "Enable collecting and reporting metrics.\n\nParameters map keys:\n\n\n Key | Description \n -------------|------------ \n :time-domain | Time domain to use for collecting and reporting duration metrics. (optional)" 52 | ([] 53 | (enable 54 | (c/get-current-connection) 55 | {})) 56 | ([{:as params, :keys [time-domain]}] 57 | (enable 58 | (c/get-current-connection) 59 | params)) 60 | ([connection {:as params, :keys [time-domain]}] 61 | (cmd/command 62 | connection 63 | "Performance" 64 | "enable" 65 | params 66 | {:time-domain "timeDomain"}))) 67 | 68 | (s/fdef 69 | enable 70 | :args 71 | (s/or 72 | :no-args 73 | (s/cat) 74 | :just-params 75 | (s/cat 76 | :params 77 | (s/keys 78 | :opt-un 79 | [::time-domain])) 80 | :connection-and-params 81 | (s/cat 82 | :connection 83 | (s/? 84 | c/connection?) 85 | :params 86 | (s/keys 87 | :opt-un 88 | [::time-domain]))) 89 | :ret 90 | (s/keys)) 91 | 92 | (defn 93 | set-time-domain 94 | "Sets time domain to use for collecting and reporting duration metrics.\nNote that this must be called before enabling metrics collection. Calling\nthis method while metrics collection is enabled returns an error.\n\nParameters map keys:\n\n\n Key | Description \n -------------|------------ \n :time-domain | Time domain" 95 | ([] 96 | (set-time-domain 97 | (c/get-current-connection) 98 | {})) 99 | ([{:as params, :keys [time-domain]}] 100 | (set-time-domain 101 | (c/get-current-connection) 102 | params)) 103 | ([connection {:as params, :keys [time-domain]}] 104 | (cmd/command 105 | connection 106 | "Performance" 107 | "setTimeDomain" 108 | params 109 | {:time-domain "timeDomain"}))) 110 | 111 | (s/fdef 112 | set-time-domain 113 | :args 114 | (s/or 115 | :no-args 116 | (s/cat) 117 | :just-params 118 | (s/cat 119 | :params 120 | (s/keys 121 | :req-un 122 | [::time-domain])) 123 | :connection-and-params 124 | (s/cat 125 | :connection 126 | (s/? 127 | c/connection?) 128 | :params 129 | (s/keys 130 | :req-un 131 | [::time-domain]))) 132 | :ret 133 | (s/keys)) 134 | 135 | (defn 136 | get-metrics 137 | "Retrieve current values of run-time metrics.\n\nReturn map keys:\n\n\n Key | Description \n ---------|------------ \n :metrics | Current values for run-time metrics." 138 | ([] 139 | (get-metrics 140 | (c/get-current-connection) 141 | {})) 142 | ([{:as params, :keys []}] 143 | (get-metrics 144 | (c/get-current-connection) 145 | params)) 146 | ([connection {:as params, :keys []}] 147 | (cmd/command 148 | connection 149 | "Performance" 150 | "getMetrics" 151 | params 152 | {}))) 153 | 154 | (s/fdef 155 | get-metrics 156 | :args 157 | (s/or 158 | :no-args 159 | (s/cat) 160 | :just-params 161 | (s/cat :params (s/keys)) 162 | :connection-and-params 163 | (s/cat 164 | :connection 165 | (s/? 166 | c/connection?) 167 | :params 168 | (s/keys))) 169 | :ret 170 | (s/keys 171 | :req-un 172 | [::metrics])) 173 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/performance_timeline.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.performance-timeline 2 | "Reporting of performance timeline events, as specified in\nhttps://w3c.github.io/performance-timeline/#dom-performanceobserver." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::largest-contentful-paint 9 | (s/keys 10 | :req-un 11 | [::render-time 12 | ::load-time 13 | ::size] 14 | :opt-un 15 | [::element-id 16 | ::url 17 | ::node-id])) 18 | 19 | (s/def 20 | ::layout-shift-attribution 21 | (s/keys 22 | :req-un 23 | [::previous-rect 24 | ::current-rect] 25 | :opt-un 26 | [::node-id])) 27 | 28 | (s/def 29 | ::layout-shift 30 | (s/keys 31 | :req-un 32 | [::value 33 | ::had-recent-input 34 | ::last-input-time 35 | ::sources])) 36 | 37 | (s/def 38 | ::timeline-event 39 | (s/keys 40 | :req-un 41 | [::frame-id 42 | ::type 43 | ::name 44 | ::time] 45 | :opt-un 46 | [::duration 47 | ::lcp-details 48 | ::layout-shift-details])) 49 | (defn 50 | enable 51 | "Previously buffered events would be reported before method returns.\nSee also: timelineEventAdded\n\nParameters map keys:\n\n\n Key | Description \n -------------|------------ \n :event-types | The types of event to report, as specified in\nhttps://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype\nThe specified filter overrides any previous filters, passing empty\nfilter disables recording.\nNote that not all types exposed to the web platform are currently supported." 52 | ([] 53 | (enable 54 | (c/get-current-connection) 55 | {})) 56 | ([{:as params, :keys [event-types]}] 57 | (enable 58 | (c/get-current-connection) 59 | params)) 60 | ([connection {:as params, :keys [event-types]}] 61 | (cmd/command 62 | connection 63 | "PerformanceTimeline" 64 | "enable" 65 | params 66 | {:event-types "eventTypes"}))) 67 | 68 | (s/fdef 69 | enable 70 | :args 71 | (s/or 72 | :no-args 73 | (s/cat) 74 | :just-params 75 | (s/cat 76 | :params 77 | (s/keys 78 | :req-un 79 | [::event-types])) 80 | :connection-and-params 81 | (s/cat 82 | :connection 83 | (s/? 84 | c/connection?) 85 | :params 86 | (s/keys 87 | :req-un 88 | [::event-types]))) 89 | :ret 90 | (s/keys)) 91 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/schema.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.schema 2 | "This domain is deprecated." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::domain 9 | (s/keys 10 | :req-un 11 | [::name 12 | ::version])) 13 | (defn 14 | get-domains 15 | "Returns supported domains.\n\nReturn map keys:\n\n\n Key | Description \n ---------|------------ \n :domains | List of supported domains." 16 | ([] 17 | (get-domains 18 | (c/get-current-connection) 19 | {})) 20 | ([{:as params, :keys []}] 21 | (get-domains 22 | (c/get-current-connection) 23 | params)) 24 | ([connection {:as params, :keys []}] 25 | (cmd/command 26 | connection 27 | "Schema" 28 | "getDomains" 29 | params 30 | {}))) 31 | 32 | (s/fdef 33 | get-domains 34 | :args 35 | (s/or 36 | :no-args 37 | (s/cat) 38 | :just-params 39 | (s/cat :params (s/keys)) 40 | :connection-and-params 41 | (s/cat 42 | :connection 43 | (s/? 44 | c/connection?) 45 | :params 46 | (s/keys))) 47 | :ret 48 | (s/keys 49 | :req-un 50 | [::domains])) 51 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/security.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.security 2 | "Security" 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::certificate-id 9 | integer?) 10 | 11 | (s/def 12 | ::mixed-content-type 13 | #{"none" "blockable" "optionally-blockable"}) 14 | 15 | (s/def 16 | ::security-state 17 | #{"neutral" "info" "insecure-broken" "secure" "unknown" "insecure"}) 18 | 19 | (s/def 20 | ::certificate-security-state 21 | (s/keys 22 | :req-un 23 | [::protocol 24 | ::key-exchange 25 | ::cipher 26 | ::certificate 27 | ::subject-name 28 | ::issuer 29 | ::valid-from 30 | ::valid-to 31 | ::certificate-has-weak-signature 32 | ::certificate-has-sha1-signature 33 | ::modern-ssl 34 | ::obsolete-ssl-protocol 35 | ::obsolete-ssl-key-exchange 36 | ::obsolete-ssl-cipher 37 | ::obsolete-ssl-signature] 38 | :opt-un 39 | [::key-exchange-group 40 | ::mac 41 | ::certificate-network-error])) 42 | 43 | (s/def 44 | ::safety-tip-status 45 | #{"badReputation" "lookalike"}) 46 | 47 | (s/def 48 | ::safety-tip-info 49 | (s/keys 50 | :req-un 51 | [::safety-tip-status] 52 | :opt-un 53 | [::safe-url])) 54 | 55 | (s/def 56 | ::visible-security-state 57 | (s/keys 58 | :req-un 59 | [::security-state 60 | ::security-state-issue-ids] 61 | :opt-un 62 | [::certificate-security-state 63 | ::safety-tip-info])) 64 | 65 | (s/def 66 | ::security-state-explanation 67 | (s/keys 68 | :req-un 69 | [::security-state 70 | ::title 71 | ::summary 72 | ::description 73 | ::mixed-content-type 74 | ::certificate] 75 | :opt-un 76 | [::recommendations])) 77 | 78 | (s/def 79 | ::insecure-content-status 80 | (s/keys 81 | :req-un 82 | [::ran-mixed-content 83 | ::displayed-mixed-content 84 | ::contained-mixed-form 85 | ::ran-content-with-cert-errors 86 | ::displayed-content-with-cert-errors 87 | ::ran-insecure-content-style 88 | ::displayed-insecure-content-style])) 89 | 90 | (s/def 91 | ::certificate-error-action 92 | #{"cancel" "continue"}) 93 | (defn 94 | disable 95 | "Disables tracking security state changes." 96 | ([] 97 | (disable 98 | (c/get-current-connection) 99 | {})) 100 | ([{:as params, :keys []}] 101 | (disable 102 | (c/get-current-connection) 103 | params)) 104 | ([connection {:as params, :keys []}] 105 | (cmd/command 106 | connection 107 | "Security" 108 | "disable" 109 | params 110 | {}))) 111 | 112 | (s/fdef 113 | disable 114 | :args 115 | (s/or 116 | :no-args 117 | (s/cat) 118 | :just-params 119 | (s/cat :params (s/keys)) 120 | :connection-and-params 121 | (s/cat 122 | :connection 123 | (s/? 124 | c/connection?) 125 | :params 126 | (s/keys))) 127 | :ret 128 | (s/keys)) 129 | 130 | (defn 131 | enable 132 | "Enables tracking security state changes." 133 | ([] 134 | (enable 135 | (c/get-current-connection) 136 | {})) 137 | ([{:as params, :keys []}] 138 | (enable 139 | (c/get-current-connection) 140 | params)) 141 | ([connection {:as params, :keys []}] 142 | (cmd/command 143 | connection 144 | "Security" 145 | "enable" 146 | params 147 | {}))) 148 | 149 | (s/fdef 150 | enable 151 | :args 152 | (s/or 153 | :no-args 154 | (s/cat) 155 | :just-params 156 | (s/cat :params (s/keys)) 157 | :connection-and-params 158 | (s/cat 159 | :connection 160 | (s/? 161 | c/connection?) 162 | :params 163 | (s/keys))) 164 | :ret 165 | (s/keys)) 166 | 167 | (defn 168 | set-ignore-certificate-errors 169 | "Enable/disable whether all certificate errors should be ignored.\n\nParameters map keys:\n\n\n Key | Description \n --------|------------ \n :ignore | If true, all certificate errors will be ignored." 170 | ([] 171 | (set-ignore-certificate-errors 172 | (c/get-current-connection) 173 | {})) 174 | ([{:as params, :keys [ignore]}] 175 | (set-ignore-certificate-errors 176 | (c/get-current-connection) 177 | params)) 178 | ([connection {:as params, :keys [ignore]}] 179 | (cmd/command 180 | connection 181 | "Security" 182 | "setIgnoreCertificateErrors" 183 | params 184 | {:ignore "ignore"}))) 185 | 186 | (s/fdef 187 | set-ignore-certificate-errors 188 | :args 189 | (s/or 190 | :no-args 191 | (s/cat) 192 | :just-params 193 | (s/cat 194 | :params 195 | (s/keys 196 | :req-un 197 | [::ignore])) 198 | :connection-and-params 199 | (s/cat 200 | :connection 201 | (s/? 202 | c/connection?) 203 | :params 204 | (s/keys 205 | :req-un 206 | [::ignore]))) 207 | :ret 208 | (s/keys)) 209 | 210 | (defn 211 | handle-certificate-error 212 | "Handles a certificate error that fired a certificateError event.\n\nParameters map keys:\n\n\n Key | Description \n ----------|------------ \n :event-id | The ID of the event.\n :action | The action to take on the certificate error." 213 | ([] 214 | (handle-certificate-error 215 | (c/get-current-connection) 216 | {})) 217 | ([{:as params, :keys [event-id action]}] 218 | (handle-certificate-error 219 | (c/get-current-connection) 220 | params)) 221 | ([connection {:as params, :keys [event-id action]}] 222 | (cmd/command 223 | connection 224 | "Security" 225 | "handleCertificateError" 226 | params 227 | {:event-id "eventId", :action "action"}))) 228 | 229 | (s/fdef 230 | handle-certificate-error 231 | :args 232 | (s/or 233 | :no-args 234 | (s/cat) 235 | :just-params 236 | (s/cat 237 | :params 238 | (s/keys 239 | :req-un 240 | [::event-id 241 | ::action])) 242 | :connection-and-params 243 | (s/cat 244 | :connection 245 | (s/? 246 | c/connection?) 247 | :params 248 | (s/keys 249 | :req-un 250 | [::event-id 251 | ::action]))) 252 | :ret 253 | (s/keys)) 254 | 255 | (defn 256 | set-override-certificate-errors 257 | "Enable/disable overriding certificate errors. If enabled, all certificate error events need to\nbe handled by the DevTools client and should be answered with `handleCertificateError` commands.\n\nParameters map keys:\n\n\n Key | Description \n ----------|------------ \n :override | If true, certificate errors will be overridden." 258 | ([] 259 | (set-override-certificate-errors 260 | (c/get-current-connection) 261 | {})) 262 | ([{:as params, :keys [override]}] 263 | (set-override-certificate-errors 264 | (c/get-current-connection) 265 | params)) 266 | ([connection {:as params, :keys [override]}] 267 | (cmd/command 268 | connection 269 | "Security" 270 | "setOverrideCertificateErrors" 271 | params 272 | {:override "override"}))) 273 | 274 | (s/fdef 275 | set-override-certificate-errors 276 | :args 277 | (s/or 278 | :no-args 279 | (s/cat) 280 | :just-params 281 | (s/cat 282 | :params 283 | (s/keys 284 | :req-un 285 | [::override])) 286 | :connection-and-params 287 | (s/cat 288 | :connection 289 | (s/? 290 | c/connection?) 291 | :params 292 | (s/keys 293 | :req-un 294 | [::override]))) 295 | :ret 296 | (s/keys)) 297 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/system_info.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.system-info 2 | "The SystemInfo domain defines methods and events for querying low-level system information." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::gpu-device 9 | (s/keys 10 | :req-un 11 | [::vendor-id 12 | ::device-id 13 | ::vendor-string 14 | ::device-string 15 | ::driver-vendor 16 | ::driver-version] 17 | :opt-un 18 | [::sub-sys-id 19 | ::revision])) 20 | 21 | (s/def 22 | ::size 23 | (s/keys 24 | :req-un 25 | [::width 26 | ::height])) 27 | 28 | (s/def 29 | ::video-decode-accelerator-capability 30 | (s/keys 31 | :req-un 32 | [::profile 33 | ::max-resolution 34 | ::min-resolution])) 35 | 36 | (s/def 37 | ::video-encode-accelerator-capability 38 | (s/keys 39 | :req-un 40 | [::profile 41 | ::max-resolution 42 | ::max-framerate-numerator 43 | ::max-framerate-denominator])) 44 | 45 | (s/def 46 | ::subsampling-format 47 | #{"yuv422" "yuv444" "yuv420"}) 48 | 49 | (s/def 50 | ::image-type 51 | #{"webp" "jpeg" "unknown"}) 52 | 53 | (s/def 54 | ::image-decode-accelerator-capability 55 | (s/keys 56 | :req-un 57 | [::image-type 58 | ::max-dimensions 59 | ::min-dimensions 60 | ::subsamplings])) 61 | 62 | (s/def 63 | ::gpu-info 64 | (s/keys 65 | :req-un 66 | [::devices 67 | ::driver-bug-workarounds 68 | ::video-decoding 69 | ::video-encoding 70 | ::image-decoding] 71 | :opt-un 72 | [::aux-attributes 73 | ::feature-status])) 74 | 75 | (s/def 76 | ::process-info 77 | (s/keys 78 | :req-un 79 | [::type 80 | ::id 81 | ::cpu-time])) 82 | (defn 83 | get-info 84 | "Returns information about the system.\n\nReturn map keys:\n\n\n Key | Description \n ---------------|------------ \n :gpu | Information about the GPUs on the system.\n :model-name | A platform-dependent description of the model of the machine. On Mac OS, this is, for\nexample, 'MacBookPro'. Will be the empty string if not supported.\n :model-version | A platform-dependent description of the version of the machine. On Mac OS, this is, for\nexample, '10.1'. Will be the empty string if not supported.\n :command-line | The command line string used to launch the browser. Will be the empty string if not\nsupported." 85 | ([] 86 | (get-info 87 | (c/get-current-connection) 88 | {})) 89 | ([{:as params, :keys []}] 90 | (get-info 91 | (c/get-current-connection) 92 | params)) 93 | ([connection {:as params, :keys []}] 94 | (cmd/command 95 | connection 96 | "SystemInfo" 97 | "getInfo" 98 | params 99 | {}))) 100 | 101 | (s/fdef 102 | get-info 103 | :args 104 | (s/or 105 | :no-args 106 | (s/cat) 107 | :just-params 108 | (s/cat :params (s/keys)) 109 | :connection-and-params 110 | (s/cat 111 | :connection 112 | (s/? 113 | c/connection?) 114 | :params 115 | (s/keys))) 116 | :ret 117 | (s/keys 118 | :req-un 119 | [::gpu 120 | ::model-name 121 | ::model-version 122 | ::command-line])) 123 | 124 | (defn 125 | get-process-info 126 | "Returns information about all running processes.\n\nReturn map keys:\n\n\n Key | Description \n --------------|------------ \n :process-info | An array of process info blocks." 127 | ([] 128 | (get-process-info 129 | (c/get-current-connection) 130 | {})) 131 | ([{:as params, :keys []}] 132 | (get-process-info 133 | (c/get-current-connection) 134 | params)) 135 | ([connection {:as params, :keys []}] 136 | (cmd/command 137 | connection 138 | "SystemInfo" 139 | "getProcessInfo" 140 | params 141 | {}))) 142 | 143 | (s/fdef 144 | get-process-info 145 | :args 146 | (s/or 147 | :no-args 148 | (s/cat) 149 | :just-params 150 | (s/cat :params (s/keys)) 151 | :connection-and-params 152 | (s/cat 153 | :connection 154 | (s/? 155 | c/connection?) 156 | :params 157 | (s/keys))) 158 | :ret 159 | (s/keys 160 | :req-un 161 | [::process-info])) 162 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/tethering.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.tethering 2 | "The Tethering domain defines methods and events for browser port binding." 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (defn 8 | bind 9 | "Request browser port binding.\n\nParameters map keys:\n\n\n Key | Description \n ------|------------ \n :port | Port number to bind." 10 | ([] 11 | (bind 12 | (c/get-current-connection) 13 | {})) 14 | ([{:as params, :keys [port]}] 15 | (bind 16 | (c/get-current-connection) 17 | params)) 18 | ([connection {:as params, :keys [port]}] 19 | (cmd/command 20 | connection 21 | "Tethering" 22 | "bind" 23 | params 24 | {:port "port"}))) 25 | 26 | (s/fdef 27 | bind 28 | :args 29 | (s/or 30 | :no-args 31 | (s/cat) 32 | :just-params 33 | (s/cat 34 | :params 35 | (s/keys 36 | :req-un 37 | [::port])) 38 | :connection-and-params 39 | (s/cat 40 | :connection 41 | (s/? 42 | c/connection?) 43 | :params 44 | (s/keys 45 | :req-un 46 | [::port]))) 47 | :ret 48 | (s/keys)) 49 | 50 | (defn 51 | unbind 52 | "Request browser port unbinding.\n\nParameters map keys:\n\n\n Key | Description \n ------|------------ \n :port | Port number to unbind." 53 | ([] 54 | (unbind 55 | (c/get-current-connection) 56 | {})) 57 | ([{:as params, :keys [port]}] 58 | (unbind 59 | (c/get-current-connection) 60 | params)) 61 | ([connection {:as params, :keys [port]}] 62 | (cmd/command 63 | connection 64 | "Tethering" 65 | "unbind" 66 | params 67 | {:port "port"}))) 68 | 69 | (s/fdef 70 | unbind 71 | :args 72 | (s/or 73 | :no-args 74 | (s/cat) 75 | :just-params 76 | (s/cat 77 | :params 78 | (s/keys 79 | :req-un 80 | [::port])) 81 | :connection-and-params 82 | (s/cat 83 | :connection 84 | (s/? 85 | c/connection?) 86 | :params 87 | (s/keys 88 | :req-un 89 | [::port]))) 90 | :ret 91 | (s/keys)) 92 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/tracing.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.tracing 2 | (:require [clojure.spec.alpha :as s] 3 | [clj-chrome-devtools.impl.command :as cmd] 4 | [clj-chrome-devtools.impl.connection :as c])) 5 | 6 | (s/def 7 | ::memory-dump-config 8 | (s/keys)) 9 | 10 | (s/def 11 | ::trace-config 12 | (s/keys 13 | :opt-un 14 | [::record-mode 15 | ::enable-sampling 16 | ::enable-systrace 17 | ::enable-argument-filter 18 | ::included-categories 19 | ::excluded-categories 20 | ::synthetic-delays 21 | ::memory-dump-config])) 22 | 23 | (s/def 24 | ::stream-format 25 | #{"json" "proto"}) 26 | 27 | (s/def 28 | ::stream-compression 29 | #{"none" "gzip"}) 30 | 31 | (s/def 32 | ::memory-dump-level-of-detail 33 | #{"detailed" "light" "background"}) 34 | 35 | (s/def 36 | ::tracing-backend 37 | #{"auto" "chrome" "system"}) 38 | (defn 39 | end 40 | "Stop trace events collection." 41 | ([] 42 | (end 43 | (c/get-current-connection) 44 | {})) 45 | ([{:as params, :keys []}] 46 | (end 47 | (c/get-current-connection) 48 | params)) 49 | ([connection {:as params, :keys []}] 50 | (cmd/command 51 | connection 52 | "Tracing" 53 | "end" 54 | params 55 | {}))) 56 | 57 | (s/fdef 58 | end 59 | :args 60 | (s/or 61 | :no-args 62 | (s/cat) 63 | :just-params 64 | (s/cat :params (s/keys)) 65 | :connection-and-params 66 | (s/cat 67 | :connection 68 | (s/? 69 | c/connection?) 70 | :params 71 | (s/keys))) 72 | :ret 73 | (s/keys)) 74 | 75 | (defn 76 | get-categories 77 | "Gets supported tracing categories.\n\nReturn map keys:\n\n\n Key | Description \n ------------|------------ \n :categories | A list of supported tracing categories." 78 | ([] 79 | (get-categories 80 | (c/get-current-connection) 81 | {})) 82 | ([{:as params, :keys []}] 83 | (get-categories 84 | (c/get-current-connection) 85 | params)) 86 | ([connection {:as params, :keys []}] 87 | (cmd/command 88 | connection 89 | "Tracing" 90 | "getCategories" 91 | params 92 | {}))) 93 | 94 | (s/fdef 95 | get-categories 96 | :args 97 | (s/or 98 | :no-args 99 | (s/cat) 100 | :just-params 101 | (s/cat :params (s/keys)) 102 | :connection-and-params 103 | (s/cat 104 | :connection 105 | (s/? 106 | c/connection?) 107 | :params 108 | (s/keys))) 109 | :ret 110 | (s/keys 111 | :req-un 112 | [::categories])) 113 | 114 | (defn 115 | record-clock-sync-marker 116 | "Record a clock sync marker in the trace.\n\nParameters map keys:\n\n\n Key | Description \n ---------|------------ \n :sync-id | The ID of this clock sync marker" 117 | ([] 118 | (record-clock-sync-marker 119 | (c/get-current-connection) 120 | {})) 121 | ([{:as params, :keys [sync-id]}] 122 | (record-clock-sync-marker 123 | (c/get-current-connection) 124 | params)) 125 | ([connection {:as params, :keys [sync-id]}] 126 | (cmd/command 127 | connection 128 | "Tracing" 129 | "recordClockSyncMarker" 130 | params 131 | {:sync-id "syncId"}))) 132 | 133 | (s/fdef 134 | record-clock-sync-marker 135 | :args 136 | (s/or 137 | :no-args 138 | (s/cat) 139 | :just-params 140 | (s/cat 141 | :params 142 | (s/keys 143 | :req-un 144 | [::sync-id])) 145 | :connection-and-params 146 | (s/cat 147 | :connection 148 | (s/? 149 | c/connection?) 150 | :params 151 | (s/keys 152 | :req-un 153 | [::sync-id]))) 154 | :ret 155 | (s/keys)) 156 | 157 | (defn 158 | request-memory-dump 159 | "Request a global memory dump.\n\nParameters map keys:\n\n\n Key | Description \n -----------------|------------ \n :deterministic | Enables more deterministic results by forcing garbage collection (optional)\n :level-of-detail | Specifies level of details in memory dump. Defaults to \"detailed\". (optional)\n\nReturn map keys:\n\n\n Key | Description \n -----------|------------ \n :dump-guid | GUID of the resulting global memory dump.\n :success | True iff the global memory dump succeeded." 160 | ([] 161 | (request-memory-dump 162 | (c/get-current-connection) 163 | {})) 164 | ([{:as params, :keys [deterministic level-of-detail]}] 165 | (request-memory-dump 166 | (c/get-current-connection) 167 | params)) 168 | ([connection {:as params, :keys [deterministic level-of-detail]}] 169 | (cmd/command 170 | connection 171 | "Tracing" 172 | "requestMemoryDump" 173 | params 174 | {:deterministic "deterministic", :level-of-detail "levelOfDetail"}))) 175 | 176 | (s/fdef 177 | request-memory-dump 178 | :args 179 | (s/or 180 | :no-args 181 | (s/cat) 182 | :just-params 183 | (s/cat 184 | :params 185 | (s/keys 186 | :opt-un 187 | [::deterministic 188 | ::level-of-detail])) 189 | :connection-and-params 190 | (s/cat 191 | :connection 192 | (s/? 193 | c/connection?) 194 | :params 195 | (s/keys 196 | :opt-un 197 | [::deterministic 198 | ::level-of-detail]))) 199 | :ret 200 | (s/keys 201 | :req-un 202 | [::dump-guid 203 | ::success])) 204 | 205 | (defn 206 | start 207 | "Start trace events collection.\n\nParameters map keys:\n\n\n Key | Description \n ---------------------------------|------------ \n :categories | Category/tag filter (optional)\n :options | Tracing options (optional)\n :buffer-usage-reporting-interval | If set, the agent will issue bufferUsage events at this interval, specified in milliseconds (optional)\n :transfer-mode | Whether to report trace events as series of dataCollected events or to save trace to a\nstream (defaults to `ReportEvents`). (optional)\n :stream-format | Trace data format to use. This only applies when using `ReturnAsStream`\ntransfer mode (defaults to `json`). (optional)\n :stream-compression | Compression format to use. This only applies when using `ReturnAsStream`\ntransfer mode (defaults to `none`) (optional)\n :trace-config | null (optional)\n :perfetto-config | Base64-encoded serialized perfetto.protos.TraceConfig protobuf message\nWhen specified, the parameters `categories`, `options`, `traceConfig`\nare ignored. (Encoded as a base64 string when passed over JSON) (optional)\n :tracing-backend | Backend type (defaults to `auto`) (optional)" 208 | ([] 209 | (start 210 | (c/get-current-connection) 211 | {})) 212 | ([{:as params, 213 | :keys 214 | [categories 215 | options 216 | buffer-usage-reporting-interval 217 | transfer-mode 218 | stream-format 219 | stream-compression 220 | trace-config 221 | perfetto-config 222 | tracing-backend]}] 223 | (start 224 | (c/get-current-connection) 225 | params)) 226 | ([connection 227 | {:as params, 228 | :keys 229 | [categories 230 | options 231 | buffer-usage-reporting-interval 232 | transfer-mode 233 | stream-format 234 | stream-compression 235 | trace-config 236 | perfetto-config 237 | tracing-backend]}] 238 | (cmd/command 239 | connection 240 | "Tracing" 241 | "start" 242 | params 243 | {:categories "categories", 244 | :options "options", 245 | :buffer-usage-reporting-interval "bufferUsageReportingInterval", 246 | :transfer-mode "transferMode", 247 | :stream-format "streamFormat", 248 | :stream-compression "streamCompression", 249 | :trace-config "traceConfig", 250 | :perfetto-config "perfettoConfig", 251 | :tracing-backend "tracingBackend"}))) 252 | 253 | (s/fdef 254 | start 255 | :args 256 | (s/or 257 | :no-args 258 | (s/cat) 259 | :just-params 260 | (s/cat 261 | :params 262 | (s/keys 263 | :opt-un 264 | [::categories 265 | ::options 266 | ::buffer-usage-reporting-interval 267 | ::transfer-mode 268 | ::stream-format 269 | ::stream-compression 270 | ::trace-config 271 | ::perfetto-config 272 | ::tracing-backend])) 273 | :connection-and-params 274 | (s/cat 275 | :connection 276 | (s/? 277 | c/connection?) 278 | :params 279 | (s/keys 280 | :opt-un 281 | [::categories 282 | ::options 283 | ::buffer-usage-reporting-interval 284 | ::transfer-mode 285 | ::stream-format 286 | ::stream-compression 287 | ::trace-config 288 | ::perfetto-config 289 | ::tracing-backend]))) 290 | :ret 291 | (s/keys)) 292 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/commands/web_audio.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.commands.web-audio 2 | "This domain allows inspection of Web Audio API.\nhttps://webaudio.github.io/web-audio-api/" 3 | (:require [clojure.spec.alpha :as s] 4 | [clj-chrome-devtools.impl.command :as cmd] 5 | [clj-chrome-devtools.impl.connection :as c])) 6 | 7 | (s/def 8 | ::graph-object-id 9 | string?) 10 | 11 | (s/def 12 | ::context-type 13 | #{"realtime" "offline"}) 14 | 15 | (s/def 16 | ::context-state 17 | #{"running" "closed" "suspended"}) 18 | 19 | (s/def 20 | ::node-type 21 | string?) 22 | 23 | (s/def 24 | ::channel-count-mode 25 | #{"max" "clamped-max" "explicit"}) 26 | 27 | (s/def 28 | ::channel-interpretation 29 | #{"speakers" "discrete"}) 30 | 31 | (s/def 32 | ::param-type 33 | string?) 34 | 35 | (s/def 36 | ::automation-rate 37 | #{"k-rate" "a-rate"}) 38 | 39 | (s/def 40 | ::context-realtime-data 41 | (s/keys 42 | :req-un 43 | [::current-time 44 | ::render-capacity 45 | ::callback-interval-mean 46 | ::callback-interval-variance])) 47 | 48 | (s/def 49 | ::base-audio-context 50 | (s/keys 51 | :req-un 52 | [::context-id 53 | ::context-type 54 | ::context-state 55 | ::callback-buffer-size 56 | ::max-output-channel-count 57 | ::sample-rate] 58 | :opt-un 59 | [::realtime-data])) 60 | 61 | (s/def 62 | ::audio-listener 63 | (s/keys 64 | :req-un 65 | [::listener-id 66 | ::context-id])) 67 | 68 | (s/def 69 | ::audio-node 70 | (s/keys 71 | :req-un 72 | [::node-id 73 | ::context-id 74 | ::node-type 75 | ::number-of-inputs 76 | ::number-of-outputs 77 | ::channel-count 78 | ::channel-count-mode 79 | ::channel-interpretation])) 80 | 81 | (s/def 82 | ::audio-param 83 | (s/keys 84 | :req-un 85 | [::param-id 86 | ::node-id 87 | ::context-id 88 | ::param-type 89 | ::rate 90 | ::default-value 91 | ::min-value 92 | ::max-value])) 93 | (defn 94 | enable 95 | "Enables the WebAudio domain and starts sending context lifetime events." 96 | ([] 97 | (enable 98 | (c/get-current-connection) 99 | {})) 100 | ([{:as params, :keys []}] 101 | (enable 102 | (c/get-current-connection) 103 | params)) 104 | ([connection {:as params, :keys []}] 105 | (cmd/command 106 | connection 107 | "WebAudio" 108 | "enable" 109 | params 110 | {}))) 111 | 112 | (s/fdef 113 | enable 114 | :args 115 | (s/or 116 | :no-args 117 | (s/cat) 118 | :just-params 119 | (s/cat :params (s/keys)) 120 | :connection-and-params 121 | (s/cat 122 | :connection 123 | (s/? 124 | c/connection?) 125 | :params 126 | (s/keys))) 127 | :ret 128 | (s/keys)) 129 | 130 | (defn 131 | disable 132 | "Disables the WebAudio domain." 133 | ([] 134 | (disable 135 | (c/get-current-connection) 136 | {})) 137 | ([{:as params, :keys []}] 138 | (disable 139 | (c/get-current-connection) 140 | params)) 141 | ([connection {:as params, :keys []}] 142 | (cmd/command 143 | connection 144 | "WebAudio" 145 | "disable" 146 | params 147 | {}))) 148 | 149 | (s/fdef 150 | disable 151 | :args 152 | (s/or 153 | :no-args 154 | (s/cat) 155 | :just-params 156 | (s/cat :params (s/keys)) 157 | :connection-and-params 158 | (s/cat 159 | :connection 160 | (s/? 161 | c/connection?) 162 | :params 163 | (s/keys))) 164 | :ret 165 | (s/keys)) 166 | 167 | (defn 168 | get-realtime-data 169 | "Fetch the realtime data from the registered contexts.\n\nParameters map keys:\n\n\n Key | Description \n ------------|------------ \n :context-id | null\n\nReturn map keys:\n\n\n Key | Description \n ---------------|------------ \n :realtime-data | null" 170 | ([] 171 | (get-realtime-data 172 | (c/get-current-connection) 173 | {})) 174 | ([{:as params, :keys [context-id]}] 175 | (get-realtime-data 176 | (c/get-current-connection) 177 | params)) 178 | ([connection {:as params, :keys [context-id]}] 179 | (cmd/command 180 | connection 181 | "WebAudio" 182 | "getRealtimeData" 183 | params 184 | {:context-id "contextId"}))) 185 | 186 | (s/fdef 187 | get-realtime-data 188 | :args 189 | (s/or 190 | :no-args 191 | (s/cat) 192 | :just-params 193 | (s/cat 194 | :params 195 | (s/keys 196 | :req-un 197 | [::context-id])) 198 | :connection-and-params 199 | (s/cat 200 | :connection 201 | (s/? 202 | c/connection?) 203 | :params 204 | (s/keys 205 | :req-un 206 | [::context-id]))) 207 | :ret 208 | (s/keys 209 | :req-un 210 | [::realtime-data])) 211 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/core.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.core 2 | (:require [clj-chrome-devtools.impl.connection :as connection])) 3 | 4 | 5 | (def current-connection connection/current-connection) 6 | 7 | (defn set-current-connection! 8 | "Set the globally used current connection" 9 | [c] 10 | (reset! connection/current-connection c)) 11 | 12 | (def connect connection/connect) 13 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/events.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.events 2 | "Listening to events coming from Chrome." 3 | (:require [clj-chrome-devtools.impl.connection :as connection])) 4 | 5 | (def default-wait-ms 30000) 6 | 7 | (defn listen 8 | "Subscribe to listen to given domain event. Both domain and event are keywords. 9 | Returns a 0-arity function that removes the listener when invoked." 10 | ([domain event callback] 11 | (listen (connection/get-current-connection) domain event callback)) 12 | ([connection domain event callback] 13 | (connection/listen connection domain event callback))) 14 | 15 | (defn listen-once 16 | "Synchronously listen for an event once, remove the listener and return the event." 17 | ([domain event] 18 | (listen-once (connection/get-current-connection) domain event)) 19 | ([connection domain event] 20 | (let [p (promise) 21 | unlisten (listen connection domain event #(deliver p %)) 22 | val @p] 23 | (unlisten) 24 | val))) 25 | 26 | (defn unlisten 27 | "Remove the listening callback from the subscribers of the specified 28 | domain event type." 29 | ([domain event callback] 30 | (unlisten (connection/get-current-connection) domain event callback)) 31 | ([connection domain event callback] 32 | (connection/unlisten connection domain event callback))) 33 | 34 | (defn wait-for-event 35 | "Synchronously wait for an event with specific parameters." 36 | ([domain event params] 37 | (wait-for-event (connection/get-current-connection) domain event params)) 38 | ([connection domain event params] 39 | (wait-for-event connection domain event params default-wait-ms)) 40 | ([connection domain event params timeout-ms] 41 | (wait-for-event connection domain event params timeout-ms nil)) 42 | ([connection domain event params timeout-ms after-attach-listener-fn] 43 | (let [p (promise) 44 | callback #(when (= params (select-keys (:params %) (keys params))) 45 | (deliver p (:params %))) 46 | unlisten (listen connection domain event callback) 47 | _ (when after-attach-listener-fn 48 | (after-attach-listener-fn)) 49 | val (deref p timeout-ms ::timeout)] 50 | (unlisten) 51 | (if (= val ::timeout) 52 | (throw (ex-info "Timeout! Specified event was not received." 53 | {:domain domain :event event :params params 54 | :timeout timeout-ms})) 55 | val)))) 56 | 57 | (defmacro with-event-wait 58 | "Execute body that causes some later events and wait for the event before returning. 59 | If the event is not received after waiting timeout-ms milliseconds, an exception is thrown." 60 | ([domain event body] 61 | `(with-event-wait (connection/get-current-connection) ~domain ~event ~body)) 62 | ([connection domain event body] 63 | `(with-event-wait ~connection ~domain ~event default-wait-ms ~body)) 64 | ([connection domain event timeout-ms body] 65 | `(wait-for-event ~connection ~domain ~event {} ~timeout-ms 66 | (fn [] 67 | ~body)))) 68 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/impl/command.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.impl.command 2 | "Implementation utils for commands" 3 | (:require [clojure.set :as set] 4 | [clj-chrome-devtools.impl.connection :as connection])) 5 | 6 | (defonce command-id (atom 0)) 7 | 8 | (defn next-command-id! [] 9 | (swap! command-id inc)) 10 | 11 | (defn command-payload [id name params parameter-names] 12 | {:id id 13 | :method name 14 | :params (set/rename-keys params parameter-names)}) 15 | 16 | (defn command [connection domain name params param-names] 17 | (let [id (next-command-id!) 18 | method (str domain "." name) 19 | payload (command-payload id method params param-names) 20 | result (connection/send-command connection payload id)] 21 | (if-let [error (:error result)] 22 | (throw (ex-info (str "Error in command " method ": " (:message error)) 23 | {:request payload 24 | :error error})) 25 | (:result result)))) 26 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/impl/connection.cljc: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.impl.connection 2 | "The remote debugging WebSocket connection" 3 | (:require [cheshire.core :as cheshire] 4 | [java-http-clj.core :as http] 5 | [java-http-clj.websocket :as ws] 6 | [clj-chrome-devtools.impl.util :refer [camel->clojure]] 7 | [clojure.string :as str] 8 | [clojure.tools.logging :as log]) 9 | (:import (java.util.concurrent 10 | #?@(:bb [Executors] 11 | :clj [Executors ExecutorService])) 12 | (java.net.http WebSocket))) 13 | 14 | (set! *warn-on-reflection* true) 15 | 16 | (def ^:private executor (Executors/newCachedThreadPool)) 17 | 18 | 19 | (defn- submit! [^Runnable func] 20 | (.submit #?(:bb executor 21 | :clj ^ExecutorService executor) func)) 22 | 23 | (defrecord Connection [ws-connection requests event-listeners] 24 | #?@(:bb [] ;; babashka doesn't support implementing interfaces at this time 25 | :clj [java.lang.AutoCloseable 26 | (close [{ws-conn :ws-connection}] 27 | (when ws-conn 28 | (ws/close ws-conn)))])) 29 | 30 | (defn connection? [c] 31 | (instance? Connection c)) 32 | 33 | (defonce current-connection (atom nil)) 34 | 35 | (defn get-current-connection [] 36 | (let [c @current-connection] 37 | (assert c "No current Chrome Devtools connection!") 38 | c)) 39 | 40 | (defn- parse-json [string] 41 | (cheshire/parse-string string (comp keyword camel->clojure))) 42 | 43 | (defn- publish-event [event-listeners msg] 44 | (let [[domain event :as event-key] 45 | (map (comp keyword camel->clojure) 46 | (str/split (:method msg) #"\.")) 47 | listeners (get event-listeners event-key)] 48 | (when (seq listeners) 49 | (let [event {:domain domain 50 | :event event 51 | :params (:params msg)}] 52 | (submit! #(doseq [listener listeners] 53 | (listener event))))))) 54 | 55 | (defn unlisten [connection domain event callback] 56 | (swap! (:event-listeners connection) update [domain event] 57 | #(disj (or % #{}) callback))) 58 | 59 | (defn listen 60 | "Listen to an event in the specific domain. Invokes callback when an event is 61 | received. 62 | 63 | Returns a 0-arity function that will remove the listener when invoked." 64 | [connection domain event callback] 65 | 66 | (swap! (:event-listeners connection) update [domain event] 67 | #(conj (or % #{}) callback)) 68 | #(unlisten connection domain event callback)) 69 | 70 | 71 | (defn- on-receive-text [requests event-listeners-atom] 72 | (let [builder (StringBuilder.)] 73 | (fn [_ws msg last?] 74 | (.append builder msg) 75 | (when last? 76 | (try 77 | (let [{id :id :as json-msg} (parse-json (str builder))] 78 | (if id 79 | ;; Has id: this is a response to our previously sent command 80 | (let [callback (@requests id)] 81 | (swap! requests dissoc id) 82 | (submit! #(callback json-msg))) 83 | ;; This is an event 84 | (publish-event @event-listeners-atom json-msg))) 85 | (catch Throwable t 86 | (log/error "Exception in devtools WebSocket receive, msg: " msg 87 | ", throwable: " t)) 88 | (finally 89 | (.setLength builder 0))))))) 90 | 91 | (defn- on-close 92 | [_ws code reason] 93 | (log/info "WebSocket connection closed with status code and reason:" code reason)) 94 | 95 | (defn- wait-for-remote-debugging-port [host port max-wait-time-ms] 96 | (let [wait-until (+ (System/currentTimeMillis) max-wait-time-ms) 97 | url (str "http://" host ":" port "/json/version") 98 | get! #(try 99 | (http/get url) 100 | (catch Exception e 101 | (log/trace "Exception while waiting for remote debugging port request" e) 102 | nil))] 103 | (loop [response (get!)] 104 | (cond 105 | (= (:status response) 200) 106 | :ok 107 | 108 | (> (System/currentTimeMillis) wait-until) 109 | (throw (ex-info "Chrome remote debugging port not up" 110 | {:host host :port port 111 | :max-wait-time-ms max-wait-time-ms})) 112 | 113 | :else 114 | (do (Thread/sleep 100) 115 | (recur (get!))))))) 116 | 117 | (defn inspectable-pages 118 | "Collect the list of inspectable pages returned by the DevTools protocol." 119 | ([host port] 120 | (inspectable-pages host port 1000)) 121 | ([host port max-wait-time-ms] 122 | (wait-for-remote-debugging-port host port max-wait-time-ms) 123 | (let [response (http/get (str "http://" host ":" port "/json/list"))] 124 | (some->> response 125 | :body 126 | parse-json)))) 127 | 128 | (defn- first-inspectable-page [pages] 129 | (or (some->> pages 130 | (filter (comp #{"page"} :type)) 131 | (keep :web-socket-debugger-url) 132 | first) 133 | (throw (ex-info "No debuggable pages found. In headless mode, Chrome requires a :url-to-open parameter in options." 134 | {:pages pages})))) 135 | 136 | #_(defn make-ws-client 137 | "Constructs ws client. Idle timeout defaults to 0, which means keep it 138 | alive for the session. The `max-msg-size-mb` defaults to 1MB." 139 | [ & [{:keys [idle-timeout max-msg-size-mb] 140 | :or {idle-timeout 0 141 | max-msg-size-mb (* 1024 1024)}}]] 142 | (let [client (ws/client)] 143 | (doto (.getPolicy client) 144 | (.setIdleTimeout idle-timeout) 145 | (.setMaxTextMessageSize max-msg-size-mb)) 146 | client)) 147 | 148 | (def ^:private 149 | default-ws-handlers 150 | {:on-binary (fn [& args] (log/error "Should not receive binary messages, args: " args)) 151 | :on-error (fn [& args] 152 | (log/error 153 | "Error in devtools WebSocket connection; throwable:" args)) 154 | :on-close on-close 155 | :on-open (fn [^WebSocket ws] 156 | (log/info "Inspector WebSocket connected: " ws) 157 | (.request ws 1)) 158 | :on-ping (fn [^WebSocket ws _msg] 159 | (.request ws 1) 160 | nil) 161 | :on-pong (fn [^WebSocket ws _msg] 162 | (.request ws 1) 163 | nil)}) 164 | 165 | (defn connect-url 166 | "Establish a websocket connection to web-socket-debugger-url, as given by 167 | inspectable-pages." 168 | [web-socket-debugger-url & [ws-builder-opts]] 169 | (assert web-socket-debugger-url) 170 | (let [;; Request id to callback 171 | requests (atom {}) 172 | 173 | ;; Event listeners by event domain and type 174 | event-listeners (atom {})] 175 | 176 | (->Connection 177 | (ws/build-websocket web-socket-debugger-url 178 | (merge 179 | default-ws-handlers 180 | {:on-text (on-receive-text requests event-listeners)}) 181 | (or ws-builder-opts {})) 182 | requests 183 | event-listeners))) 184 | 185 | (defn connect 186 | "Establish a websocket connection to the DevTools protocol at host:port. 187 | Selects the first inspectable page returned; to attach to a specific page, 188 | see connect-url. Initial connection will be retried for up to 189 | max-wait-time-ms milliseconds (default 1000) before giving up." 190 | ([] 191 | (connect "localhost" 9222)) 192 | ([host port] 193 | (connect host port 1000)) 194 | ([host port max-wait-time-ms] 195 | (connect host port max-wait-time-ms {})) 196 | ([host port max-wait-time-ms ws-builder-options] 197 | (when max-wait-time-ms 198 | (wait-for-remote-debugging-port host port max-wait-time-ms)) 199 | (let [url (-> (inspectable-pages host port) 200 | (first-inspectable-page))] 201 | (connect-url url ws-builder-options)))) 202 | 203 | (defn send-command-async 204 | "Send command and return a promise for its result." 205 | [{requests :requests con :ws-connection} payload id] 206 | (let [p (promise)] 207 | (swap! requests assoc id #(deliver p %)) 208 | (ws/send con (cheshire/encode payload)) 209 | p)) 210 | 211 | (defn send-command 212 | "Send command synchronously. Blocks until result is done and returns it." 213 | [connection payload id] 214 | @(send-command-async connection payload id)) 215 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/impl/define.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.impl.define 2 | "Macro for defining a domain of commands." 3 | (:require [clj-chrome-devtools.protocol-definitions :as proto] 4 | [clj-chrome-devtools.impl.connection :as connection] 5 | [clojure.string :as str] 6 | [clj-chrome-devtools.impl.util :refer [camel->clojure]] 7 | [clojure.spec.alpha :as s] 8 | [clojure.pprint :as pprint])) 9 | 10 | 11 | (def to-symbol (comp symbol camel->clojure)) 12 | 13 | (defn process-doc [doc] 14 | (some-> doc 15 | (str/replace #"" "`") 16 | (str/replace #"" "`"))) 17 | 18 | (defn- describe-map-keys [ks] 19 | (let [max-key-length (reduce max 0 (map (comp count camel->clojure :name) ks))] 20 | (str "\n\n Key " (str/join (repeat (- max-key-length 2) " ")) "| Description " 21 | "\n " (str/join (repeat (+ 2 max-key-length) "-")) "|------------ \n" 22 | (str/join "\n" 23 | (map #(str (format (str " :%-" max-key-length "s | %s") 24 | (camel->clojure (:name %)) 25 | (process-doc (:description %))) 26 | (when (:optional %) 27 | " (optional)")) 28 | ks))))) 29 | 30 | (def kw (comp keyword camel->clojure)) 31 | 32 | (defn ns-kw [string] 33 | (keyword (str *ns*) (camel->clojure string))) 34 | 35 | (defn keys-spec [properties] 36 | (let [{opt-un true req-un false} (group-by (comp boolean :optional) properties)] 37 | `(s/keys ~@(when-not (empty? req-un) 38 | `(:req-un [~@(map (comp ns-kw :name) req-un)])) 39 | ~@(when-not (empty? opt-un) 40 | `(:opt-un [~@(map (comp ns-kw :name) opt-un)]))))) 41 | 42 | (defn spec-for-type [{type :type :as t}] 43 | (if (:enum t) 44 | ;; Enumerated type, just output the set of valid values 45 | (into #{} (:enum t)) 46 | 47 | (case type 48 | "object" (keys-spec (:properties t)) 49 | "string" `string? 50 | "integer" `integer? 51 | "number" `number? 52 | "boolean" `boolean? 53 | "array" `(s/coll-of ~(spec-for-type (:items t))) 54 | `any?))) 55 | 56 | (defmacro define-type-specs [domain] 57 | `(do 58 | ~@(for [{id :id type :type :as t} (proto/types-for-domain domain) 59 | :let [name-kw (ns-kw id)]] 60 | 61 | `(do 62 | ;; If this is an object, create specs for basic type keys 63 | ~@(for [{name :name :as property-type} (:properties t) 64 | :when (#{"string" "number" "integer" "boolean" "array"} type)] 65 | `(s/def ~(ns-kw name) 66 | ~(spec-for-type property-type))) 67 | (s/def ~name-kw ~(spec-for-type t)))))) 68 | 69 | (defn generate-type-specs [domain] 70 | (mapcat 71 | (fn [{id :id type :type :as t}] 72 | (let [name-kw (ns-kw id)] 73 | (concat 74 | ;; If this is an object, create specs for basic type keys 75 | (for [{name :name :as property-type} (:properties t) 76 | :when (#{"string" "number" "integer" "boolean" "array"} type)] 77 | `(s/def ~(ns-kw name) 78 | ~(spec-for-type property-type))) 79 | [`(s/def ~name-kw ~(spec-for-type t))]))) 80 | (proto/types-for-domain domain))) 81 | 82 | (defmacro define-command-functions [domain] 83 | `(do 84 | ~@(for [{:keys [name description parameters returns]} (proto/commands-for-domain domain) 85 | :let [fn-name (to-symbol name) 86 | params (mapv (comp to-symbol :name) parameters) 87 | param-names (zipmap (map (comp keyword camel->clojure :name) parameters) 88 | (map :name parameters)) 89 | [required-params optional-params] (split-with (comp not :optional) params)]] 90 | `(do 91 | (defn ~fn-name 92 | ~(str (process-doc description) 93 | (when-not (empty? parameters) 94 | (str "\n\nParameters map keys:\n" 95 | (describe-map-keys parameters))) 96 | (when-not (empty? returns) 97 | (str "\n\nReturn map keys:\n" 98 | (describe-map-keys returns)))) 99 | ([] (~(to-symbol name) (connection/get-current-connection) {})) 100 | ([{:keys [~@params] :as ~'params}] 101 | (~(to-symbol name) (connection/get-current-connection) ~'params)) 102 | ([~'connection {:keys [~@params] :as ~'params}] 103 | (let [id# (next-command-id!) 104 | method# ~(str domain "." name) 105 | payload# (command-payload id# method# ~'params 106 | ~param-names) 107 | result (connection/send-command ~'connection payload# id#)] 108 | (if-let [error# (:error result#)] 109 | (throw (ex-info (str "Error in command " method# ": " (:message error#)) 110 | {:request payload# 111 | :error error#})) 112 | (:result result#))))) 113 | (s/fdef ~fn-name 114 | :args (s/or :no-args 115 | (s/cat) 116 | 117 | :just-params 118 | (s/cat :params ~(keys-spec parameters)) 119 | 120 | :connection-and-params 121 | (s/cat 122 | :connection (s/? connection/connection?) 123 | :params ~(keys-spec parameters))) 124 | :ret ~(keys-spec returns)))))) 125 | 126 | (defn generate-command-functions [domain] 127 | (mapcat 128 | (fn [{:keys [name description parameters returns]}] 129 | (let [fn-name (to-symbol name) 130 | params (mapv (comp to-symbol :name) parameters) 131 | param-names (zipmap (map (comp keyword camel->clojure :name) parameters) 132 | (map :name parameters)) 133 | [required-params optional-params] (split-with (comp not :optional) params)] 134 | [`(defn ~fn-name 135 | ~(str (process-doc description) 136 | (when-not (empty? parameters) 137 | (str "\n\nParameters map keys:\n" 138 | (describe-map-keys parameters))) 139 | (when-not (empty? returns) 140 | (str "\n\nReturn map keys:\n" 141 | (describe-map-keys returns)))) 142 | ([] (~(to-symbol name) (connection/get-current-connection) {})) 143 | ([{:keys [~@params] :as ~'params}] 144 | (~(to-symbol name) (connection/get-current-connection) ~'params)) 145 | ([~'connection {:keys [~@params] :as ~'params}] 146 | (clj-chrome-devtools.impl.command/command 147 | ~'connection ~domain ~name ~'params ~param-names))) 148 | `(s/fdef ~fn-name 149 | :args (s/or :no-args 150 | (s/cat) 151 | 152 | :just-params 153 | (s/cat :params ~(keys-spec parameters)) 154 | 155 | :connection-and-params 156 | (s/cat 157 | :connection (s/? connection/connection?) 158 | :params ~(keys-spec parameters))) 159 | :ret ~(keys-spec returns))])) 160 | (proto/commands-for-domain domain))) 161 | 162 | (defn call-in-ns [ns fun] 163 | (let [old-ns (ns-name *ns*)] 164 | (in-ns ns) 165 | (let [ret (fun)] 166 | (in-ns old-ns) 167 | ret))) 168 | 169 | (defn pretty-print-code [the-ns code] 170 | (str/join "\n" 171 | (map #(-> (with-out-str 172 | (pprint/pprint %)) 173 | 174 | ;; Make ns references pretties 175 | (str/replace "clojure.spec.alpha/" "s/") 176 | (str/replace "clj-chrome-devtools.impl.connection/" "c/") 177 | (str/replace "clj-chrome-devtools.impl.command/" "cmd/") 178 | (str/replace "clojure.core/" "") 179 | 180 | ;; Use same ns keywords 181 | (str/replace (str ":" the-ns "/") 182 | (str "::"))) code))) 183 | 184 | #_(defmacro define-event-handlers [domain] 185 | `(do 186 | ~@(for [{:keys [name parameters description] :as event} (proto/events-for-domain domain) 187 | :let [fn-name (symbol (str "on-" (camel->clojure name)))]] 188 | `(defn ~fn-name 189 | ~(str "Register handler for " name " event.\n" description "\n\n" 190 | (when-not (empty? parameters) 191 | (str "The handler will be called with a map with the following keys: " 192 | (describe-map-keys parameters))) 193 | "\n\nReturns a 0 arity function that will remove this handler when called.") 194 | [connection# handler#] 195 | )))) 196 | 197 | 198 | (defmacro define-domain [domain] 199 | `(do 200 | (define-type-specs ~domain) 201 | (define-command-functions ~domain) 202 | #_(define-event-handlers domain))) 203 | 204 | (defn -main [& args] 205 | (doseq [{:keys [domain description]} (proto/all-domains)] 206 | (let [clj-name (camel->clojure domain) 207 | file-name (str "src/clj_chrome_devtools/commands/" (str/lower-case (str/replace clj-name "-" "_")) ".clj") 208 | ns-name (str "clj-chrome-devtools.commands." (str/lower-case clj-name)) 209 | ns-sym (symbol ns-name)] 210 | (println "Generate command namespace: " file-name) 211 | (spit file-name 212 | (str "(ns clj-chrome-devtools.commands." (str/lower-case clj-name) "\n" 213 | (when description 214 | (str " " (pr-str (process-doc description)) "\n")) 215 | " (:require [clojure.spec.alpha :as s]\n" 216 | " [clj-chrome-devtools.impl.command :as cmd]\n" 217 | " [clj-chrome-devtools.impl.connection :as c]))\n\n" 218 | (pretty-print-code ns-name (call-in-ns ns-sym #(generate-type-specs domain))) 219 | (pretty-print-code ns-name (call-in-ns ns-sym #(generate-command-functions domain)))))))) 220 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/impl/util.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.impl.util 2 | (:require [clojure.string :as str])) 3 | 4 | (defn camel->clojure [string] 5 | (-> string 6 | ;; borrowed from r0man/inflections 7 | ;; ------------------------------- 8 | ;; consoleAPICalled -> console-api-called 9 | (str/replace #"([A-Z]+)([A-Z][a-z])" "$1-$2") 10 | ;; console1APICalled -> console1-api-called 11 | (str/replace #"([a-z\d])([A-Z])" "$1-$2") 12 | ;; console api called -> console-api-called 13 | (str/replace #"\s+" "-") 14 | ;; ;; console_api_called -> console-api-called 15 | (str/replace #"_" "-") 16 | ;; Lower case everything 17 | (str/lower-case))) 18 | 19 | (defn random-free-port [] 20 | (let [s (doto (java.net.ServerSocket. 0) 21 | (.setReuseAddress true))] 22 | (try 23 | (.getLocalPort s) 24 | (finally (.close s))))) 25 | -------------------------------------------------------------------------------- /src/clj_chrome_devtools/protocol_definitions.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.protocol-definitions 2 | "Loads CDP protocol definition JSON files for consumption by def macros." 3 | (:require [cheshire.core :as cheshire])) 4 | 5 | (defn- load-json [json-file] 6 | (as-> json-file it 7 | (str "devtools-protocol/json/" it) 8 | (slurp it) 9 | (cheshire/parse-string it true))) 10 | 11 | (def protocol-files ["browser_protocol.json" "js_protocol.json"]) 12 | 13 | (defn all-domains [] 14 | (mapcat (comp :domains load-json) protocol-files)) 15 | 16 | (defn domain-by-name [domain] 17 | (some #(when (= (:domain %) domain) %) (all-domains))) 18 | 19 | (defn commands-for-domain [domain] 20 | (:commands (domain-by-name domain))) 21 | 22 | (defn types-for-domain [domain] 23 | (:types (domain-by-name domain))) 24 | 25 | (defn events-for-domain [domain] 26 | (:events (domain-by-name domain))) 27 | 28 | (defn domains [] 29 | (into #{} 30 | (map :domain) 31 | (all-domains))) 32 | -------------------------------------------------------------------------------- /test/clj_chrome_devtools/automation_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.automation-test 2 | (:require [clj-chrome-devtools.automation :as a] 3 | [clj-chrome-devtools.automation.fixture :refer [create-chrome-fixture]] 4 | [clojure.spec.test.alpha :as stest] 5 | [clojure.test :as t :refer [deftest is testing]] 6 | [clojure.string :as str] 7 | [clojure.java.io :as io])) 8 | 9 | (stest/instrument) 10 | 11 | (defonce chrome-fixture (create-chrome-fixture {:url-to-open (str (io/resource "test-page.html"))})) 12 | (t/use-fixtures :each chrome-fixture) 13 | 14 | (deftest evaluate 15 | (testing "a call that takes longer than the default timeout should throw an exception" 16 | (let [[prior-default-timeout _] (reset-vals! a/default-timeout-ms 100)] 17 | (is (thrown-with-msg? 18 | RuntimeException 19 | #"^Timeout!.+" 20 | (a/evaluate "Array(1024 ** 3).fill().map(Math.random).length"))) 21 | (reset! a/default-timeout-ms prior-default-timeout))) 22 | (testing "a call that takes longer than the supplied timeout should throw an exception" 23 | (is (thrown-with-msg? 24 | RuntimeException 25 | #"^Timeout!.+" 26 | (a/evaluate @a/current-automation 27 | "Array(1024 ** 3).fill().map(Math.random).length" 28 | 100))))) 29 | 30 | 31 | (deftest pdf-print-test 32 | (a/to "http://example.com") 33 | (a/print-pdf @a/current-automation "example.pdf") 34 | (let [pdf (slurp "example.pdf")] 35 | (is (str/starts-with? pdf "%PDF-1.4")) ; file exists and starts with proper PDF header 36 | (is (> (count pdf) 30000)))) ; file should be little over 30k 37 | -------------------------------------------------------------------------------- /test/clj_chrome_devtools/chrome_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.chrome-test 2 | "Some tests with an actual running Chrome" 3 | (:require [clj-chrome-devtools.automation.fixture :refer [create-chrome-fixture]] 4 | [clj-chrome-devtools.automation :refer :all] 5 | [clojure.test :as t :refer [deftest is testing]] 6 | [clojure.java.io :as io] 7 | [clojure.spec.test.alpha :as stest])) 8 | 9 | (stest/instrument) 10 | 11 | (def test-page 12 | (io/resource "test-page.html")) 13 | 14 | (defonce chrome-fixture (create-chrome-fixture {:url-to-open (str test-page)})) 15 | (t/use-fixtures :each chrome-fixture) 16 | 17 | (deftest simple-page-load 18 | (to test-page) 19 | (is (= (map text-of (sel "ul#thelist li")) '("foo" "bar" "baz")))) 20 | 21 | (deftest selectors 22 | (to test-page) 23 | (testing "Selectors work in place of node-refences" 24 | (is (= "0" (text-of "#counter"))) 25 | (click [:div.countertest :button.increment]) 26 | (is (= "1" (text-of [:#counter]))) 27 | (click (sel1 "button.increment")) 28 | (is (= "2" (text-of "div#counter"))) 29 | (double-click "button.reset") 30 | (is (= "0" (text-of [:div#counter]))))) 31 | 32 | (deftest input 33 | (to test-page) 34 | (testing "Typing into an input field works" 35 | (is (= "NO GREETING YET" (text-of "#greeting"))) 36 | (doseq [txt ["old friend" 37 | "foo@bar" 38 | "keys [ that require $ modifiers"]] 39 | (input-text [:#greeter :input] txt) 40 | (click "#greeter button") 41 | (is (= (str "Hello, " txt "!") (text-of [:#greeting]))) 42 | (clear-text-input [:#greeter :input])))) 43 | -------------------------------------------------------------------------------- /test/clj_chrome_devtools/domain_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.domain-test 2 | "Test that all namespaces can be required and have public vars." 3 | (:require [clojure.test :as t :refer [deftest is]])) 4 | 5 | (def domains '[clj-chrome-devtools.commands.accessibility 6 | clj-chrome-devtools.commands.animation 7 | clj-chrome-devtools.commands.application-cache 8 | clj-chrome-devtools.commands.audits 9 | clj-chrome-devtools.commands.browser 10 | clj-chrome-devtools.commands.cache-storage 11 | clj-chrome-devtools.commands.console 12 | clj-chrome-devtools.commands.css 13 | clj-chrome-devtools.commands.database 14 | clj-chrome-devtools.commands.debugger 15 | clj-chrome-devtools.commands.device-orientation 16 | clj-chrome-devtools.commands.dom 17 | clj-chrome-devtools.commands.dom-debugger 18 | clj-chrome-devtools.commands.dom-snapshot 19 | clj-chrome-devtools.commands.dom-storage 20 | clj-chrome-devtools.commands.emulation 21 | clj-chrome-devtools.commands.heap-profiler 22 | clj-chrome-devtools.commands.indexed-db 23 | clj-chrome-devtools.commands.input 24 | clj-chrome-devtools.commands.inspector 25 | clj-chrome-devtools.commands.io 26 | clj-chrome-devtools.commands.layer-tree 27 | clj-chrome-devtools.commands.log 28 | clj-chrome-devtools.commands.memory 29 | clj-chrome-devtools.commands.network 30 | clj-chrome-devtools.commands.overlay 31 | clj-chrome-devtools.commands.page 32 | clj-chrome-devtools.commands.performance 33 | clj-chrome-devtools.commands.profiler 34 | clj-chrome-devtools.commands.runtime 35 | clj-chrome-devtools.commands.schema 36 | clj-chrome-devtools.commands.security 37 | clj-chrome-devtools.commands.service-worker 38 | clj-chrome-devtools.commands.storage 39 | clj-chrome-devtools.commands.system-info 40 | clj-chrome-devtools.commands.target 41 | clj-chrome-devtools.commands.tethering 42 | clj-chrome-devtools.commands.tracing]) 43 | 44 | (deftest require-domain-namespaces 45 | (doseq [ns domains] 46 | (require ns) 47 | (is (>= (count (ns-publics ns)) 1)))) 48 | -------------------------------------------------------------------------------- /test/clj_chrome_devtools/impl/connection_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-chrome-devtools.impl.connection-test 2 | (:require [clj-chrome-devtools.automation :refer [create-automation sel text-of to]] 3 | [clj-chrome-devtools.automation.fixture :refer [create-chrome-fixture]] 4 | [clj-chrome-devtools.impl.connection :as c] 5 | [clj-chrome-devtools.impl.util :as util] 6 | [clojure.java.io :as io] 7 | [clojure.spec.test.alpha :as stest] 8 | [clojure.test :as t :refer [deftest is testing]]) 9 | (:import [java.util.concurrent CompletionException])) 10 | 11 | (stest/instrument) 12 | 13 | ;; We need to start Chrome up listening for devtools connections on a random free port, and then 14 | ;; pass the same port number to c/connect. 15 | (defonce port (util/random-free-port)) 16 | 17 | (defonce chrome-fixture (create-chrome-fixture {:remote-debugging-port port 18 | :url-to-open (io/resource "test-page.html")})) 19 | (t/use-fixtures :each chrome-fixture) 20 | 21 | (defn- make-conn 22 | "The main reason we need this is so it’ll use the value of the port var. See the comment on that 23 | var above." 24 | [] 25 | (c/connect "localhost" port)) 26 | 27 | 28 | (def test-page 29 | (io/resource "test-page.html")) 30 | 31 | (deftest closeable 32 | (testing "Connection objects should work with (be closed by) with-open" 33 | (let [conn (make-conn) 34 | auto (create-automation conn)] 35 | (with-open [_ conn] 36 | ;; Simple validation that the connection works 37 | (to auto test-page) 38 | (is (= (map #(text-of auto %) 39 | (sel auto "ul#thelist li")) 40 | '("foo" "bar" "baz")))) 41 | (is (thrown-with-msg? 42 | CompletionException #"Output closed" 43 | (to auto test-page)))))) 44 | -------------------------------------------------------------------------------- /test/test-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | clj-chrome-devtools test page 4 | 18 | 19 | 20 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
idname
1Foo Barsky
2Alan Smithee
44 |
45 | 46 |
47 |
0
48 | 49 | 50 |
51 | 52 |
53 | 54 | 55 |
NO GREETING YET
56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /update-devtools-protocol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | git clone https://github.com/ChromeDevTools/devtools-protocol 4 | 5 | clojure -m clj-chrome-devtools.impl.define 6 | 7 | rm -rf devtools-protocol 8 | --------------------------------------------------------------------------------