diff --git a/.codex/skills/release-hyper-browser-simulator/SKILL.md b/.codex/skills/release-hyper-browser-simulator/SKILL.md index 573838e..9d662a3 100644 --- a/.codex/skills/release-hyper-browser-simulator/SKILL.md +++ b/.codex/skills/release-hyper-browser-simulator/SKILL.md @@ -18,6 +18,7 @@ Use this skill to prepare, publish, or verify releases of the Rust workspace in - Generated release workflow: `.github/workflows/release.yml`. - Local release helpers: `justfile`. - User install docs: `README.md`. +- Cloudflare worker API schema: `cloudflare-browser-simulator` git submodule, using `cli/openapi/cloudflare-browser-simulator.json`. Before making changes, check `git status --short --branch` and work with any existing dirty files without reverting unrelated user edits. @@ -30,8 +31,16 @@ Homebrew: - Release artifacts are hosted on GitHub. - Formulae are pushed to `hypervideo/homebrew-tap`. - CI needs the `HOMEBREW_TAP_TOKEN` secret on `hypervideo/browser-simulator`. +- CI and release checkouts need the `CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN` secret on `hypervideo/browser-simulator`; it must be a read-only token that can fetch this public repo and the private `hypervideo/cloudflare-browser-simulator` submodule. - Prerelease tags do not publish to Homebrew unless `publish-prereleases` is enabled. +Cloudflare worker submodule: + +- The worker deploys from its own repository. Do not deploy the worker from this repo's release flow. +- The simulator release source must pin a worker submodule commit that is reachable from GitHub, not only from a local sibling checkout. +- If the submodule pointer references a worker commit that is not pushed yet, push that worker commit first. Use a non-`main` branch if deployment should not happen; the worker repo deploy workflow deploys from `main`. +- To consume a newer worker API, update and commit the worker repo's generated `cli/openapi/cloudflare-browser-simulator.json`, then update the submodule pointer here and make any Rust client compatibility fixes. + Nix: - `flake.nix` exposes `.#hyper-client-simulator`, `.#client-simulator`, and `.#default`. @@ -48,6 +57,8 @@ Nix: git fetch --tags origin git tag --sort=-v:refname | head gh release list --repo hypervideo/browser-simulator --limit 10 +git submodule update --init --recursive +git submodule status --recursive ``` 2. Choose the next SemVer version, using a `v` prefix only for the git tag. @@ -58,7 +69,18 @@ gh release list --repo hypervideo/browser-simulator --limit 10 - `nix/packages.nix`: `version = "X.Y.Z";`. - `Cargo.lock`: refresh local package versions after the `Cargo.toml` bump. -4. Validate before tagging: +4. Confirm submodule release readiness: + +```sh +git -C cloudflare-browser-simulator status --short --branch +git -C cloudflare-browser-simulator rev-parse HEAD +git -C cloudflare-browser-simulator fetch origin --tags +git -C cloudflare-browser-simulator branch -r --contains HEAD +``` + +The submodule should be clean and its commit must be contained by an `origin/*` ref before tagging. If no remote branch or tag is printed, push the worker commit first. + +5. Validate before tagging: ```sh just fmt @@ -96,6 +118,8 @@ gh run list --repo hypervideo/browser-simulator --workflow Release --limit 10 gh release view vX.Y.Z --repo hypervideo/browser-simulator ``` +If release checkout fails while fetching `cloudflare-browser-simulator`, confirm `CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN` exists and can read the private worker repo, and confirm the pinned submodule commit has been pushed. + Check the Homebrew tap formula: ```sh diff --git a/.codex/skills/run-headless-simulator/SKILL.md b/.codex/skills/run-headless-simulator/SKILL.md new file mode 100644 index 0000000..96abc54 --- /dev/null +++ b/.codex/skills/run-headless-simulator/SKILL.md @@ -0,0 +1,150 @@ +--- +name: run-headless-simulator +description: Run hyper-client-simulator participants from the CLI without the TUI. Use for agent-friendly headless smoke tests, multi-participant runs, and backend startup checks. +--- + +# Run Headless Simulator + +## Overview + +Use the `headless` subcommand when an agent or script needs to run simulator participants without the interactive TUI. In the repo, prefer the development command through Cargo; for an installed release, call the binary directly. + +```sh +cargo run -- headless [OPTIONS] +hyper-client-simulator headless [OPTIONS] +``` + +Run from the Nix devshell when possible so Chromium, FFmpeg, native libraries, and the Rust toolchain match the repository environment. + +## Basic Usage + +Start one participant from global CLI settings: + +```sh +cargo run -- headless \ + --url https://latest.dev.hyper.video/0Y6-6FZ-20J \ + --backend local \ + --headless true \ + --audio-enabled true \ + --video-enabled true +``` + +Start one backend-only smoke test that does not require Chromium: + +```sh +cargo run -- headless \ + --url https://example.com/lite/demo \ + --participant '{"backend":"remote-stub","audio_enabled":false}' +``` + +Start multiple participants by repeating `--participant`: + +```sh +cargo run -- headless \ + --url https://latest.dev.hyper.video/F27-T5F-DXY \ + --participant '{"backend":"local","audio_enabled":false}' \ + --participant '{"backend":"cloudflare","audio_enabled":true}' +``` + +## Configuration Model + +- Global CLI flags apply to the base config loaded by the simulator. +- If no `--participant` values are passed, `headless` starts one participant from the effective global config. +- Each repeated `--participant` JSON value clones the effective global config and applies only that participant's overrides. +- CLI overrides and participant JSON overrides are in-memory only; `headless` does not persist them back to the config file. +- Logs print through normal tracing output rather than the TUI logger. +- First `Ctrl-C` requests shutdown and waits for participants to stop; a second `Ctrl-C` exits with code `130`. + +## Options + +Global CLI option names use kebab-case: + +- `--url URL` +- `--backend BACKEND` +- `--headless true|false` +- `--audio-enabled true|false` +- `--video-enabled true|false` +- `--screenshare-enabled true|false` +- `--auto-gain-control true|false` +- `--noise-suppression MODEL` +- `--transport MODE` +- `--video-constraint-publish-webcam CONSTRAINT` +- `--video-constraint-subscribe CONSTRAINT` +- `--video-max-concurrent-tracks TRACKS` +- `--blur true|false` +- `--participant JSON` + +Participant JSON field names use snake_case: + +- `url` +- `backend` +- `headless` +- `audio_enabled` +- `video_enabled` +- `screenshare_enabled` +- `auto_gain_control` +- `noise_suppression` +- `transport` +- `video_constraint_publish_webcam` +- `video_constraint_subscribe` +- `video_max_concurrent_tracks` +- `blur` + +Participant JSON denies unknown fields. Treat typos as command errors, not ignored settings. + +## Values + +Backend values: + +- `local` +- `cloudflare` +- `remote-stub` +- `aws-device-farm` + +Transport values: + +- `webtransport` +- `webrtc` + +Video constraint values: + +- `none` +- `90p` +- `144p` +- `240p` +- `360p` +- `480p` +- `720p` +- `1080p` +- `1440p` +- `2160p` + +Noise suppression values: + +- `none` +- `deepfilternet` +- `rnnoise` +- `iris-carthy` +- `krisp-high` +- `krisp-medium` +- `krisp-low` +- `krisp-high-with-bvc` +- `krisp-medium-with-bvc` +- `ai-coustics-sparrow-xxs` +- `ai-coustics-sparrow-xs` +- `ai-coustics-sparrow-s` +- `ai-coustics-sparrow-l` +- `ai-coustics-sparrow-xxs-48khz` +- `ai-coustics-sparrow-xs-48khz` +- `ai-coustics-rook-s-48khz` +- `ai-coustics-rook-l-48khz` + +Use an integer for `--video-max-concurrent-tracks` or JSON `video_max_concurrent_tracks`. In participant JSON, `null` is treated as absent and therefore inherits the global value; it does not clear a global track limit. + +## Agent Workflow + +1. Prefer `remote-stub` for a fast smoke test when browser automation is not the thing under test. +2. Use `local` for real Chromium-driven joining, and confirm the target Hyper frontend exposes the media settings used by video constraints. +3. Put common settings in global CLI flags, then use repeated `--participant` JSON for per-participant differences. +4. Keep JSON compact and quote it with single quotes in shell commands. +5. Check `cargo run -- headless --help` or `hyper-client-simulator headless --help` if the command rejects a flag. diff --git a/.codex/skills/run-headless-simulator/agents/openai.yaml b/.codex/skills/run-headless-simulator/agents/openai.yaml new file mode 100644 index 0000000..d18f22a --- /dev/null +++ b/.codex/skills/run-headless-simulator/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Run Headless Simulator" + short_description: "Run simulator participants without the TUI" + default_prompt: "Use $run-headless-simulator to run hyper-client-simulator participants directly from the CLI with the headless command." diff --git a/.env.tpl b/.env.tpl new file mode 100644 index 0000000..c9fcb45 --- /dev/null +++ b/.env.tpl @@ -0,0 +1,5 @@ +# AWS device farm +DEVICE_FARM_AWS_ACCESS_KEY_ID={{ op://infra/aws-device-farm-simulator-credentials/access_key_id }} +DEVICE_FARM_AWS_SECRET_ACCESS_KEY={{ op://infra/aws-device-farm-simulator-credentials/secret_access_key }} +DEVICE_FARM_AWS_REGION=us-west-2 +DEVICE_FARM_PROJECT_ARN={{ op://infra/aws-device-farm-simulator-credentials/mgpse2ej3msjproinokciv773m }} diff --git a/.envrc b/.envrc index 78deaa5..becbf1c 100644 --- a/.envrc +++ b/.envrc @@ -1,2 +1,4 @@ export NIXPKGS_ALLOW_UNFREE=1 # for google-chrome use flake . --impure + +dotenv_if_exists .env diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 582cc52..870e090 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: pull_request: branches: ['**'] +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -41,14 +44,36 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} + private-submodule-notice: + name: Private Submodule Notice + runs-on: ubuntu-latest + timeout-minutes: 1 + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} + + steps: + - name: Explain limited fork CI + run: | + echo "Full lint/test CI is skipped for fork pull requests because this public repository depends on the private hypervideo/cloudflare-browser-simulator submodule." + echo "Run the full workflow from a trusted branch after review." + lint-and-test: name: Lint and Test runs-on: ubuntu-latest timeout-minutes: 10 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + env: + DEVICE_FARM_AWS_ACCESS_KEY_ID: ${{ secrets.DEVICE_FARM_AWS_ACCESS_KEY_ID }} + DEVICE_FARM_AWS_SECRET_ACCESS_KEY: ${{ secrets.DEVICE_FARM_AWS_SECRET_ACCESS_KEY }} + DEVICE_FARM_AWS_REGION: ${{ secrets.DEVICE_FARM_AWS_REGION }} + DEVICE_FARM_PROJECT_ARN: ${{ secrets.DEVICE_FARM_PROJECT_ARN }} steps: - name: Checkout repository uses: actions/checkout@v6.0.2 + with: + persist-credentials: false + submodules: recursive + token: ${{ secrets.CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN }} - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 32e7d76..c8745b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,6 +60,7 @@ jobs: with: persist-credentials: false submodules: recursive + token: ${{ secrets.CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN }} - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 @@ -112,6 +113,10 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json + DEVICE_FARM_AWS_ACCESS_KEY_ID: ${{ secrets.DEVICE_FARM_AWS_ACCESS_KEY_ID }} + DEVICE_FARM_AWS_SECRET_ACCESS_KEY: ${{ secrets.DEVICE_FARM_AWS_SECRET_ACCESS_KEY }} + DEVICE_FARM_AWS_REGION: ${{ secrets.DEVICE_FARM_AWS_REGION }} + DEVICE_FARM_PROJECT_ARN: ${{ secrets.DEVICE_FARM_PROJECT_ARN }} steps: - name: enable windows longpaths run: | @@ -120,6 +125,7 @@ jobs: with: persist-credentials: false submodules: recursive + token: ${{ secrets.CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN }} - name: Install Rust non-interactively if not already installed if: ${{ matrix.container }} run: | @@ -174,11 +180,16 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json + DEVICE_FARM_AWS_ACCESS_KEY_ID: ${{ secrets.DEVICE_FARM_AWS_ACCESS_KEY_ID }} + DEVICE_FARM_AWS_SECRET_ACCESS_KEY: ${{ secrets.DEVICE_FARM_AWS_SECRET_ACCESS_KEY }} + DEVICE_FARM_AWS_REGION: ${{ secrets.DEVICE_FARM_AWS_REGION }} + DEVICE_FARM_PROJECT_ARN: ${{ secrets.DEVICE_FARM_PROJECT_ARN }} steps: - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive + token: ${{ secrets.CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN }} - name: Install cached dist uses: actions/download-artifact@v7 with: @@ -229,6 +240,7 @@ jobs: with: persist-credentials: false submodules: recursive + token: ${{ secrets.CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN }} - name: Install cached dist uses: actions/download-artifact@v7 with: @@ -341,3 +353,4 @@ jobs: with: persist-credentials: false submodules: recursive + token: ${{ secrets.CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN }} diff --git a/.gitignore b/.gitignore index 99f6144..d2b1c40 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target/ tmp/ hyper_session_cookies.json /.direnv/ +/.env diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d7a723c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cloudflare-browser-simulator"] + path = cloudflare-browser-simulator + url = https://github.com/hypervideo/cloudflare-browser-simulator.git diff --git a/2026-04-16-cloudflare-driver.md b/2026-04-16-cloudflare-driver.md deleted file mode 100644 index cb3631d..0000000 --- a/2026-04-16-cloudflare-driver.md +++ /dev/null @@ -1,535 +0,0 @@ -# Plan: Cloudflare Driver For `hyper-browser-simulator` - -## Recommendation - -The generic backend seam already exists in the current codebase: - -- `ParticipantDriverSession` in `browser/src/participant/shared/runtime.rs` is the real backend contract. -- `FrontendAutomation` in `browser/src/participant/local/frontend.rs` is now local-Chromium-only. - -So the Cloudflare work should build on the current `ParticipantDriverSession` runtime instead of reviving the older concrete `FrontendDriver` idea. - -Recommended shape: - -- Keep the actual driver implementation in `client-simulator-browser`, under `browser/src/participant/cloudflare/`. -- Add one new workspace crate for the generated worker client, for example `cloudflare-worker-client/`. -- Do not put the driver implementation itself in a separate crate. That would create a dependency cycle because `client-simulator-browser` owns the `ParticipantDriverSession` trait and also needs to construct the driver. -- Reuse the existing Rust auth flow for Hyper Core. The driver should obtain or reuse the `hyper_session` cookie locally and pass the raw cookie value to the worker during session creation. -- Treat the worker's `sessionId` as a private implementation detail of the driver. Nothing above `CloudflareSession` should know about it. -- Cache authoritative state from worker responses and use a low-frequency worker state poll to implement `wait_for_termination()`. Note that the cloudflare Browser Rendering instance will terminate after its keep-alive timeout passes without inactivity. The driver and cloudflare worker needs to connect regularly to the instance to keep it alive. - -## Related Project - -The counterpart worker project lives at: - -- `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator` - -The most relevant files in that repo for this plan are: - -- worker/API entrypoint: `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/worker/src/index.ts` -- worker routes and schemas: `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/worker/src/api/` -- current worker automation logic: `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/worker/src/api/logic.ts` -- current generated-client example: `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/cli/` -- counterpart implementation plan: `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/plans/2026-04-16_hyper-browser-simulator-support.md` - -## Current State - -What is already in place: - -- The shared runtime and driver contract are implemented. -- `LocalChromiumSession` is the production local backend. -- `RemoteStubSession` is a simulated remote backend. -- `browser/src/participant/cloudflare/mod.rs` is an empty placeholder. -- `ParticipantBackendKind` only supports `local` and `remote-stub`. -- The TUI already has backend selection. - -What is missing for a real Cloudflare backend: - -- a generated Rust client for the worker API in this repo -- a `CloudflareSession` implementation of `ParticipantDriverSession` -- config for the worker base URL and request behavior -- spawn/store wiring for the new backend -- tests against a mock worker - -The cloudflare-browser-simulator repo already has a worker implementation following `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/plans/2026-04-16_hyper-browser-simulator-support.md`. You can run a local worker using the justfile commands at `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/justfile`. If the worker API is not sufficient or could be made more ergonomic for the driver, you can modify its implementation and OpenAPI spec as needed. - -## Design Decisions - -### Use a generated client crate, not hand-written HTTP - -The worker repo already treats its OpenAPI document as canonical. Mirror that pattern here. - -Recommended crate layout: - -- `cloudflare-worker-client/` - - `build.rs` - - `openapi/cloudflare-browser-simulator.json` - - `src/generated.rs` - - `src/lib.rs` - - `src/client.rs` - -Responsibilities: - -- own the committed OpenAPI copy used by this repo -- generate the Rust client with `progenitor` -- expose a thin ergonomic wrapper around the generated client -- keep `reqwest` transport, timeout, base URL, and error formatting out of `browser/` - -### Keep the Cloudflare driver in `browser/` - -Recommended files: - -- `browser/src/participant/cloudflare/mod.rs` -- `browser/src/participant/cloudflare/session.rs` -- `browser/src/participant/cloudflare/config.rs` -- `browser/src/participant/cloudflare/mapping.rs` - -Responsibilities: - -- map `ParticipantLaunchSpec` to worker create-session requests -- map `ParticipantMessage` to worker command requests -- translate worker state into `ParticipantState` -- forward worker log entries into `ParticipantLogMessage` -- own `sessionId`, cached state, and termination polling - -### Reuse the existing auth stack - -For Hyper Core: - -- use `HyperSessionCookieManger` exactly as the local backend does -- if a cookie is already available, reuse it -- otherwise fetch one with `fetch_new_cookie(base_url, username)` -- send only the cookie value to the worker - -The worker should set that cookie into the browser context before navigation. Do not duplicate the guest-auth flow inside the worker. - -### Keep backend-specific limitations at the driver boundary - -Cloudflare differs from local Chromium in a few important ways: - -- always headless -- no local user data dir -- no local fake-media files from the Rust host -- WebRTC-only in practice - -The driver should absorb those differences by: - -- logging when a setting is ignored or normalized -- exposing the actual applied state from the worker -- not changing the `Participant`, TUI command surface, or shared runtime just to model Cloudflare internals - -## Scope - -In scope for this implementation: - -- new `cloudflare` backend kind -- Hyper Core and Hyper Lite support through the worker -- full `ParticipantMessage` coverage -- accurate `ParticipantState` refreshes -- proper close and unexpected-termination handling -- generated OpenAPI client in this repo - -Explicit non-goals for v1: - -- headed Cloudflare sessions -- remote support for local fake-media files or URLs -- sharing DOM selector code across Rust and TypeScript through a new abstraction layer -- exposing worker-specific identifiers outside the driver - -## Progress Tracker - -Overall status: `phase 6 automated validation added; explicit Cloudflare keep-alive polling implemented; manual smoke validation pending` - -Cross-repo dependency: - -- This plan depends on the worker contract described in `/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator/plans/2026-04-16_hyper-browser-simulator-support.md`. - -Milestones: - -- [x] Phase 1: Freeze the worker contract and add the generated-client crate -- [x] Phase 2: Add Cloudflare backend config and spawn wiring -- [x] Phase 3: Implement `CloudflareSession` start and close -- [x] Phase 4: Implement command handling, cached state, and termination polling -- [x] Phase 5: Add TUI and UX handling for backend-specific limitations -- [ ] Phase 6: Validate with unit, integration, and manual tests - -Latest implementation note: - -- the Cloudflare driver now sends explicit `POST /sessions/{sessionId}/keep-alive` requests on its background poll loop and clamps that loop to a safe interval within the configured Browser Rendering inactivity window -- the worker-side `sessionTimeoutMs` is now treated as the Browser Rendering `keep_alive` inactivity timeout instead of a hard session lifetime, so active sessions can outlive 10 minutes when the simulator keeps sending commands - -## Detailed Plan - -### Phase 1: Freeze the worker contract and add the generated-client crate - -Goal: - -- make the worker API a typed dependency of this repo -- avoid hand-written request and response structs - -Changes: - -- add a new workspace member, for example `cloudflare-worker-client` -- add `progenitor`, `progenitor-client`, `openapiv3`, `prettyplease`, `syn`, and the runtime `reqwest` pieces needed for generated code -- copy the canonical worker spec into this repo under the new crate -- add `build.rs` to generate the client from the committed spec -- expose a small wrapper API for: - - constructing a client from base URL and timeouts - - formatting API errors into `eyre::Report` - - convenient methods for create, command, state, and close calls - -TDD steps: - -- [x] add a failing test for client construction and base URL normalization -- [x] add a failing test for worker error translation into actionable Rust errors -- [x] implement the wrapper until those tests pass - -Implemented in this phase: - -- added the `cloudflare-worker-client` workspace crate -- copied the canonical worker OpenAPI spec into `cloudflare-worker-client/openapi/cloudflare-browser-simulator.json` -- added `build.rs`-driven client generation with `progenitor` -- exposed a thin wrapper with base URL normalization, typed session helpers, and `eyre` error formatting -- added focused unit tests covering base URL normalization and actionable worker error translation - -Completion criteria: - -- `client-simulator-browser` can depend on the generated client crate without any direct OpenAPI or `reqwest` boilerplate -- the worker contract is represented by generated Rust types in this repo - -### Phase 2: Add Cloudflare backend config and spawn wiring - -Goal: - -- make Cloudflare a first-class backend selection - -Recommended config shape: - -```yaml -backend: cloudflare -cloudflare: - base_url: https://cloudflare-browser-simulator.hyper-video.workers.dev - request_timeout_seconds: 30 - session_timeout_ms: 600000 - navigation_timeout_ms: 45000 - selector_timeout_ms: 20000 - debug: false - health_poll_interval_ms: 5000 -``` - -Recommended code changes: - -- extend `ParticipantBackendKind` in `config/src/client_config.rs` with `Cloudflare` -- add a `CloudflareConfig` struct in `config/` -- update `config/src/lib.rs` and `config/src/default-config.yaml` -- update the TUI backend picker to include `cloudflare` -- replace separate `spawn_local()` and `spawn_remote_stub()` calls from the TUI with one backend-dispatching store method to stay DRY -- add `ParticipantStore::spawn(config)` or equivalent central dispatch -- add `Participant::spawn_cloudflare(...)` or equivalent internal constructor - -Recommended simplification: - -- do not add a large new TUI editor for every Cloudflare field in the first patch -- keep backend selection in the TUI -- keep advanced Cloudflare settings configurable through YAML first - -TDD steps: - -- [x] add failing config parsing tests for `backend: cloudflare` and the nested `cloudflare` block -- [x] add a failing store test proving backend dispatch reaches the Cloudflare constructor -- [x] implement the config and dispatch changes until the tests pass - -Implemented in this phase: - -- extended `ParticipantBackendKind` with `cloudflare` -- added `CloudflareConfig` in `config/` and threaded it through `Config` plus the default YAML -- updated config parsing tests to cover `backend: cloudflare` and the nested `cloudflare` block -- replaced the TUI's direct local vs remote-stub branching with `ParticipantStore::spawn(config)` backend dispatch -- added `Participant::spawn(config, ...)` and `Participant::spawn_cloudflare(...)` dispatch wiring -- added a minimal `CloudflareSession` placeholder so the Cloudflare backend path can be constructed and routed without implementing the real driver yet -- added a browser-store test proving Cloudflare backend dispatch reaches the Cloudflare constructor - -Notes: - -- the Cloudflare backend now parses and dispatches correctly, but the placeholder session still returns an explicit "not implemented yet" start error until Phase 3 - -Completion criteria: - -- the user can select `cloudflare` as a backend -- the simulator can construct a Cloudflare-backed participant session from config - -### Phase 3: Implement `CloudflareSession` start and close - -Goal: - -- create a real remote participant backend with correct lifecycle semantics - -Recommended `CloudflareSession` fields: - -- `launch_spec: ParticipantLaunchSpec` -- `cloudflare_config: CloudflareConfig` -- `log_sender: UnboundedSender` -- `session_id: Option` -- `cached_state: ParticipantState` -- auth dependencies needed to lazily obtain a cookie for Hyper Core - -`start()` should: - -- prepare the create-session request from `ParticipantLaunchSpec` -- fetch or reuse a `hyper_session` cookie for Hyper Core if needed -- call the worker create endpoint -- store `sessionId` -- initialize `cached_state` from the worker response -- forward worker log entries into the participant log channel - -`close()` should: - -- call the worker close endpoint if a session exists -- clear local session state -- be idempotent - -TDD steps: - -- [x] add a failing integration test against a mock worker for successful start -- [x] add a failing integration test for close-after-start -- [x] add a failing test for Hyper Core cookie injection into the create request -- [x] implement `start()` and `close()` until those tests pass - -Implemented in this phase: - -- added `cloudflare-worker-client` as a browser-crate dependency and used it from the Cloudflare driver lifecycle -- replaced the placeholder `CloudflareSession` start failure with real worker `create_session` and `close_session` calls -- reused an already-borrowed Hyper Core cookie when available and otherwise fetched one locally through `HyperSessionCookieManger` before sending only the raw cookie value to the worker -- mapped `ParticipantLaunchSpec` settings into the worker create request and mapped worker participant state back into Rust `ParticipantState` -- cached the initial worker state locally so the shared runtime can publish accurate state immediately after startup -- made `close()` idempotent when no worker session exists -- added a mock-HTTP lifecycle test covering guest-cookie fetch, worker create payload, initial state caching, and worker close dispatch - -Notes: - -- `handle_command()` still returns an explicit "not implemented yet" error for Cloudflare commands -- `wait_for_termination()` is still pending and will be implemented with worker state polling in Phase 4 - -Completion criteria: - -- a Cloudflare participant can be started and closed through the shared runtime -- no Cloudflare identifiers leak above the driver - -### Phase 4: Implement command handling, cached state, and termination polling - -Goal: - -- reach full runtime compatibility with the command/state contract - -Implementation notes: - -- map every `ParticipantMessage` variant to the worker command API -- update `cached_state` from worker command responses -- make `refresh_state()` return the cached authoritative state from the last worker response -- reserve explicit worker state calls for: - - termination polling - - recovery or debugging paths - -Recommended command semantics: - -- `Join` -> worker join command -- `Leave` -> worker leave command, keep the backend session alive -- `ToggleAudio` -> worker toggle audio command -- `ToggleVideo` -> worker toggle video command -- `ToggleScreenshare` -> worker toggle screenshare command -- `SetNoiseSuppression` -> worker set noise suppression command -- `SetWebcamResolutions` -> worker set webcam resolution command -- `ToggleBackgroundBlur` -> worker toggle blur command - -Termination handling: - -- spawn a background polling task after `start()` -- hit the worker state endpoint at a low frequency -- if the worker reports the session missing, closed, or failed, send a `DriverTermination` -- stop the poll cleanly during intentional close - -TDD steps: - -- [x] add one failing test per command mapping -- [x] add a failing test proving `refresh_state()` reflects command responses without extra network calls -- [x] add a failing test for unexpected termination on worker `404` or equivalent closed-session signal -- [x] implement command handling and polling until the tests pass - -Implemented in this phase: - -- mapped the full `ParticipantMessage` surface onto the worker command API and updated the Cloudflare cached state from each command response -- changed `refresh_state()` to return the in-memory cached worker state instead of making extra network calls -- added a background worker-state poller that refreshes cached state during health checks and reports unexpected worker-side session loss through `wait_for_termination()` -- stopped the poller cleanly during intentional close so the runtime can distinguish expected shutdown from backend loss -- expanded the Cloudflare driver tests to cover command payload mapping, cache-only refresh behavior, and termination reporting on worker-state failures - -Notes: - -- the driver now reaches command/state parity with the shared runtime contract -- Phase 5 is still the next unimplemented slice and will focus on backend-specific UX and limitation handling - -Completion criteria: - -- `CloudflareSession` supports the full `ParticipantMessage` surface -- runtime state remains accurate after start and every command -- unexpected worker-side session loss reaches `wait_for_termination()` - -### Phase 5: Add TUI and UX handling for backend-specific limitations - -Goal: - -- keep the UI understandable without overcomplicating it - -Recommended behavior: - -- allow all existing participant controls to remain visible -- when the backend cannot honor a local-only setting, surface that through logs and resulting state -- keep the TUI layout stable in the first iteration - -Specific decisions to encode: - -- `headless` is ignored for Cloudflare because the backend is always headless -- local fake-media file and URL selections are ignored for Cloudflare -- transport should normalize to `WebRTC`; if the user configured `WebTransport`, log the normalization and reflect `WebRTC` in state - -Optional follow-up, not required for the first implementation: - -- annotate unsupported fields in the TUI when `backend == cloudflare` - -TDD steps: - -- [x] add a failing test for transport normalization -- [x] add a failing test for ignored fake-media settings producing a log entry -- [x] implement the minimal UX behavior needed for those tests - -Implemented in this phase: - -- added Cloudflare-only launch-option handling so the driver can reason about headless and fake-media selections without introducing TUI-specific branching into the shared runtime -- normalized Cloudflare create-session requests to `WebRTC` when the user configured `WebTransport`, and emitted a warning log explaining the normalization -- added warning logs when the Cloudflare backend is asked to honor `headless: false` or a local fake-media file/URL selection that the worker cannot use -- kept the existing TUI layout and controls unchanged so backend-specific behavior stays visible through logs and resulting participant state instead of extra UI forks -- expanded the Cloudflare driver tests to cover transport normalization plus ignored-setting log emission - -Notes: - -- the simulator still exposes the existing controls, but Cloudflare-specific limitations are now surfaced predictably at session start -- Phase 6 remains the next open slice and is limited to broader automated and manual validation - -Completion criteria: - -- backend-specific behavior is visible and predictable -- the UI does not need backend-specific branching for every participant command - -### Phase 6: Validate with unit, integration, and manual tests - -Goal: - -- verify the backend works without a real Cloudflare dependency in automated tests - -Automated tests: - -- add unit tests close to the Cloudflare mapping and config code -- add integration tests in `browser/tests/` with a mock HTTP server such as `wiremock` -- exercise the shared runtime with a real `CloudflareSession` against mocked worker responses - -Suggested automated scenarios: - -- start success for Hyper Core -- start success for Hyper Lite -- command success for every `ParticipantMessage` variant -- command failure logging without crashing the runtime -- close after leave -- unexpected termination detection -- config parsing and backend dispatch - -Manual validation: - -- `just test` -- `just clippy` -- run the worker locally and point the simulator at it through the new Cloudflare config block -- join a Hyper Core room -- join a Hyper Lite room -- exercise audio, video, screenshare, noise suppression, resolution, blur, leave, and close - -Implemented in this phase: - -- added `browser/tests/cloudflare_driver.rs` to exercise the Cloudflare backend through the public `Participant` runtime instead of the driver internals -- covered a Hyper Lite end-to-end flow where mocked worker responses drive the shared participant state through start, join, audio toggle, video toggle, and close -- covered a command-failure path to prove the shared runtime keeps the participant alive after a worker command error and can still close cleanly -- covered unexpected termination handling by letting the worker state poll fail and asserting the public participant state transitions to stopped -- covered the Hyper Core auth path through the public backend spawn flow, including guest-cookie fetch, `/api/v1/auth/me/name`, and propagation of the fetched `hyper_session` cookie into the worker create request - -Notes: - -- existing unit coverage in `browser/src/participant/cloudflare/mod.rs` and config/runtime tests remain in place; this phase adds the missing browser-level integration layer on top -- manual smoke validation against a real local worker has not been executed yet, so this phase is not marked complete - -Completion criteria: - -- automated tests cover the driver contract -- manual smoke tests pass against a real worker - -## Recommended File Changes - -Rust workspace: - -- `Cargo.toml` -- `browser/Cargo.toml` -- `config/src/client_config.rs` -- `config/src/lib.rs` -- `config/src/default-config.yaml` -- `browser/src/participant/mod.rs` -- `browser/src/participant/shared/store.rs` -- `browser/src/participant/cloudflare/mod.rs` -- `tui/src/tui/components/browser_start.rs` - -New files and directories: - -- `cloudflare-worker-client/Cargo.toml` -- `cloudflare-worker-client/build.rs` -- `cloudflare-worker-client/openapi/cloudflare-browser-simulator.json` -- `cloudflare-worker-client/src/generated.rs` -- `cloudflare-worker-client/src/lib.rs` -- `cloudflare-worker-client/src/client.rs` -- `browser/tests/cloudflare_driver.rs` - -## Risks And Mitigations - -### Risk: a separate driver crate causes a dependency cycle - -Mitigation: - -- keep the generated client in a new crate -- keep the actual `CloudflareSession` in `client-simulator-browser` - -### Risk: double round-trips for every command - -Mitigation: - -- use create and command responses as authoritative state updates -- do not immediately re-fetch state after every command just because the runtime has a `refresh_state()` hook - -### Risk: Cloudflare session lifetime is shorter than local Chromium sessions - -Mitigation: - -- surface the configured keep-alive limits clearly in config and logs -- rely on the termination poller so the runtime reflects worker-side expiry promptly - -### Risk: fake-media parity becomes a scope trap - -Mitigation: - -- explicitly ship v1 with synthetic worker media only -- keep local fake-media handling local-only - -## Recommended First Patch Set - -The first implementation PR in this repo should do only this: - -1. add the generated-client crate -2. add Cloudflare config and backend selection -3. add the `CloudflareSession` skeleton with start and close only -4. add mock-worker integration tests for start and close - -That gives a reviewable base. Command parity and polish can land immediately after. diff --git a/2026-04-17_build-macos-binary.md b/2026-04-17_build-macos-binary.md deleted file mode 100644 index b1b8486..0000000 --- a/2026-04-17_build-macos-binary.md +++ /dev/null @@ -1,287 +0,0 @@ -# Plan: Build macOS Binary Distribution - -## Goal - -Set this repo up to ship prebuilt macOS binaries for colleagues without requiring a local Rust toolchain. - -Keep the implementation simple: - -- use `dist` (`cargo-dist`) to build release archives and generate the release workflow, -- make local macOS Chrome discovery work outside the Nix shell, -- run the macOS release builds on Blacksmith macOS runners. - -This plan is intentionally limited to shipping the `hyper-client-simulator` CLI for macOS. -It does not try to redesign the runtime, build a `.app` bundle, or add extra packaging formats unless `dist` gives them to us cheaply. - -## Constraints And Assumptions - -- The shipped binary still depends on a locally installed Chrome/Chromium runtime. -- The current code only discovers Chrome via `PATH`, which is not enough for normal macOS machines. -- `ffmpeg` is only needed for custom fake-media conversion. That should not block the base distribution plan. -- This repo is already a normal Rust workspace with a root binary package, which fits `dist` well. - -## Implementation Steps - -### 1. Add `dist` configuration for macOS release artifacts - -STATUS: completed - -Objective: - -- introduce the minimal `dist` config needed to build release archives for: - - `aarch64-apple-darwin` - - `x86_64-apple-darwin` - -Work: - -- install `dist` locally using our nix flake and the `cargo-dist` derivation and initialize the workspace with `dist init` -- commit the generated `Cargo.toml` metadata and any generated workflow/config files -- keep the initial target list macOS-only instead of enabling a larger cross-platform matrix right now -- make sure the root package metadata is present and suitable for generated release artifacts: - - `description` - - `homepage` or a repository/homepage value usable by release tooling -- use the generated `profile.dist` instead of inventing custom release profiles - -Expected result: - -- `dist` can produce release archives for both macOS architectures from this workspace -- the repo has a standard `dist` config that can be regenerated later with `dist init` - -Notes: - -- `dist` can also manage Homebrew later, but the base GitHub Release artifact flow should land first. -- Avoid custom `dist` scripting unless the generated defaults prove insufficient. - -### 2. Add `just` commands for local release workflows - -STATUS: completed - -Objective: - -- make local release-related operations obvious and repeatable - -Work: - -- add a small set of `justfile` commands, likely: - - `just dist-init` - - `just dist-generate` - - `just dist-plan` - - `just dist-build` -- keep the commands thin wrappers around `dist` -- prefer names that match the `dist` subcommands directly - -Expected result: - -- contributors can regenerate config and test release builds locally without remembering `dist` syntax - -Notes: - -- Do not add a large release DSL to `justfile` -- Do not duplicate CI logic in `just`; keep it as a thin local entrypoint - -### 3. Extend Chrome discovery for normal macOS installs - -STATUS: completed - -Objective: - -- make the released binary work on a colleague’s Mac without relying on the Nix shell adding Chrome to `PATH` - -Current code: - -- `browser/src/participant/local/session.rs` looks up: - - `chromium` - - `google-chrome` - - `google-chrome-stable` - - `chrome` -- if none are on `PATH`, startup fails - -Work: - -- keep the existing `PATH` lookup first -- add a macOS fallback that checks common app-bundle executable locations, starting with: - - `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` -- optionally also check: - - `/Applications/Chromium.app/Contents/MacOS/Chromium` - - `~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` -- return the first existing executable path -- keep the change scoped to one helper, likely `get_binary()` -- add focused unit coverage for the path-resolution logic if the function can be made testable without filesystem-heavy integration tests - -Expected result: - -- the binary works on standard macOS setups where Chrome is installed in `/Applications` - -Notes: - -- Keep this as detection logic only -- Do not add a full browser-install manager -- Do not introduce a macOS-only CLI flag unless the fallback logic turns out to be too brittle - -### 4. Generate the GitHub release workflow with `dist` - -STATUS: completed - -Objective: - -- let `dist` own the release workflow rather than hand-writing a separate packaging pipeline - -Work: - -- use `dist`’s GitHub CI integration so `.github/workflows/release.yml` is generated from config -- review the generated workflow and only make the smallest necessary customizations -- keep the CI model close to upstream `dist` conventions so `dist init` / `dist generate` stay usable later - -Expected result: - -- release builds are driven by a standard `dist` workflow and tag-based release process - -Notes: - -- Avoid hand-maintaining a fully custom release workflow if `dist` can express the same thing in config -- If workflow customization is needed, prefer `dist` config knobs over direct YAML edits - -### 5. Route macOS targets to Blacksmith macOS runners - -STATUS: completed - -Objective: - -- build macOS release artifacts on Blacksmith using the requested runner class - -Work: - -- configure `dist` custom runners for the macOS targets so generated release jobs use: - - `blacksmith-12vcpu-macos-latest` -- keep any non-macOS global/planning jobs on the simplest supported runner unless there is a strong reason to move them too -- confirm the generated workflow still remains mostly `dist`-managed - -Expected result: - -- macOS artifact jobs run on Blacksmith instead of GitHub-hosted macOS runners - -Notes: - -- The main customization point should be `dist`’s GitHub custom runner configuration, not a hand-edited workflow matrix -- If `dist` requires a separate global runner setting for plan/host jobs, keep that explicit and minimal - -### 6. Validate the end-to-end release path - -STATUS: completed - -Objective: - -- confirm the new flow works before relying on it for colleague distribution - -Work: - -- run the local `just dist-plan` / `just dist-build` commands -- verify the generated artifacts include the `hyper-client-simulator` binary for both macOS targets -- open or inspect the generated release workflow -- if practical, test one dry-run or pre-release tag in GitHub Actions -- verify the binary can find Chrome on a normal macOS install outside the Nix shell - -Expected result: - -- confidence that a tagged release will produce usable macOS artifacts - -### 7. Add Homebrew installer support - -STATUS: implemented in-repo, pending GitHub secret setup - -Objective: - -- let colleagues install `hyper-client-simulator` with Homebrew instead of manually downloading release archives - -Work: - -- enable the `homebrew` installer in `dist` -- publish to the dedicated tap `hypervideo/homebrew-tap` -- configure the required Homebrew metadata in `dist` so generated formulae point at the GitHub Release artifacts for this repo -- regenerate the `dist` release workflow so Homebrew publishing is managed by `dist` rather than a separate hand-written pipeline -- document the expected install flow for users, including: - - `brew tap ...` - - `brew install hyper-client-simulator` -- verify the generated Homebrew formula installs the macOS binary and that the installed binary still relies on the local Chrome/Chromium runtime as expected - -Current state: - -- `dist-workspace.toml` enables the `homebrew` installer, sets `tap = "hypervideo/homebrew-tap"`, and asks `dist` to run the `homebrew` publish job -- `.github/workflows/release.yml` is generated from `dist` and includes the `publish-homebrew-formula` job that pushes `Formula/*.rb` into the tap repo using `HOMEBREW_TAP_TOKEN` -- the public tap repository now exists at `https://github.com/hypervideo/homebrew-tap` -- local verification on 2026-04-20 succeeded: - - `cargo dist manifest --artifacts=all --output-format=json --no-local-paths --allow-dirty --tag=v0.1.0` reported `hyper-client-simulator.rb` plus the macOS release archives - - `cargo dist build --target aarch64-apple-darwin --artifacts=all --allow-dirty --tag=v0.1.0` generated a real arm64 archive and formula - - installing that generated formula from a throwaway local tap succeeded - - `hyper-client-simulator --help` ran successfully after the Homebrew install -- the installed Homebrew package contains the simulator binary and README only; it does not bundle Chrome/Chromium, so runtime browser discovery still depends on the local machine as intended - -Remaining GitHub setup: - -1. create a GitHub token with write access to `hypervideo/homebrew-tap` -2. add that token as the `HOMEBREW_TAP_TOKEN` secret on `hypervideo/browser-simulator` -3. push a stable version tag so the `Release` workflow publishes the formula into the tap - -Expected user install flow: - -- `brew tap hypervideo/tap` -- `brew install hyper-client-simulator` -- `brew upgrade hyper-client-simulator` - -Expected result: - -- tagged releases publish both plain GitHub Release archives and a Homebrew formula -- colleagues can install or upgrade with `brew` - -Notes: - -- Keep Homebrew support scoped to the existing macOS CLI distribution -- Do not broaden this into notarization, `.app` bundling, or extra installer formats -- Prefer `dist`'s native Homebrew support over maintaining a custom formula by hand -- Homebrew only tracks the latest published formula version; prereleases do not publish unless `publish-prereleases` is explicitly enabled - -## Suggested Order Of Execution - -1. Add `dist` config. -2. Add the `just` commands. -3. Fix macOS Chrome discovery. -4. Generate and review the release workflow. -5. Configure Blacksmith runners through `dist`. -6. Validate the local and CI release flow. -7. Add Homebrew installer support once the base release flow is stable. - -## Non-Goals For This Pass - -- building a signed `.app` bundle -- notarization -- DMG packaging -- automatic Chrome installation -- broad Linux/Windows release support - -Homebrew is a follow-up phase after the base GitHub Release artifact flow is stable. - -## Documentation References - -- dist introduction: https://axodotdev.github.io/cargo-dist/book/introduction.html -- dist install: https://axodotdev.github.io/cargo-dist/book/install.html -- dist simple workspace guide: https://axodotdev.github.io/cargo-dist/book/workspaces/simple-guide.html -- dist config reference, including `github-custom-runners`: https://axodotdev.github.io/cargo-dist/book/reference/config.html -- dist Homebrew installer docs, for later follow-up: https://axodotdev.github.io/cargo-dist/book/installers/homebrew.html -- Blacksmith quickstart and runner-tag mapping: https://docs.blacksmith.sh/introduction/quickstart - -## Practical Deliverables - -When this plan is implemented, the resulting diff should roughly contain: - -- `Cargo.toml` updates for `dist` -- `justfile` release commands -- a small macOS Chrome discovery change in `browser/src/participant/local/session.rs` -- a generated `.github/workflows/release.yml` -- any `dist`-managed config files generated by initialization - -If the Homebrew follow-up is implemented too, the resulting diff should also roughly contain: - -- `dist` installer config enabling Homebrew -- any Homebrew-specific `dist` metadata needed for the chosen tap -- regenerated `dist` release workflow changes required for Homebrew publishing -- short contributor or user-facing install docs showing the `brew tap` / `brew install` flow diff --git a/Cargo.lock b/Cargo.lock index ebf7fac..99ace72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] + [[package]] name = "arraydeque" version = "0.5.1" @@ -152,6 +161,44 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-config" +version = "1.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "517aa062d8bd9015ee23d6daa5e1c1372328412fdae4e6c4c1be9b69c6ad37a2" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 1.4.0", + "time", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f20799b373a1be121fe3005fba0c2090af9411573878f224df44b42727fcaf7" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + [[package]] name = "aws-lc-rs" version = "1.16.3" @@ -174,6 +221,304 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "aws-runtime" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ed8e8c52d2dc2390ad9f15647fe663f71e9780b4262c190fbb823a32721566" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "bytes-utils", + "fastrand", + "http 1.4.0", + "http-body 1.0.1", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-devicefarm" +version = "1.105.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81a9aa2b25d3f45676bafe80560d9b39b9c3a31344d0f33db44274d4ad942df" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.4.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.105.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f4f8065fe615dbed9096458ba98dda6d641553ffd5aedd27e37e65211aca9f" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 0.2.12", + "http 1.4.0", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7083fb918b38474ac65ffbf8a69fc8792d36879f4ac5f1667b43aec61efe9a5" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.4.0", + "percent-encoding", + "sha2 0.11.0", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffcaf626bdda484571968400c326a244598634dc75fd451325a54ad1a59acfc" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.63.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a2f165a7feee6f263028b899d0a181987f4fa7179a6411a32a439fba7c5f769" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2", + "http 1.4.0", + "hyper", + "hyper-rustls", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.62.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "517089205f18ab4adc5a3e02888cb139bbbbb2e168eac9f396216925d1fbeaf5" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06c2315d173edbf1920da8ba3a7189695827002e4c0fc961973ab1c54abca9c" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a56d79744fb3edb5d722ef79d86081e121d3b9422cb209eb03aea6aa4f21ebd" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.4.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc117c179ecf39a62a0a3f49f600e9ac26a7ad7dd172177999f83933af776c32" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api-macros", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.4.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-runtime-api-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7396fd9500589e62e460e987ecb671bad374934e55ec3b5f498cc7a8a8a7b7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "aws-smithy-schema" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 1.4.0", +] + +[[package]] +name = "aws-smithy-types" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056b66dbce2f81cc0c1e2b05bb402eb58f8a3530479d650efadd5bbae9a4050b" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.4.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce02add1aa3677d022f8adf81dcbe3046a95f17a1b1e8979c145cd21d3d22b3" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16bf10b03a3c01e6b3b7d47cd964e873ffe9e7d4e80fad16bd4c077cb068531" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.76" @@ -195,6 +540,16 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "better-panic" version = "0.3.0" @@ -274,6 +629,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "castaway" version = "0.2.4" @@ -337,8 +702,6 @@ dependencies = [ [[package]] name = "chromiumoxide_cdp" version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a6a03a7ebac4ea85308f285d6959a3e6b2ce32a0c9465dc7a7b1db0144eec7" dependencies = [ "chromiumoxide_pdl", "chromiumoxide_types", @@ -430,6 +793,10 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" name = "client-simulator-browser" version = "0.3.4" dependencies = [ + "async-trait", + "aws-config", + "aws-sdk-devicefarm", + "bytes", "chromiumoxide", "chrono", "client-simulator-config", @@ -437,10 +804,12 @@ dependencies = [ "derive_more", "eyre", "futures", + "http 1.4.0", "reqwest", "serde", "serde_json", "strum 0.28.0", + "thirtyfour", "tokio", "tokio-util", "tracing", @@ -461,6 +830,7 @@ dependencies = [ "names", "reqwest", "serde", + "serde_json", "sha1 0.11.0", "strum 0.28.0", "temp-dir", @@ -526,6 +896,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "color-eyre" version = "0.6.5" @@ -616,6 +992,27 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.6.0" @@ -707,6 +1104,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossterm" version = "0.28.1" @@ -782,6 +1188,15 @@ dependencies = [ "phf", ] +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "darling" version = "0.23.0" @@ -885,6 +1300,7 @@ dependencies = [ "block-buffer 0.12.0", "const-oid", "crypto-common 0.2.1", + "ctutils", ] [[package]] @@ -896,6 +1312,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-sys" version = "0.5.0" @@ -1016,6 +1441,12 @@ dependencies = [ "regex", ] +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "filedescriptor" version = "0.8.3" @@ -1027,6 +1458,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1045,6 +1486,17 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1072,6 +1524,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs4" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e72ed92b67c146290f88e9c89d60ca163ea417a446f61ffd7b72df3e7f1dfd5" +dependencies = [ + "rustix 1.1.4", + "tokio", + "windows-sys 0.61.2", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1239,7 +1702,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -1294,6 +1757,26 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.0" @@ -1304,6 +1787,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1311,7 +1805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -1322,8 +1816,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1349,6 +1843,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + [[package]] name = "hybrid-array" version = "0.4.11" @@ -1369,8 +1869,8 @@ dependencies = [ "futures-channel", "futures-core", "h2", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -1384,6 +1884,7 @@ name = "hyper-client-simulator" version = "0.3.4" dependencies = [ "better-panic", + "chrono", "clap", "client-simulator-browser", "client-simulator-config", @@ -1391,7 +1892,9 @@ dependencies = [ "color-eyre", "eyre", "human-panic", + "humantime", "libc", + "serde", "serde_json", "strip-ansi-escapes", "tokio", @@ -1407,10 +1910,11 @@ version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http", + "http 1.4.0", "hyper", "hyper-util", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -1426,8 +1930,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "hyper", "ipnet", "libc", @@ -1780,6 +2284,21 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lab" version = "0.11.0" @@ -1941,6 +2460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2022,6 +2542,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2118,6 +2647,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "owo-colors" version = "4.3.0" @@ -2147,6 +2682,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "pathdiff" version = "0.2.3" @@ -2199,7 +2740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -2260,6 +2801,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -2361,7 +2908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e349eed84b9a1a6a5dbe478d335e3df73d32a93c5eefe571c9b8cb298aab5d" dependencies = [ "heck", - "http", + "http 1.4.0", "indexmap", "openapiv3", "proc-macro2", @@ -2690,6 +3237,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + [[package]] name = "regex-syntax" version = "0.8.10" @@ -2721,8 +3274,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", "hyper", "hyper-rustls", @@ -3031,6 +3584,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap", "itoa", "memchr", "serde", @@ -3038,6 +3592,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -3117,6 +3682,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3173,6 +3749,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "simd_cesu8" version = "1.1.1" @@ -3229,6 +3811,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringmatch" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aadc0801d92f0cdc26127c67c4b8766284f52a5ba22894f285e3101fa57d05d" +dependencies = [ + "regex", +] + [[package]] name = "strip-ansi-escapes" version = "0.2.1" @@ -3369,6 +3960,17 @@ dependencies = [ "libc", ] +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "temp-dir" version = "0.2.0" @@ -3433,7 +4035,7 @@ dependencies = [ "pest_derive", "phf", "serde", - "sha2", + "sha2 0.10.9", "signal-hook 0.3.18", "siphasher", "terminfo", @@ -3450,6 +4052,50 @@ dependencies = [ "winapi", ] +[[package]] +name = "thirtyfour" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801f15adc5e2882c13b36d48003e5512b017e7b89ebf5fb35d59074a261e6afb" +dependencies = [ + "arc-swap", + "async-trait", + "base64", + "bytes", + "cfg-if", + "const_format", + "dirs", + "flate2", + "fs4", + "futures-util", + "http 1.4.0", + "indexmap", + "pastey", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "stringmatch", + "tar", + "thirtyfour-macros", + "thiserror 2.0.18", + "tokio", + "tracing", + "url", + "zip", +] + +[[package]] +name = "thirtyfour-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf0ffc3ba4368e99597bd6afd83f4ff6febad66d9ae541ab46e697d32285fc0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3658,8 +4304,8 @@ dependencies = [ "bitflags 2.11.1", "bytes", "futures-util", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "iri-string", "pin-project-lite", "tower", @@ -3782,7 +4428,7 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http", + "http 1.4.0", "httparse", "log", "rand 0.9.4", @@ -3791,6 +4437,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + [[package]] name = "typenum" version = "1.20.0" @@ -3910,6 +4562,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -3953,6 +4611,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "vte" version = "0.14.1" @@ -4164,7 +4828,7 @@ dependencies = [ "getrandom 0.3.4", "mac_address", "serde", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "uuid", ] @@ -4654,6 +5318,22 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.4", +] + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yaml-rust2" version = "0.10.4" @@ -4787,8 +5467,40 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index 6e38da0..d79823d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,11 @@ homepage = "https://github.com/hypervideo/browser-simulator" repository = "https://github.com/hypervideo/browser-simulator" [workspace.dependencies] +aws-config = { version = "1", default-features = false, features = ["behavior-version-latest", "rt-tokio", "default-https-client"] } +aws-sdk-devicefarm = { version = "1", default-features = false, features = ["behavior-version-latest", "rt-tokio", "default-https-client"] } +async-trait = "0.1.89" better-panic = "0.3.0" +bytes = "1.11.1" chromiumoxide = { version = "0.9.1", features = ["bytes"], default-features = false } chrono = { version = "0.4.44", features = ["serde"] } clap = { version = "4.6.1", features = ["derive", "env", "cargo", "string"] } @@ -28,6 +32,8 @@ directories = "6.0.0" eyre = "0.6.12" futures = "0.3.32" human-panic = "2.0.8" +humantime = "2.3.0" +http = "1.4.0" lazy_static = "1.5.0" libc = "0.2.186" pretty_assertions = "1.4.1" @@ -41,6 +47,7 @@ signal-hook = "0.4.4" strip-ansi-escapes = "0.2.1" strum = { version = "0.28.0", features = ["derive"] } temp-dir = "0.2.0" +thirtyfour = "0.36" tokio = { version = "1.52.2", default-features = false, features = ["macros", "rt-multi-thread", "sync", "signal"] } tokio-util = "0.7.18" tracing = "0.1.44" @@ -56,6 +63,9 @@ client-simulator-config = { path = "./config" } client-simulator-tui = { path = "./tui" } cloudflare-worker-client = { path = "./cloudflare-worker-client" } +[patch.crates-io] +chromiumoxide_cdp = { path = "vendor/chromiumoxide_cdp" } + [workspace.lints.clippy] # Allows for simpler exports. module_inception = "allow" @@ -78,6 +88,7 @@ repository.workspace = true [dependencies] better-panic.workspace = true +chrono.workspace = true clap.workspace = true client-simulator-browser.workspace = true client-simulator-config.workspace = true @@ -85,8 +96,10 @@ client-simulator-tui.workspace = true color-eyre.workspace = true eyre.workspace = true human-panic.workspace = true +humantime.workspace = true libc.workspace = true serde_json.workspace = true +serde.workspace = true strip-ansi-escapes.workspace = true tokio.workspace = true tracing.workspace = true diff --git a/README.md b/README.md index 36b50ee..a434884 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,25 @@ brew upgrade hyper-client-simulator The Homebrew package installs the simulator binary only. Chrome or Chromium must still be installed locally on the Mac where you run it. + +## Development + +This repository uses `hypervideo/cloudflare-browser-simulator` as a Git submodule +for the Cloudflare worker OpenAPI schema used by `cloudflare-worker-client`. +Clone with submodules, or initialize them after cloning: + +```sh +git clone --recurse-submodules git@github.com:hypervideo/browser-simulator.git +git submodule update --init --recursive +``` + +To consume a newer worker API, first update and commit the generated OpenAPI +artifact in `hypervideo/cloudflare-browser-simulator`, then update this +repository's submodule pointer and make any matching Rust client changes. Worker +deployment remains owned by the Cloudflare worker repository. + +GitHub Actions needs the repository secret +`CLOUDFLARE_BROWSER_SIMULATOR_READ_TOKEN` set to a read-only token that can +fetch both this repository and the private worker submodule. Full lint/test CI is +skipped for untrusted fork pull requests because that secret is not exposed +there. diff --git a/browser/Cargo.toml b/browser/Cargo.toml index 3fa0f8b..ef74f9f 100644 --- a/browser/Cargo.toml +++ b/browser/Cargo.toml @@ -6,15 +6,21 @@ edition.workspace = true repository.workspace = true [dependencies] +aws-config.workspace = true +aws-sdk-devicefarm.workspace = true +async-trait.workspace = true +bytes.workspace = true chromiumoxide.workspace = true chrono.workspace = true derive_more.workspace = true eyre.workspace = true futures.workspace = true +http.workspace = true reqwest.workspace = true serde.workspace = true serde_json.workspace = true strum.workspace = true +thirtyfour.workspace = true tokio.workspace = true tokio-util.workspace = true tracing.workspace = true diff --git a/browser/src/lib.rs b/browser/src/lib.rs index 5181d8a..dca5811 100644 --- a/browser/src/lib.rs +++ b/browser/src/lib.rs @@ -3,4 +3,8 @@ extern crate tracing; pub mod auth; pub mod participant; -pub mod util; + +#[doc(hidden)] +pub mod testing { + pub use crate::participant::device_farm::TestGridApi; +} diff --git a/browser/src/participant/cloudflare/mod.rs b/browser/src/participant/cloudflare/mod.rs index 268e9b1..8531e30 100644 --- a/browser/src/participant/cloudflare/mod.rs +++ b/browser/src/participant/cloudflare/mod.rs @@ -310,9 +310,21 @@ impl CloudflareSession { ParticipantMessage::SetNoiseSuppression(value) => types::SessionCommandRequest::SetNoiseSuppression { noise_suppression: map_command_noise_suppression(value), }, - ParticipantMessage::SetWebcamResolutions(value) => types::SessionCommandRequest::SetWebcamResolution { - webcam_resolution: map_command_webcam_resolution(value), - }, + ParticipantMessage::SetVideoConstraintPublishWebcam(value) => { + types::SessionCommandRequest::SetVideoConstraintPublishWebcam { + video_constraint_publish_webcam: map_command_video_constraint_publish_webcam(value), + } + } + ParticipantMessage::SetVideoConstraintSubscribe(value) => { + types::SessionCommandRequest::SetVideoConstraintSubscribe { + video_constraint_subscribe: map_command_video_constraint_subscribe(value), + } + } + ParticipantMessage::SetVideoMaxConcurrentTracks(value) => { + types::SessionCommandRequest::SetVideoMaxConcurrentTracks { + video_max_concurrent_tracks: value.map(|value| value as u64), + } + } ParticipantMessage::ToggleBackgroundBlur => types::SessionCommandRequest::ToggleBackgroundBlur, } } @@ -574,23 +586,14 @@ fn map_settings(settings: &crate::participant::shared::ParticipantSettings) -> t types::ParticipantSettingsNoiseSuppression::AiCousticsRookL48khz } }, - resolution: match settings.resolution { - client_simulator_config::WebcamResolution::Auto => types::ParticipantSettingsResolution::Auto, - client_simulator_config::WebcamResolution::P144 => types::ParticipantSettingsResolution::P144, - client_simulator_config::WebcamResolution::P240 => types::ParticipantSettingsResolution::P240, - client_simulator_config::WebcamResolution::P360 => types::ParticipantSettingsResolution::P360, - client_simulator_config::WebcamResolution::P480 => types::ParticipantSettingsResolution::P480, - client_simulator_config::WebcamResolution::P720 => types::ParticipantSettingsResolution::P720, - client_simulator_config::WebcamResolution::P1080 => types::ParticipantSettingsResolution::P1080, - client_simulator_config::WebcamResolution::P1440 => types::ParticipantSettingsResolution::P1440, - client_simulator_config::WebcamResolution::P2160 => types::ParticipantSettingsResolution::P2160, - client_simulator_config::WebcamResolution::P4320 => types::ParticipantSettingsResolution::P4320, - }, screenshare_enabled: settings.screenshare_enabled, transport: match settings.transport { client_simulator_config::TransportMode::WebRTC => types::ParticipantSettingsTransport::Webrtc, client_simulator_config::TransportMode::WebTransport => types::ParticipantSettingsTransport::Webtransport, }, + video_constraint_publish_webcam: map_video_constraint_publish_webcam(settings.video_constraint_publish_webcam), + video_constraint_subscribe: map_video_constraint_subscribe(settings.video_constraint_subscribe), + video_max_concurrent_tracks: settings.video_max_concurrent_tracks.map(|value| value as u64), video_enabled: settings.video_enabled, } } @@ -652,18 +655,11 @@ fn map_state(state: &types::ParticipantState) -> ParticipantState { types::ParticipantStateTransportMode::Webrtc => client_simulator_config::TransportMode::WebRTC, types::ParticipantStateTransportMode::Webtransport => client_simulator_config::TransportMode::WebTransport, }, - webcam_resolution: match state.webcam_resolution { - types::ParticipantStateWebcamResolution::Auto => client_simulator_config::WebcamResolution::Auto, - types::ParticipantStateWebcamResolution::P144 => client_simulator_config::WebcamResolution::P144, - types::ParticipantStateWebcamResolution::P240 => client_simulator_config::WebcamResolution::P240, - types::ParticipantStateWebcamResolution::P360 => client_simulator_config::WebcamResolution::P360, - types::ParticipantStateWebcamResolution::P480 => client_simulator_config::WebcamResolution::P480, - types::ParticipantStateWebcamResolution::P720 => client_simulator_config::WebcamResolution::P720, - types::ParticipantStateWebcamResolution::P1080 => client_simulator_config::WebcamResolution::P1080, - types::ParticipantStateWebcamResolution::P1440 => client_simulator_config::WebcamResolution::P1440, - types::ParticipantStateWebcamResolution::P2160 => client_simulator_config::WebcamResolution::P2160, - types::ParticipantStateWebcamResolution::P4320 => client_simulator_config::WebcamResolution::P4320, - }, + video_constraint_publish_webcam: map_state_video_constraint_publish_webcam( + state.video_constraint_publish_webcam, + ), + video_constraint_subscribe: map_state_video_constraint_subscribe(state.video_constraint_subscribe), + video_max_concurrent_tracks: state.video_max_concurrent_tracks.map(|value| value as usize), background_blur: state.background_blur, screenshare_activated: state.screenshare_activated, } @@ -719,20 +715,129 @@ fn map_command_noise_suppression( } } -fn map_command_webcam_resolution( - webcam_resolution: client_simulator_config::WebcamResolution, -) -> types::SessionCommandRequestWebcamResolution { - match webcam_resolution { - client_simulator_config::WebcamResolution::Auto => types::SessionCommandRequestWebcamResolution::Auto, - client_simulator_config::WebcamResolution::P144 => types::SessionCommandRequestWebcamResolution::P144, - client_simulator_config::WebcamResolution::P240 => types::SessionCommandRequestWebcamResolution::P240, - client_simulator_config::WebcamResolution::P360 => types::SessionCommandRequestWebcamResolution::P360, - client_simulator_config::WebcamResolution::P480 => types::SessionCommandRequestWebcamResolution::P480, - client_simulator_config::WebcamResolution::P720 => types::SessionCommandRequestWebcamResolution::P720, - client_simulator_config::WebcamResolution::P1080 => types::SessionCommandRequestWebcamResolution::P1080, - client_simulator_config::WebcamResolution::P1440 => types::SessionCommandRequestWebcamResolution::P1440, - client_simulator_config::WebcamResolution::P2160 => types::SessionCommandRequestWebcamResolution::P2160, - client_simulator_config::WebcamResolution::P4320 => types::SessionCommandRequestWebcamResolution::P4320, +fn map_video_constraint_publish_webcam( + value: client_simulator_config::VideoConstraint, +) -> types::ParticipantSettingsVideoConstraintPublishWebcam { + match value { + client_simulator_config::VideoConstraint::None => types::ParticipantSettingsVideoConstraintPublishWebcam::None, + client_simulator_config::VideoConstraint::P90 => types::ParticipantSettingsVideoConstraintPublishWebcam::X90p, + client_simulator_config::VideoConstraint::P144 => types::ParticipantSettingsVideoConstraintPublishWebcam::X144p, + client_simulator_config::VideoConstraint::P240 => types::ParticipantSettingsVideoConstraintPublishWebcam::X240p, + client_simulator_config::VideoConstraint::P360 => types::ParticipantSettingsVideoConstraintPublishWebcam::X360p, + client_simulator_config::VideoConstraint::P480 => types::ParticipantSettingsVideoConstraintPublishWebcam::X480p, + client_simulator_config::VideoConstraint::P720 => types::ParticipantSettingsVideoConstraintPublishWebcam::X720p, + client_simulator_config::VideoConstraint::P1080 => { + types::ParticipantSettingsVideoConstraintPublishWebcam::X1080p + } + client_simulator_config::VideoConstraint::P1440 => { + types::ParticipantSettingsVideoConstraintPublishWebcam::X1440p + } + client_simulator_config::VideoConstraint::P2160 => { + types::ParticipantSettingsVideoConstraintPublishWebcam::X2160p + } + } +} + +fn map_video_constraint_subscribe( + value: client_simulator_config::VideoConstraint, +) -> types::ParticipantSettingsVideoConstraintSubscribe { + match value { + client_simulator_config::VideoConstraint::None => types::ParticipantSettingsVideoConstraintSubscribe::None, + client_simulator_config::VideoConstraint::P90 => types::ParticipantSettingsVideoConstraintSubscribe::X90p, + client_simulator_config::VideoConstraint::P144 => types::ParticipantSettingsVideoConstraintSubscribe::X144p, + client_simulator_config::VideoConstraint::P240 => types::ParticipantSettingsVideoConstraintSubscribe::X240p, + client_simulator_config::VideoConstraint::P360 => types::ParticipantSettingsVideoConstraintSubscribe::X360p, + client_simulator_config::VideoConstraint::P480 => types::ParticipantSettingsVideoConstraintSubscribe::X480p, + client_simulator_config::VideoConstraint::P720 => types::ParticipantSettingsVideoConstraintSubscribe::X720p, + client_simulator_config::VideoConstraint::P1080 => types::ParticipantSettingsVideoConstraintSubscribe::X1080p, + client_simulator_config::VideoConstraint::P1440 => types::ParticipantSettingsVideoConstraintSubscribe::X1440p, + client_simulator_config::VideoConstraint::P2160 => types::ParticipantSettingsVideoConstraintSubscribe::X2160p, + } +} + +fn map_state_video_constraint_publish_webcam( + value: types::ParticipantStateVideoConstraintPublishWebcam, +) -> client_simulator_config::VideoConstraint { + match value { + types::ParticipantStateVideoConstraintPublishWebcam::None => client_simulator_config::VideoConstraint::None, + types::ParticipantStateVideoConstraintPublishWebcam::X90p => client_simulator_config::VideoConstraint::P90, + types::ParticipantStateVideoConstraintPublishWebcam::X144p => client_simulator_config::VideoConstraint::P144, + types::ParticipantStateVideoConstraintPublishWebcam::X240p => client_simulator_config::VideoConstraint::P240, + types::ParticipantStateVideoConstraintPublishWebcam::X360p => client_simulator_config::VideoConstraint::P360, + types::ParticipantStateVideoConstraintPublishWebcam::X480p => client_simulator_config::VideoConstraint::P480, + types::ParticipantStateVideoConstraintPublishWebcam::X720p => client_simulator_config::VideoConstraint::P720, + types::ParticipantStateVideoConstraintPublishWebcam::X1080p => client_simulator_config::VideoConstraint::P1080, + types::ParticipantStateVideoConstraintPublishWebcam::X1440p => client_simulator_config::VideoConstraint::P1440, + types::ParticipantStateVideoConstraintPublishWebcam::X2160p => client_simulator_config::VideoConstraint::P2160, + } +} + +fn map_state_video_constraint_subscribe( + value: types::ParticipantStateVideoConstraintSubscribe, +) -> client_simulator_config::VideoConstraint { + match value { + types::ParticipantStateVideoConstraintSubscribe::None => client_simulator_config::VideoConstraint::None, + types::ParticipantStateVideoConstraintSubscribe::X90p => client_simulator_config::VideoConstraint::P90, + types::ParticipantStateVideoConstraintSubscribe::X144p => client_simulator_config::VideoConstraint::P144, + types::ParticipantStateVideoConstraintSubscribe::X240p => client_simulator_config::VideoConstraint::P240, + types::ParticipantStateVideoConstraintSubscribe::X360p => client_simulator_config::VideoConstraint::P360, + types::ParticipantStateVideoConstraintSubscribe::X480p => client_simulator_config::VideoConstraint::P480, + types::ParticipantStateVideoConstraintSubscribe::X720p => client_simulator_config::VideoConstraint::P720, + types::ParticipantStateVideoConstraintSubscribe::X1080p => client_simulator_config::VideoConstraint::P1080, + types::ParticipantStateVideoConstraintSubscribe::X1440p => client_simulator_config::VideoConstraint::P1440, + types::ParticipantStateVideoConstraintSubscribe::X2160p => client_simulator_config::VideoConstraint::P2160, + } +} + +fn map_command_video_constraint_publish_webcam( + value: client_simulator_config::VideoConstraint, +) -> types::SessionCommandRequestVideoConstraintPublishWebcam { + match value { + client_simulator_config::VideoConstraint::None => { + types::SessionCommandRequestVideoConstraintPublishWebcam::None + } + client_simulator_config::VideoConstraint::P90 => types::SessionCommandRequestVideoConstraintPublishWebcam::X90p, + client_simulator_config::VideoConstraint::P144 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X144p + } + client_simulator_config::VideoConstraint::P240 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X240p + } + client_simulator_config::VideoConstraint::P360 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X360p + } + client_simulator_config::VideoConstraint::P480 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X480p + } + client_simulator_config::VideoConstraint::P720 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X720p + } + client_simulator_config::VideoConstraint::P1080 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X1080p + } + client_simulator_config::VideoConstraint::P1440 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X1440p + } + client_simulator_config::VideoConstraint::P2160 => { + types::SessionCommandRequestVideoConstraintPublishWebcam::X2160p + } + } +} + +fn map_command_video_constraint_subscribe( + value: client_simulator_config::VideoConstraint, +) -> types::SessionCommandRequestVideoConstraintSubscribe { + match value { + client_simulator_config::VideoConstraint::None => types::SessionCommandRequestVideoConstraintSubscribe::None, + client_simulator_config::VideoConstraint::P90 => types::SessionCommandRequestVideoConstraintSubscribe::X90p, + client_simulator_config::VideoConstraint::P144 => types::SessionCommandRequestVideoConstraintSubscribe::X144p, + client_simulator_config::VideoConstraint::P240 => types::SessionCommandRequestVideoConstraintSubscribe::X240p, + client_simulator_config::VideoConstraint::P360 => types::SessionCommandRequestVideoConstraintSubscribe::X360p, + client_simulator_config::VideoConstraint::P480 => types::SessionCommandRequestVideoConstraintSubscribe::X480p, + client_simulator_config::VideoConstraint::P720 => types::SessionCommandRequestVideoConstraintSubscribe::X720p, + client_simulator_config::VideoConstraint::P1080 => types::SessionCommandRequestVideoConstraintSubscribe::X1080p, + client_simulator_config::VideoConstraint::P1440 => types::SessionCommandRequestVideoConstraintSubscribe::X1440p, + client_simulator_config::VideoConstraint::P2160 => types::SessionCommandRequestVideoConstraintSubscribe::X2160p, } } @@ -770,7 +875,7 @@ mod tests { CloudflareConfig, NoiseSuppression, TransportMode, - WebcamResolution, + VideoConstraint, }; use serde_json::{ json, @@ -831,7 +936,9 @@ mod tests { "autoGainControl": true, "noiseSuppression": "rnnoise", "transportMode": "webrtc", - "webcamResolution": "p720", + "videoConstraintPublishWebcam": "720p", + "videoConstraintSubscribe": "none", + "videoMaxConcurrentTracks": null, "backgroundBlur": true }, "log": [ @@ -883,7 +990,9 @@ mod tests { assert!(state.joined); assert_eq!(state.noise_suppression, NoiseSuppression::RNNoise); assert_eq!(state.transport_mode, TransportMode::WebRTC); - assert_eq!(state.webcam_resolution, WebcamResolution::P720); + assert_eq!(state.video_constraint_publish_webcam, VideoConstraint::P720); + assert_eq!(state.video_constraint_subscribe, VideoConstraint::None); + assert_eq!(state.video_max_concurrent_tracks, None); assert!(state.auto_gain_control); assert!(state.background_blur); @@ -925,9 +1034,11 @@ mod tests { "autoGainControl": true, "blur": true, "noiseSuppression": "rnnoise", - "resolution": "p720", "screenshareEnabled": false, "transport": "webrtc", + "videoConstraintPublishWebcam": "720p", + "videoConstraintSubscribe": "none", + "videoMaxConcurrentTracks": null, "videoEnabled": true } }) @@ -954,7 +1065,9 @@ mod tests { "autoGainControl": true, "noiseSuppression": "ai-coustics-rook-s-48khz", "transportMode": "webrtc", - "webcamResolution": "p720", + "videoConstraintPublishWebcam": "720p", + "videoConstraintSubscribe": "none", + "videoMaxConcurrentTracks": null, "backgroundBlur": true }, "log": [], @@ -1007,7 +1120,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(false, false, false, false, true, "none", "auto", false), + "state": worker_state_json(false, false, false, false, true, "none", "none", false), "log": [], }), ), @@ -1016,7 +1129,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, false, false, false, true, "none", "auto", false), + "state": worker_state_json(true, false, false, false, true, "none", "none", false), "log": [], }), ), @@ -1025,7 +1138,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, false, false, true, "none", "auto", false), + "state": worker_state_json(true, true, false, false, true, "none", "none", false), "log": [], }), ), @@ -1034,7 +1147,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, true, false, true, "none", "auto", false), + "state": worker_state_json(true, true, true, false, true, "none", "none", false), "log": [], }), ), @@ -1043,7 +1156,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, true, true, true, "none", "auto", false), + "state": worker_state_json(true, true, true, true, true, "none", "none", false), "log": [], }), ), @@ -1052,7 +1165,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, true, true, false, "none", "auto", false), + "state": worker_state_json(true, true, true, true, false, "none", "none", false), "log": [], }), ), @@ -1061,7 +1174,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, true, true, false, "deepfilternet", "auto", false), + "state": worker_state_json(true, true, true, true, false, "deepfilternet", "none", false), "log": [], }), ), @@ -1070,7 +1183,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, true, true, false, "deepfilternet", "p1080", false), + "state": worker_state_json(true, true, true, true, false, "deepfilternet", "1080p", false), "log": [], }), ), @@ -1079,7 +1192,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(true, true, true, true, false, "deepfilternet", "p1080", true), + "state": worker_state_json(true, true, true, true, false, "deepfilternet", "1080p", true), "log": [], }), ), @@ -1088,7 +1201,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-commands", - "state": worker_state_json(false, true, true, false, false, "deepfilternet", "p1080", true), + "state": worker_state_json(false, true, true, false, false, "deepfilternet", "1080p", true), "log": [], }), ), @@ -1134,7 +1247,7 @@ mod tests { false, true, NoiseSuppression::Disabled, - WebcamResolution::Auto, + VideoConstraint::None, false, ), ), @@ -1148,7 +1261,7 @@ mod tests { false, true, NoiseSuppression::Disabled, - WebcamResolution::Auto, + VideoConstraint::None, false, ), ), @@ -1162,7 +1275,7 @@ mod tests { false, true, NoiseSuppression::Disabled, - WebcamResolution::Auto, + VideoConstraint::None, false, ), ), @@ -1176,7 +1289,7 @@ mod tests { true, true, NoiseSuppression::Disabled, - WebcamResolution::Auto, + VideoConstraint::None, false, ), ), @@ -1190,7 +1303,7 @@ mod tests { true, false, NoiseSuppression::Disabled, - WebcamResolution::Auto, + VideoConstraint::None, false, ), ), @@ -1204,13 +1317,13 @@ mod tests { true, false, NoiseSuppression::Deepfilternet, - WebcamResolution::Auto, + VideoConstraint::None, false, ), ), ( - ParticipantMessage::SetWebcamResolutions(WebcamResolution::P1080), - json!({ "type": "set-webcam-resolution", "webcamResolution": "p1080" }), + ParticipantMessage::SetVideoConstraintPublishWebcam(VideoConstraint::P1080), + json!({ "type": "set-video-constraint-publish-webcam", "videoConstraintPublishWebcam": "1080p" }), expected_state( true, true, @@ -1218,7 +1331,7 @@ mod tests { true, false, NoiseSuppression::Deepfilternet, - WebcamResolution::P1080, + VideoConstraint::P1080, false, ), ), @@ -1232,7 +1345,7 @@ mod tests { true, false, NoiseSuppression::Deepfilternet, - WebcamResolution::P1080, + VideoConstraint::P1080, true, ), ), @@ -1246,7 +1359,7 @@ mod tests { false, false, NoiseSuppression::Deepfilternet, - WebcamResolution::P1080, + VideoConstraint::P1080, true, ), ), @@ -1286,7 +1399,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-webrtc", - "state": worker_state_json(false, false, false, false, true, "rnnoise", "p720", true), + "state": worker_state_json(false, false, false, false, true, "rnnoise", "720p", true), "log": [], }), ), @@ -1351,7 +1464,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-limitations", - "state": worker_state_json(false, false, false, false, true, "rnnoise", "p720", true), + "state": worker_state_json(false, false, false, false, true, "rnnoise", "720p", true), "log": [], }), ), @@ -1407,7 +1520,7 @@ mod tests { json!({ "ok": true, "sessionId": "cf-session-terminated", - "state": worker_state_json(true, false, false, false, true, "none", "auto", false), + "state": worker_state_json(true, false, false, false, true, "none", "none", false), "log": [], }), ), @@ -1475,7 +1588,9 @@ mod tests { auto_gain_control: true, noise_suppression: NoiseSuppression::RNNoise, transport: TransportMode::WebRTC, - resolution: WebcamResolution::P720, + video_constraint_publish_webcam: VideoConstraint::P720, + video_constraint_subscribe: VideoConstraint::None, + video_max_concurrent_tracks: None, blur: true, }, } @@ -1488,7 +1603,7 @@ mod tests { screenshare_activated: bool, auto_gain_control: bool, noise_suppression: &str, - webcam_resolution: &str, + video_constraint_publish_webcam: &str, background_blur: bool, ) -> Value { json!({ @@ -1500,7 +1615,9 @@ mod tests { "autoGainControl": auto_gain_control, "noiseSuppression": noise_suppression, "transportMode": "webrtc", - "webcamResolution": webcam_resolution, + "videoConstraintPublishWebcam": video_constraint_publish_webcam, + "videoConstraintSubscribe": "none", + "videoMaxConcurrentTracks": null, "backgroundBlur": background_blur, }) } @@ -1512,7 +1629,7 @@ mod tests { screenshare_activated: bool, auto_gain_control: bool, noise_suppression: NoiseSuppression, - webcam_resolution: WebcamResolution, + video_constraint_publish_webcam: VideoConstraint, background_blur: bool, ) -> ParticipantState { ParticipantState { @@ -1524,7 +1641,9 @@ mod tests { auto_gain_control, noise_suppression, transport_mode: TransportMode::WebRTC, - webcam_resolution, + video_constraint_publish_webcam, + video_constraint_subscribe: VideoConstraint::None, + video_max_concurrent_tracks: None, background_blur, screenshare_activated, } @@ -1539,7 +1658,12 @@ mod tests { assert_eq!(actual.auto_gain_control, expected.auto_gain_control); assert_eq!(actual.noise_suppression, expected.noise_suppression); assert_eq!(actual.transport_mode, expected.transport_mode); - assert_eq!(actual.webcam_resolution, expected.webcam_resolution); + assert_eq!( + actual.video_constraint_publish_webcam, + expected.video_constraint_publish_webcam + ); + assert_eq!(actual.video_constraint_subscribe, expected.video_constraint_subscribe); + assert_eq!(actual.video_max_concurrent_tracks, expected.video_max_concurrent_tracks); assert_eq!(actual.background_blur, expected.background_blur); assert_eq!(actual.screenshare_activated, expected.screenshare_activated); } diff --git a/browser/src/participant/device_farm/control.rs b/browser/src/participant/device_farm/control.rs new file mode 100644 index 0000000..52fb20f --- /dev/null +++ b/browser/src/participant/device_farm/control.rs @@ -0,0 +1,210 @@ +use super::test_grid::TestGridApi; +use aws_sdk_devicefarm::{ + primitives::DateTime, + types::{ + TestGridSession, + TestGridSessionStatus, + }, +}; +use eyre::{ + bail, + Context as _, + Result, +}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] +pub struct DeviceFarmSessionInfo { + pub arn: String, + pub session_id: String, + pub status: String, + pub created: Option, + pub ended: Option, + pub age_seconds: Option, + pub billing_minutes: Option, + pub selenium_properties: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DeviceFarmCloseResult { + Closed, + AlreadyClosed, +} + +pub fn session_id_from_arn(arn: &str) -> Option { + arn.rsplit_once('/').map(|(_, session_id)| session_id.to_string()) +} + +pub fn selenium_properties_json(value: Option<&str>) -> Option { + value.map(|value| serde_json::from_str(value).unwrap_or_else(|_| serde_json::Value::String(value.to_string()))) +} + +pub async fn list_project_sessions( + api: &dyn TestGridApi, + project_arn: &str, + status: Option, +) -> Result> { + let now_epoch_seconds = chrono::Utc::now().timestamp(); + let sessions = api.list_test_grid_sessions(project_arn, status).await?; + Ok(sessions + .into_iter() + .map(|session| map_test_grid_session(session, now_epoch_seconds)) + .collect()) +} + +pub async fn list_active_project_sessions( + api: &dyn TestGridApi, + project_arn: &str, +) -> Result> { + list_project_sessions(api, project_arn, Some(TestGridSessionStatus::Active)).await +} + +pub async fn close_test_grid_session( + api: &dyn TestGridApi, + project_arn: &str, + url_expires_seconds: u64, + session_id: &str, +) -> Result { + let signed_url = api + .create_test_grid_url(project_arn, url_expires_seconds) + .await + .context("failed to create Device Farm Test Grid URL")?; + let signed_url = signed_url + .parse::() + .context("Device Farm returned an invalid Selenium endpoint URL")?; + let command_uri = format!("/session/{session_id}") + .parse::() + .context("invalid WebDriver session deletion URI")?; + let url = signed_test_grid_command_url(&signed_url, &command_uri)?; + + let response = reqwest::Client::new() + .delete(url) + .send() + .await + .context("failed to send WebDriver delete session request")?; + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + + if status == reqwest::StatusCode::OK || status == reqwest::StatusCode::NO_CONTENT { + return Ok(DeviceFarmCloseResult::Closed); + } + + if webdriver_error_is_invalid_session_id(&body) { + return Ok(DeviceFarmCloseResult::AlreadyClosed); + } + + if status == reqwest::StatusCode::FORBIDDEN || status == reqwest::StatusCode::METHOD_NOT_ALLOWED { + bail!( + "AWS Device Farm did not accept WebDriver session deletion for {session_id} (HTTP {status}); \ + Test Grid has no supported per-session force-close API response: {body}" + ); + } + + bail!("failed to close Device Farm session {session_id} (HTTP {status}): {body}"); +} + +pub(crate) fn signed_test_grid_command_url(signed_url: &url::Url, command_uri: &http::Uri) -> Result { + let mut url = signed_url.clone(); + let command_path = webdriver_command_path(signed_url, command_uri); + let base_path = signed_url.path().trim_end_matches('/'); + let path = if base_path.is_empty() { + format!("/{command_path}") + } else if command_path.is_empty() { + base_path.to_string() + } else { + format!("{base_path}/{command_path}") + }; + url.set_path(&path); + + let query = match (signed_url.query(), command_uri.query()) { + (Some(signed), Some(command)) if !command.is_empty() => Some(format!("{signed}&{command}")), + (Some(signed), _) => Some(signed.to_string()), + (None, Some(command)) if !command.is_empty() => Some(command.to_string()), + _ => None, + }; + url.set_query(query.as_deref()); + + Ok(url) +} + +fn map_test_grid_session(session: TestGridSession, now_epoch_seconds: i64) -> DeviceFarmSessionInfo { + let arn = session.arn().unwrap_or_default().to_string(); + let created = session.created(); + let ended = session.ended(); + DeviceFarmSessionInfo { + session_id: session_id_from_arn(&arn).unwrap_or_default(), + arn, + status: session + .status() + .map(ToString::to_string) + .unwrap_or_else(|| "UNKNOWN".to_string()), + created: created.map(ToString::to_string), + ended: ended.map(ToString::to_string), + age_seconds: age_seconds(created, ended, now_epoch_seconds), + billing_minutes: session.billing_minutes(), + selenium_properties: selenium_properties_json(session.selenium_properties()), + } +} + +fn age_seconds(created: Option<&DateTime>, ended: Option<&DateTime>, now_epoch_seconds: i64) -> Option { + let created = created?; + let end_epoch_seconds = ended.map_or(now_epoch_seconds, DateTime::secs); + Some(end_epoch_seconds - created.secs()) +} + +fn webdriver_command_path(signed_url: &url::Url, command_uri: &http::Uri) -> String { + let joined_path = command_uri.path(); + let base_path = signed_url.path().trim_end_matches('/'); + + if !base_path.is_empty() { + let base_prefix = format!("{base_path}/"); + if let Some(command) = joined_path.strip_prefix(&base_prefix) { + return command.trim_start_matches('/').to_string(); + } + + if let Some((parent, _)) = base_path.rsplit_once('/') { + let parent_prefix = if parent.is_empty() { + "/".to_string() + } else { + format!("{parent}/") + }; + if let Some(command) = joined_path.strip_prefix(&parent_prefix) { + return command.trim_start_matches('/').to_string(); + } + } + } + + joined_path.trim_start_matches('/').to_string() +} + +fn webdriver_error_is_invalid_session_id(body: &str) -> bool { + serde_json::from_str::(body).is_ok_and(|value| { + value + .get("value") + .and_then(|value| value.get("error")) + .and_then(serde_json::Value::as_str) + == Some("invalid session id") + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn extracts_session_id_from_test_grid_session_arn() { + let arn = "arn:aws:devicefarm:us-west-2:111122223333:testgrid-session:project-guid/session-guid"; + assert_eq!(session_id_from_arn(arn), Some("session-guid".to_string())); + } + + #[test] + fn maps_selenium_properties_to_json_when_possible() { + let value = selenium_properties_json(Some(r#"{"browserName":"chrome"}"#)); + assert_eq!(value, Some(serde_json::json!({"browserName": "chrome"}))); + } + + #[test] + fn keeps_non_json_selenium_properties_as_string() { + let value = selenium_properties_json(Some("browserName=chrome")); + assert_eq!(value, Some(serde_json::json!("browserName=chrome"))); + } +} diff --git a/browser/src/participant/device_farm/mod.rs b/browser/src/participant/device_farm/mod.rs new file mode 100644 index 0000000..ec00c79 --- /dev/null +++ b/browser/src/participant/device_farm/mod.rs @@ -0,0 +1,636 @@ +mod control; +mod test_grid; +mod webdriver_driver; + +use crate::{ + auth::{ + BorrowedCookie, + HyperSessionCookieManger, + }, + participant::{ + frontend::{ + FrontendAuth, + FrontendAutomation, + FrontendContext, + FrontendKindBuilder, + }, + shared::{ + messages::{ + ParticipantLogMessage, + ParticipantMessage, + }, + DriverTermination, + ParticipantDriverSession, + ParticipantLaunchSpec, + ParticipantState, + }, + }, +}; +pub use aws_sdk_devicefarm::types::TestGridSessionStatus; +use client_simulator_config::{ + media::FakeMedia, + DeviceFarmConfig, +}; +pub use control::{ + close_test_grid_session, + list_active_project_sessions, + list_project_sessions, + DeviceFarmCloseResult, + DeviceFarmSessionInfo, +}; +use eyre::{ + bail, + Context as _, + ContextCompat as _, + Report, + Result, +}; +use futures::{ + future::BoxFuture, + FutureExt as _, +}; +use std::{ + sync::Arc, + time::Duration, +}; +pub use test_grid::{ + AwsTestGrid, + TestGridApi, +}; +use thirtyfour::{ + common::config::WebDriverConfig, + prelude::{ + WebDriverError, + WebDriverResult, + }, + session::http::{ + Body, + HttpClient, + }, + CapabilitiesHelper, + ChromeCapabilities, + ChromiumLikeCapabilities, + DesiredCapabilities, + WebDriver, +}; +use tokio::{ + sync::{ + mpsc::UnboundedSender, + oneshot, + watch, + }, + task::JoinHandle, + time::{ + timeout, + MissedTickBehavior, + }, +}; +#[allow(unused_imports)] +pub(crate) use webdriver_driver::WebDriverDriver; + +const DEVICE_FARM_MAX_DURATION_MIN_SECS: u64 = 180; +const DEVICE_FARM_MAX_DURATION_MAX_SECS: u64 = 2400; +const DEVICE_FARM_IDLE_TIMEOUT_MIN_SECS: u64 = 30; +const DEVICE_FARM_IDLE_TIMEOUT_MAX_SECS: u64 = 900; +const DEVICE_FARM_FRONTEND_CLOSE_TIMEOUT: Duration = Duration::from_secs(10); + +#[allow(dead_code)] +#[derive(Debug, Clone, PartialEq)] +pub(super) struct DeviceFarmLaunchOptions { + headless: bool, + fake_media: FakeMedia, +} + +impl From<&client_simulator_config::Config> for DeviceFarmLaunchOptions { + fn from(config: &client_simulator_config::Config) -> Self { + Self { + headless: config.headless, + fake_media: config.fake_media(), + } + } +} + +#[allow(dead_code)] +pub(super) struct DeviceFarmSession { + launch_spec: ParticipantLaunchSpec, + launch_options: DeviceFarmLaunchOptions, + config: DeviceFarmConfig, + sender: UnboundedSender, + api: Arc, + auth: Option, + automation: Option>, + webdriver: Option, + cached_state: ParticipantState, + termination_tx: watch::Sender>, + termination_rx: watch::Receiver>, + poller_shutdown_tx: Option>, + poller_task: Option>, +} + +#[allow(dead_code)] +impl DeviceFarmSession { + #[allow(clippy::too_many_arguments)] + pub(super) fn new( + launch_spec: ParticipantLaunchSpec, + launch_options: DeviceFarmLaunchOptions, + config: DeviceFarmConfig, + sender: UnboundedSender, + cookie: Option, + cookie_manager: HyperSessionCookieManger, + api: Arc, + ) -> Self { + let auth = FrontendAuth::for_kind(launch_spec.frontend_kind, cookie, cookie_manager); + let (termination_tx, termination_rx) = watch::channel(None); + Self { + cached_state: ParticipantState { + username: launch_spec.username.clone(), + ..Default::default() + }, + launch_spec, + launch_options, + config, + sender, + api, + auth: Some(auth), + automation: None, + webdriver: None, + termination_tx, + termination_rx, + poller_shutdown_tx: None, + poller_task: None, + } + } + + fn log_message(&self, level: &str, message: impl ToString) { + let log_message = ParticipantLogMessage::new(level, &self.launch_spec.username, message); + log_message.write(); + if let Err(err) = self.sender.send(log_message) { + trace!(participant = %self.launch_spec.username, "Failed to send device farm log message: {err}"); + } + } + + fn log_backend_limitations(&self) { + if matches!(self.launch_options.fake_media, FakeMedia::FileOrUrl(_)) { + self.log_message( + "warn", + "Device Farm backend cannot use a local fake-media file/URL; using the synthetic fake device instead", + ); + } + } + + fn build_capabilities(config: &DeviceFarmConfig) -> Result { + let mut caps = DesiredCapabilities::chrome(); + // Synthetic fake media only. Device Farm has no access to local fake-media files. + caps.add_arg("--use-fake-ui-for-media-stream")?; + caps.add_arg("--use-fake-device-for-media-stream")?; + caps.insert_base_capability( + "aws:maxDurationSecs".to_string(), + serde_json::json!(aws_duration_secs( + config.session_max_duration_ms, + DEVICE_FARM_MAX_DURATION_MIN_SECS, + DEVICE_FARM_MAX_DURATION_MAX_SECS, + )), + ); + caps.insert_base_capability( + "aws:idleTimeoutSecs".to_string(), + serde_json::json!(aws_duration_secs( + config.idle_timeout_ms, + DEVICE_FARM_IDLE_TIMEOUT_MIN_SECS, + DEVICE_FARM_IDLE_TIMEOUT_MAX_SECS, + )), + ); + Ok(caps) + } + + async fn connect(api: Arc, config: DeviceFarmConfig) -> Result { + let url = api + .create_test_grid_url(&config.project_arn, config.url_expires_seconds) + .await?; + let caps = Self::build_capabilities(&config)?; + let endpoint_url: url::Url = url + .parse() + .context("Device Farm returned an invalid Selenium endpoint URL")?; + debug!( + endpoint_host = endpoint_url.host_str().unwrap_or(""), + endpoint_uses_wd_hub = endpoint_url.path().ends_with("/wd/hub"), + "Received Device Farm Selenium endpoint", + ); + let client = DeviceFarmHttpClient::new(endpoint_url); + // Device Farm can take 60-120s before a requested session becomes usable. + WebDriver::new_with_config_and_client(&url, caps, WebDriverConfig::default(), client) + .await + .context("failed to connect to Device Farm Selenium endpoint") + } + + async fn start_inner(&mut self) -> Result<()> { + if self.automation.is_some() { + bail!("Device Farm session already started"); + } + self.log_backend_limitations(); + + self.log_message( + "info", + format!( + "Requesting Device Farm test grid URL for project {}", + self.config.project_arn + ), + ); + let driver = Self::connect(Arc::clone(&self.api), self.config.clone()).await?; + self.webdriver = Some(driver.clone()); + let webdriver_driver = WebDriverDriver::new(driver); + + let auth = self.auth.take().context("device farm auth already consumed")?; + let context = FrontendContext { + launch_spec: self.launch_spec.clone(), + driver: Box::new(webdriver_driver), + sender: self.sender.clone(), + }; + let mut automation = FrontendKindBuilder::build(context, auth).await?; + + self.termination_tx.send_replace(None); + self.start_max_duration_poller(); + if let Err(err) = automation.join().await { + self.stop_max_duration_poller().await; + return Err(err); + } + + self.automation = Some(automation); + self.cached_state.running = true; + self.log_message("info", "Connected Device Farm browser session"); + Ok(()) + } + + fn automation_mut(&mut self) -> Result<&mut (dyn FrontendAutomation + 'static)> { + self.automation + .as_deref_mut() + .context("Device Farm automation not started") + } + + fn effective_poll_interval(&self) -> Duration { + let configured = self.config.health_poll_interval_ms.max(1); + let budget = aws_duration_secs( + self.config.idle_timeout_ms, + DEVICE_FARM_IDLE_TIMEOUT_MIN_SECS, + DEVICE_FARM_IDLE_TIMEOUT_MAX_SECS, + ) + .saturating_mul(500) + .max(1); + Duration::from_millis(configured.min(budget)) + } + + fn effective_max_duration(&self) -> Duration { + Duration::from_secs(aws_duration_secs( + self.config.session_max_duration_ms, + DEVICE_FARM_MAX_DURATION_MIN_SECS, + DEVICE_FARM_MAX_DURATION_MAX_SECS, + )) + } + + fn start_max_duration_poller(&mut self) { + // Periodic runtime state refreshes send WebDriver commands and keep the + // AWS idle timeout alive. This task only mirrors the AWS hard session cap. + let interval = self.effective_poll_interval(); + let max_duration = self.effective_max_duration(); + let termination_tx = self.termination_tx.clone(); + let (shutdown_tx, mut shutdown_rx) = oneshot::channel(); + + let task = tokio::spawn(async move { + let mut ticker = tokio::time::interval(interval); + ticker.set_missed_tick_behavior(MissedTickBehavior::Delay); + ticker.tick().await; + let started = tokio::time::Instant::now(); + loop { + tokio::select! { + _ = &mut shutdown_rx => break, + _ = ticker.tick() => { + if started.elapsed() >= max_duration { + let _ = termination_tx.send(Some(DriverTermination::new( + "warn", + "Device Farm session reached its configured max duration", + ))); + break; + } + } + } + } + }); + + self.poller_shutdown_tx = Some(shutdown_tx); + self.poller_task = Some(task); + } + + async fn stop_max_duration_poller(&mut self) { + if let Some(tx) = self.poller_shutdown_tx.take() { + let _ = tx.send(()); + } + if let Some(task) = self.poller_task.take() { + let _ = task.await; + } + } + + async fn close_inner(&mut self) -> Result<()> { + self.stop_max_duration_poller().await; + + let mut automation = self.automation.take(); + let driver = self.webdriver.take(); + + // Close has two levels: leave the meeting if the frontend still looks + // joined, then always tear down the Selenium session. Do not refresh + // state here; remote missing-selector calls can block shutdown long + // enough to prevent the explicit WebDriver quit below. + if self.cached_state.joined { + if let Some(automation) = automation.as_mut() { + match timeout(DEVICE_FARM_FRONTEND_CLOSE_TIMEOUT, automation.leave()).await { + Ok(Ok(())) => {} + Ok(Err(err)) => { + self.log_message("error", format!("Failed leaving space while closing: {err}")); + } + Err(_) => { + self.log_message( + "warn", + "Timed out leaving space while closing; closing WebDriver session", + ); + } + } + } + } + + if let Some(driver) = driver { + // Keep automation alive until after quit. It owns another + // WebDriver clone, and a successful quit marks the shared + // SessionHandle so dropping that clone will not run thirtyfour's + // synchronous Drop cleanup. + let driver_for_leak = driver.clone(); + if let Err(err) = driver.quit().await { + self.log_message("error", format!("Failed closing WebDriver session: {err}")); + // Explicit DELETE /session failed. Without this, thirtyfour + // will try a blocking Drop cleanup later; during Tokio runtime + // shutdown that fallback can panic because it schedules work on + // a closing executor. leak() only suppresses thirtyfour's Drop + // cleanup; it does not leak Rust memory. The remote AWS session + // may remain until its idle/max-duration timeout, which is + // preferable to crashing after explicit cleanup already failed. + if let Err(leak_err) = driver_for_leak.leak() { + debug!(participant = %self.participant_name(), "Could not suppress WebDriver drop cleanup after quit failure: {leak_err}"); + } + } + } + + drop(automation); + + self.cached_state.running = false; + self.cached_state.joined = false; + self.cached_state.screenshare_activated = false; + self.log_message("info", "Closed Device Farm browser session"); + Ok(()) + } + + async fn ping_webdriver(driver: WebDriver) -> Result<()> { + driver + .current_url() + .await + .context("Device Farm WebDriver session is not responsive")?; + Ok(()) + } + + async fn wait_for_termination_inner(&mut self) -> DriverTermination { + loop { + if let Some(termination) = self.termination_rx.borrow().clone() { + return termination; + } + if self.termination_rx.changed().await.is_err() { + return DriverTermination::new("warn", "device farm termination channel closed"); + } + } + } +} + +#[derive(Clone)] +struct DeviceFarmHttpClient { + signed_url: url::Url, + inner: reqwest::Client, +} + +impl DeviceFarmHttpClient { + fn new(signed_url: url::Url) -> Self { + Self { + signed_url, + inner: reqwest::Client::new(), + } + } +} + +#[async_trait::async_trait] +impl HttpClient for DeviceFarmHttpClient { + async fn send(&self, request: http::Request>) -> WebDriverResult> { + let request = rewrite_device_farm_request_uri(&self.signed_url, request)?; + HttpClient::send(&self.inner, request).await + } + + async fn new(&self) -> Arc { + Arc::new(Self::new(self.signed_url.clone())) + } +} + +fn rewrite_device_farm_request_uri<'a>( + signed_url: &url::Url, + request: http::Request>, +) -> WebDriverResult>> { + let (mut parts, body) = request.into_parts(); + let rewritten = signed_test_grid_command_url(signed_url, &parts.uri)?; + parts.uri = rewritten + .as_str() + .parse() + .map_err(|err| WebDriverError::ParseError(format!("invalid Device Farm WebDriver request URI: {err}")))?; + Ok(http::Request::from_parts(parts, body)) +} + +fn signed_test_grid_command_url(signed_url: &url::Url, command_uri: &http::Uri) -> WebDriverResult { + control::signed_test_grid_command_url(signed_url, command_uri) + .map_err(|err| WebDriverError::ParseError(format!("invalid Device Farm WebDriver request URI: {err}"))) +} + +fn aws_duration_secs(duration_ms: u64, min_secs: u64, max_secs: u64) -> u64 { + duration_ms.div_ceil(1000).clamp(min_secs, max_secs) +} + +impl ParticipantDriverSession for DeviceFarmSession { + fn participant_name(&self) -> &str { + &self.launch_spec.username + } + + fn start(&mut self) -> BoxFuture<'_, Result<()>> { + self.start_inner().boxed() + } + + fn handle_command(&mut self, message: ParticipantMessage) -> BoxFuture<'_, Result<()>> { + async move { self.automation_mut()?.handle_command(message).await }.boxed() + } + + fn refresh_state(&mut self) -> BoxFuture<'_, Result> { + async move { + if self.automation.is_none() { + return Ok(self.cached_state.clone()); + } + + let driver = self + .webdriver + .as_ref() + .context("Device Farm WebDriver session not started")? + .clone(); + Self::ping_webdriver(driver).await?; + let automation = self.automation.as_mut().context("Device Farm automation not started")?; + let state = automation.refresh_state().await?; + self.cached_state = state.clone(); + Ok(state) + } + .boxed() + } + + fn close(&mut self) -> BoxFuture<'_, Result<()>> { + self.close_inner().boxed() + } + + fn wait_for_termination(&mut self) -> BoxFuture<'_, DriverTermination> { + self.wait_for_termination_inner().boxed() + } + + fn state_refresh_interval(&self) -> Option { + Some(self.effective_poll_interval()) + } + + fn state_refresh_error_termination(&self, err: &Report) -> Option { + Some(DriverTermination::new( + "warn", + format!("Device Farm browser session stopped responding while refreshing state: {err}"), + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use std::sync::{ + atomic::{ + AtomicBool, + Ordering, + }, + Arc, + }; + use tokio::sync::mpsc::unbounded_channel; + + #[test] + fn capabilities_clamp_aws_duration_ranges() { + let high_max_low_idle = capabilities_for_duration(3_600_000, 5_000); + assert_eq!(high_max_low_idle._get("aws:maxDurationSecs"), Some(&json!(2400))); + assert_eq!(high_max_low_idle._get("aws:idleTimeoutSecs"), Some(&json!(30))); + + let low_max_high_idle = capabilities_for_duration(60_000, 2_000_000); + assert_eq!(low_max_high_idle._get("aws:maxDurationSecs"), Some(&json!(180))); + assert_eq!(low_max_high_idle._get("aws:idleTimeoutSecs"), Some(&json!(900))); + } + + fn capabilities_for_duration(session_max_duration_ms: u64, idle_timeout_ms: u64) -> ChromeCapabilities { + let config = DeviceFarmConfig { + session_max_duration_ms, + idle_timeout_ms, + ..DeviceFarmConfig::default() + }; + DeviceFarmSession::build_capabilities(&config).unwrap() + } + + #[tokio::test] + async fn close_uses_cached_state_instead_of_refreshing_frontend_before_leave() { + let refreshed = Arc::new(AtomicBool::new(false)); + let left = Arc::new(AtomicBool::new(false)); + let (termination_tx, termination_rx) = watch::channel(None); + let (sender, _receiver) = unbounded_channel(); + + let mut session = DeviceFarmSession { + launch_spec: launch_spec(), + launch_options: DeviceFarmLaunchOptions { + headless: true, + fake_media: FakeMedia::default(), + }, + config: DeviceFarmConfig::default(), + sender, + api: Arc::new(UnusedTestGridApi), + auth: None, + automation: Some(Box::new(RecordingAutomation { + refreshed: Arc::clone(&refreshed), + left: Arc::clone(&left), + })), + webdriver: None, + cached_state: ParticipantState { + running: true, + joined: true, + screenshare_activated: true, + ..Default::default() + }, + termination_tx, + termination_rx, + poller_shutdown_tx: None, + poller_task: None, + }; + + session.close_inner().await.unwrap(); + + assert!(!refreshed.load(Ordering::SeqCst)); + assert!(left.load(Ordering::SeqCst)); + assert!(!session.cached_state.running); + assert!(!session.cached_state.joined); + assert!(!session.cached_state.screenshare_activated); + } + + fn launch_spec() -> ParticipantLaunchSpec { + let config = client_simulator_config::Config { + url: Some("https://example.com/m/demo".parse().unwrap()), + ..Default::default() + }; + let participant_config = client_simulator_config::ParticipantConfig::new(&config, Some("sim-user")).unwrap(); + ParticipantLaunchSpec::from(participant_config) + } + + struct RecordingAutomation { + refreshed: Arc, + left: Arc, + } + + impl FrontendAutomation for RecordingAutomation { + fn join(&mut self) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + + fn leave(&mut self) -> BoxFuture<'_, Result<()>> { + async move { + self.left.store(true, Ordering::SeqCst); + Ok(()) + } + .boxed() + } + + fn handle_command(&mut self, _message: ParticipantMessage) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + + fn refresh_state(&mut self) -> BoxFuture<'_, Result> { + async move { + self.refreshed.store(true, Ordering::SeqCst); + Ok(ParticipantState { + joined: true, + ..Default::default() + }) + } + .boxed() + } + } + + struct UnusedTestGridApi; + + impl TestGridApi for UnusedTestGridApi { + fn create_test_grid_url(&self, _project_arn: &str, _expires_seconds: u64) -> BoxFuture<'_, Result> { + async { unreachable!("close test should not request a Device Farm URL") }.boxed() + } + } +} diff --git a/browser/src/participant/device_farm/test_grid.rs b/browser/src/participant/device_farm/test_grid.rs new file mode 100644 index 0000000..7f8bab7 --- /dev/null +++ b/browser/src/participant/device_farm/test_grid.rs @@ -0,0 +1,144 @@ +use aws_sdk_devicefarm::types::{ + TestGridSession, + TestGridSessionStatus, +}; +use client_simulator_config::{ + DEVICE_FARM_AWS_ACCESS_KEY_ID, + DEVICE_FARM_AWS_SECRET_ACCESS_KEY, +}; +use eyre::{ + bail, + Context as _, + Result, +}; +use futures::{ + future::BoxFuture, + FutureExt as _, +}; + +/// Minimal seam over the Device Farm Test Grid control plane so the session +/// logic can be tested without real AWS calls. +pub trait TestGridApi: Send + Sync { + /// Create a short-lived Selenium Remote WebDriver URL for `project_arn`. + fn create_test_grid_url(&self, project_arn: &str, expires_seconds: u64) -> BoxFuture<'_, Result>; + + fn list_test_grid_sessions( + &self, + _project_arn: &str, + _status: Option, + ) -> BoxFuture<'_, Result>> { + async move { bail!("ListTestGridSessions is not implemented by this TestGridApi") }.boxed() + } + + fn get_test_grid_session( + &self, + _project_arn: &str, + _session_id: &str, + ) -> BoxFuture<'_, Result>> { + async move { bail!("GetTestGridSession is not implemented by this TestGridApi") }.boxed() + } +} + +/// Real implementation backed by `aws-sdk-devicefarm`. +pub struct AwsTestGrid { + region: String, + client: tokio::sync::OnceCell, +} + +impl AwsTestGrid { + pub fn new(region: &str) -> Self { + Self { + region: region.to_owned(), + client: tokio::sync::OnceCell::new(), + } + } + + async fn client(&self) -> &aws_sdk_devicefarm::Client { + let region = self.region.clone(); + self.client + .get_or_init(|| async move { + let region = aws_sdk_devicefarm::config::Region::new(region); + let credentials = aws_sdk_devicefarm::config::Credentials::new( + DEVICE_FARM_AWS_ACCESS_KEY_ID, + DEVICE_FARM_AWS_SECRET_ACCESS_KEY, + None, + None, + "embedded-device-farm-env", + ); + let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .region(region) + .credentials_provider(credentials) + .load() + .await; + aws_sdk_devicefarm::Client::new(&config) + }) + .await + } +} + +impl TestGridApi for AwsTestGrid { + fn create_test_grid_url(&self, project_arn: &str, expires_seconds: u64) -> BoxFuture<'_, Result> { + let project_arn = project_arn.to_owned(); + async move { + let output = self + .client() + .await + .create_test_grid_url() + .project_arn(project_arn) + .expires_in_seconds(expires_seconds as i32) + .send() + .await + .context("CreateTestGridUrl failed")?; + output + .url() + .map(str::to_owned) + .ok_or_else(|| eyre::eyre!("CreateTestGridUrl returned no url")) + } + .boxed() + } + + fn list_test_grid_sessions( + &self, + project_arn: &str, + status: Option, + ) -> BoxFuture<'_, Result>> { + let project_arn = project_arn.to_owned(); + async move { + let mut builder = self.client().await.list_test_grid_sessions().project_arn(project_arn); + if let Some(status) = status { + builder = builder.status(status); + } + + let mut pages = builder.into_paginator().send(); + let mut sessions = Vec::new(); + while let Some(page) = pages.next().await { + let page = page.context("ListTestGridSessions failed")?; + sessions.extend(page.test_grid_sessions().iter().cloned()); + } + Ok(sessions) + } + .boxed() + } + + fn get_test_grid_session( + &self, + project_arn: &str, + session_id: &str, + ) -> BoxFuture<'_, Result>> { + let project_arn = project_arn.to_owned(); + let session_id = session_id.to_owned(); + async move { + let output = self + .client() + .await + .get_test_grid_session() + .project_arn(project_arn) + .session_id(session_id) + .send() + .await + .context("GetTestGridSession failed")?; + Ok(output.test_grid_session().cloned()) + } + .boxed() + } +} diff --git a/browser/src/participant/device_farm/webdriver_driver.rs b/browser/src/participant/device_farm/webdriver_driver.rs new file mode 100644 index 0000000..8a89eb2 --- /dev/null +++ b/browser/src/participant/device_farm/webdriver_driver.rs @@ -0,0 +1,155 @@ +use crate::participant::frontend::BrowserDriver; +use eyre::{ + Context as _, + Result, +}; +use futures::{ + future::BoxFuture, + FutureExt as _, +}; +use std::time::Duration; +use thirtyfour::{ + By, + Cookie, + WebDriver, +}; + +/// `BrowserDriver` implementation backed by a remote Selenium `WebDriver` +/// (AWS Device Farm Test Grid endpoint). +#[allow(dead_code)] +pub(crate) struct WebDriverDriver { + driver: WebDriver, +} + +#[allow(dead_code)] +impl WebDriverDriver { + pub(crate) fn new(driver: WebDriver) -> Self { + Self { driver } + } + + /// Consume the driver and end the remote session. + pub(crate) async fn quit(self) -> Result<()> { + self.driver.quit().await.context("failed to quit WebDriver session") + } + + /// A cheap no-op WebDriver command, used by the keep-alive poller to stay + /// inside `aws:idleTimeoutSecs` and to detect a dead session. + pub(crate) async fn ping(&self) -> Result<()> { + self.driver + .current_url() + .await + .context("WebDriver session is not responsive")?; + Ok(()) + } +} + +impl BrowserDriver for WebDriverDriver { + fn goto(&self, url: &str) -> BoxFuture<'_, Result<()>> { + let url = url.to_owned(); + async move { self.driver.goto(&url).await.context("failed to navigate") }.boxed() + } + + fn exists(&self, selector: &str) -> BoxFuture<'_, Result> { + let selector = selector.to_owned(); + async move { Ok(self.driver.find(By::Css(&selector)).await.is_ok()) }.boxed() + } + + fn wait_for(&self, selector: &str, timeout: Duration) -> BoxFuture<'_, Result<()>> { + let selector = selector.to_owned(); + async move { + let start = std::time::Instant::now(); + loop { + if self.driver.find(By::Css(&selector)).await.is_ok() { + return Ok(()); + } + if start.elapsed() > timeout { + eyre::bail!("timeout waiting for selector {selector}"); + } + tokio::time::sleep(Duration::from_millis(200)).await; + } + } + .boxed() + } + + fn click(&self, selector: &str) -> BoxFuture<'_, Result<()>> { + let selector = selector.to_owned(); + async move { + self.driver + .find(By::Css(&selector)) + .await + .with_context(|| format!("Could not find {selector}"))? + .click() + .await + .with_context(|| format!("Could not click {selector}")) + } + .boxed() + } + + fn fill(&self, selector: &str, text: &str) -> BoxFuture<'_, Result<()>> { + let selector = selector.to_owned(); + let text = text.to_owned(); + async move { + let element = self + .driver + .find(By::Css(&selector)) + .await + .with_context(|| format!("Could not find {selector}"))?; + element.clear().await.context("failed to clear element")?; + element.send_keys(&text).await.context("failed to type into element")?; + Ok(()) + } + .boxed() + } + + fn attribute(&self, selector: &str, name: &str) -> BoxFuture<'_, Result>> { + let selector = selector.to_owned(); + let name = name.to_owned(); + async move { + match self.driver.find(By::Css(&selector)).await { + Ok(element) => element + .attr(&name) + .await + .with_context(|| format!("failed to read attribute {name}")), + Err(_) => Ok(None), + } + } + .boxed() + } + + fn eval(&self, js_body: &str, arg: Option) -> BoxFuture<'_, Result> { + let js_body = js_body.to_owned(); + async move { + let args = match arg { + Some(value) => vec![value], + None => vec![], + }; + let ret = self + .driver + .execute(&js_body, args) + .await + .context("failed to execute script")?; + Ok(ret.json().clone()) + } + .boxed() + } + + fn set_cookie(&self, domain: &str, name: &str, value: &str) -> BoxFuture<'_, Result<()>> { + let domain = domain.to_owned(); + let name = name.to_owned(); + let value = value.to_owned(); + async move { + // WebDriver requires being on the target origin before adding a cookie. + let origin = format!("https://{domain}/"); + self.driver + .goto(&origin) + .await + .with_context(|| format!("failed to open origin {origin} before setting cookie"))?; + let mut cookie = Cookie::new(name, value); + cookie.set_domain(domain); + cookie.set_path("/"); + self.driver.add_cookie(cookie).await.context("failed to add cookie")?; + Ok(()) + } + .boxed() + } +} diff --git a/browser/src/participant/frontend/builder.rs b/browser/src/participant/frontend/builder.rs new file mode 100644 index 0000000..2f05368 --- /dev/null +++ b/browser/src/participant/frontend/builder.rs @@ -0,0 +1,62 @@ +use super::{ + core::ParticipantInner, + driver::{ + FrontendAutomation, + FrontendContext, + }, + lite::ParticipantInnerLite, +}; +use crate::{ + auth::{ + BorrowedCookie, + HyperSessionCookieManger, + }, + participant::shared::ResolvedFrontendKind, +}; +use eyre::Result; + +/// How to authenticate the frontend. HyperLite needs no cookie. +pub(in crate::participant) enum FrontendAuth { + HyperCore { + cookie: Option, + cookie_manager: HyperSessionCookieManger, + }, + HyperLite, +} + +impl FrontendAuth { + pub(in crate::participant) fn for_kind( + kind: ResolvedFrontendKind, + cookie: Option, + cookie_manager: HyperSessionCookieManger, + ) -> Self { + match kind { + ResolvedFrontendKind::HyperCore => Self::HyperCore { cookie, cookie_manager }, + ResolvedFrontendKind::HyperLite => Self::HyperLite, + } + } +} + +/// Builds the concrete `FrontendAutomation` for a context + auth, shared by all backends. +pub(in crate::participant) struct FrontendKindBuilder; + +impl FrontendKindBuilder { + pub(in crate::participant) async fn build( + context: FrontendContext, + auth: FrontendAuth, + ) -> Result> { + match auth { + FrontendAuth::HyperCore { cookie, cookie_manager } => { + let cookie = if let Some(cookie) = cookie { + cookie + } else { + cookie_manager + .fetch_new_cookie(context.launch_spec.base_url(), context.participant_name()) + .await? + }; + Ok(Box::new(ParticipantInner::new(context, cookie))) + } + FrontendAuth::HyperLite => Ok(Box::new(ParticipantInnerLite::new(context))), + } + } +} diff --git a/browser/src/participant/frontend/commands.rs b/browser/src/participant/frontend/commands.rs new file mode 100644 index 0000000..b035c1b --- /dev/null +++ b/browser/src/participant/frontend/commands.rs @@ -0,0 +1,263 @@ +use super::driver::BrowserDriver; +use client_simulator_config::{ + NoiseSuppression, + VideoConstraint, +}; +use eyre::{ + Context as _, + Result, +}; + +// Getters return a value via `return ;`. Setters mutate and ignore the result. +const NOISE_SUPPRESSION_GET: &str = "return hyper.settings.media.noiseSuppression;"; +const NOISE_SUPPRESSION_SET: &str = "hyper.settings.media.actions.setNoiseSuppression(arguments[0]);"; +const AUTO_GAIN_GET: &str = "return hyper.settings.media.autoGainControl;"; +const AUTO_GAIN_SET: &str = "hyper.settings.media.actions.setAutoGainControl(arguments[0]);"; +const BACKGROUND_BLUR_GET: &str = "return hyper.settings.media.backgroundBlur;"; +const BACKGROUND_BLUR_SET: &str = "hyper.settings.media.actions.setBackgroundBlur(arguments[0]);"; +const VIDEO_CONSTRAINT_PUBLISH_GET: &str = "return hyper.settings.media.videoConstraintPublishWebcam;"; +const VIDEO_CONSTRAINT_PUBLISH_SET: &str = + "hyper.settings.media.actions.setVideoConstraintPublishWebcam(arguments[0]);"; +const VIDEO_CONSTRAINT_SUBSCRIBE_GET: &str = "return hyper.settings.media.videoConstraintSubscribe;"; +const VIDEO_CONSTRAINT_SUBSCRIBE_SET: &str = "hyper.settings.media.actions.setVideoConstraintSubscribe(arguments[0]);"; +const VIDEO_MAX_CONCURRENT_TRACKS_GET: &str = "return hyper.settings.media.videoMaxConcurrentTracks;"; +const VIDEO_MAX_CONCURRENT_TRACKS_SET: &str = "hyper.settings.media.actions.setVideoMaxConcurrentTracks(arguments[0]);"; +const FORCE_WEBRTC_GET: &str = "return hyper.settings.sessionDebug.forceWebrtc;"; +const FORCE_WEBRTC_SET: &str = "hyper.settings.sessionDebug.actions.setForceWebrtc(arguments[0]);"; + +async fn get_string(driver: &dyn BrowserDriver, js: &str) -> Result { + let value = driver.eval(js, None).await?; + serde_json::from_value(value).context("failed to read string from eval result") +} + +async fn get_bool(driver: &dyn BrowserDriver, js: &str) -> Result { + let value = driver.eval(js, None).await?; + serde_json::from_value(value).context("failed to read bool from eval result") +} + +async fn set_value(driver: &dyn BrowserDriver, js: &str, value: S) -> Result<()> { + let arg = serde_json::to_value(value).context("failed to serialize eval argument")?; + driver.eval(js, Some(arg)).await?; + Ok(()) +} + +pub(super) async fn get_noise_suppression(driver: &dyn BrowserDriver) -> Result { + get_string(driver, NOISE_SUPPRESSION_GET) + .await? + .parse::() + .context("failed to parse NoiseSuppression from string") +} + +pub(super) async fn set_noise_suppression(driver: &dyn BrowserDriver, value: NoiseSuppression) -> Result<()> { + set_value(driver, NOISE_SUPPRESSION_SET, value.to_string()).await +} + +pub(super) async fn get_auto_gain_control(driver: &dyn BrowserDriver) -> Result { + get_bool(driver, AUTO_GAIN_GET).await +} + +pub(super) async fn set_auto_gain_control(driver: &dyn BrowserDriver, value: bool) -> Result<()> { + set_value(driver, AUTO_GAIN_SET, value).await +} + +pub(super) async fn get_background_blur(driver: &dyn BrowserDriver) -> Result { + get_bool(driver, BACKGROUND_BLUR_GET).await +} + +pub(super) async fn set_background_blur(driver: &dyn BrowserDriver, value: bool) -> Result<()> { + set_value(driver, BACKGROUND_BLUR_SET, value).await +} + +pub(super) async fn get_video_constraint_publish_webcam(driver: &dyn BrowserDriver) -> Result { + get_string(driver, VIDEO_CONSTRAINT_PUBLISH_GET) + .await? + .parse::() + .context("failed to parse videoConstraintPublishWebcam from string") +} + +pub(super) async fn set_video_constraint_publish_webcam( + driver: &dyn BrowserDriver, + value: VideoConstraint, +) -> Result<()> { + set_value(driver, VIDEO_CONSTRAINT_PUBLISH_SET, value.to_string()).await +} + +pub(super) async fn get_video_constraint_subscribe(driver: &dyn BrowserDriver) -> Result { + get_string(driver, VIDEO_CONSTRAINT_SUBSCRIBE_GET) + .await? + .parse::() + .context("failed to parse videoConstraintSubscribe from string") +} + +pub(super) async fn set_video_constraint_subscribe(driver: &dyn BrowserDriver, value: VideoConstraint) -> Result<()> { + set_value(driver, VIDEO_CONSTRAINT_SUBSCRIBE_SET, value.to_string()).await +} + +pub(super) async fn get_video_max_concurrent_tracks(driver: &dyn BrowserDriver) -> Result> { + // Read as a nullable float to tolerate the page returning e.g. `2.0`, then cast. + let value = driver.eval(VIDEO_MAX_CONCURRENT_TRACKS_GET, None).await?; + let as_f64: Option = + serde_json::from_value(value).context("failed to read videoMaxConcurrentTracks from eval result")?; + Ok(as_f64.map(|value| value as usize)) +} + +pub(super) async fn set_video_max_concurrent_tracks(driver: &dyn BrowserDriver, value: Option) -> Result<()> { + set_value(driver, VIDEO_MAX_CONCURRENT_TRACKS_SET, value).await +} + +pub(super) async fn get_force_webrtc(driver: &dyn BrowserDriver) -> Result { + get_bool(driver, FORCE_WEBRTC_GET).await +} + +pub(super) async fn set_force_webrtc(driver: &dyn BrowserDriver, value: bool) -> Result<()> { + set_value(driver, FORCE_WEBRTC_SET, value).await +} + +#[cfg(test)] +mod tests { + use super::*; + use client_simulator_config::VideoConstraint; + use eyre::Result; + use futures::{ + future::BoxFuture, + FutureExt as _, + }; + use serde_json::json; + use std::{ + sync::Mutex, + time::Duration, + }; + + #[derive(Default)] + struct RecordingDriver { + calls: Mutex)>>, + next_result: Mutex, + } + + impl RecordingDriver { + fn with_result(value: serde_json::Value) -> Self { + Self { + calls: Mutex::new(Vec::new()), + next_result: Mutex::new(value), + } + } + + fn calls(&self) -> Vec<(String, Option)> { + self.calls.lock().unwrap().clone() + } + } + + impl BrowserDriver for RecordingDriver { + fn goto(&self, _url: &str) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + + fn exists(&self, _selector: &str) -> BoxFuture<'_, Result> { + async { Ok(false) }.boxed() + } + + fn wait_for(&self, _selector: &str, _timeout: Duration) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + + fn click(&self, _selector: &str) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + + fn fill(&self, _selector: &str, _text: &str) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + + fn attribute(&self, _selector: &str, _name: &str) -> BoxFuture<'_, Result>> { + async { Ok(None) }.boxed() + } + + fn eval(&self, js_body: &str, arg: Option) -> BoxFuture<'_, Result> { + self.calls.lock().unwrap().push((js_body.to_string(), arg)); + let value = self.next_result.lock().unwrap().clone(); + async move { Ok(value) }.boxed() + } + + fn set_cookie(&self, _domain: &str, _name: &str, _value: &str) -> BoxFuture<'_, Result<()>> { + async { Ok(()) }.boxed() + } + } + + #[tokio::test] + async fn sets_video_constraints_through_media_settings_api() { + let driver = RecordingDriver::default(); + + set_video_constraint_publish_webcam(&driver, VideoConstraint::P480) + .await + .unwrap(); + set_video_constraint_subscribe(&driver, VideoConstraint::P720) + .await + .unwrap(); + set_video_max_concurrent_tracks(&driver, Some(2)).await.unwrap(); + set_video_max_concurrent_tracks(&driver, None).await.unwrap(); + + assert_eq!( + driver.calls(), + vec![ + ( + "hyper.settings.media.actions.setVideoConstraintPublishWebcam(arguments[0]);".to_string(), + Some(json!("480p")), + ), + ( + "hyper.settings.media.actions.setVideoConstraintSubscribe(arguments[0]);".to_string(), + Some(json!("720p")), + ), + ( + "hyper.settings.media.actions.setVideoMaxConcurrentTracks(arguments[0]);".to_string(), + Some(json!(2)), + ), + ( + "hyper.settings.media.actions.setVideoMaxConcurrentTracks(arguments[0]);".to_string(), + Some(serde_json::Value::Null), + ), + ], + ); + } + + #[tokio::test] + async fn gets_video_constraints_from_media_settings_api() { + let driver = RecordingDriver::with_result(json!("360p")); + + let value = get_video_constraint_publish_webcam(&driver).await.unwrap(); + + assert_eq!(value, VideoConstraint::P360); + assert_eq!( + driver.calls(), + vec![( + "return hyper.settings.media.videoConstraintPublishWebcam;".to_string(), + None, + )], + ); + } + + #[tokio::test] + async fn gets_nullable_video_max_concurrent_tracks() { + let driver = RecordingDriver::with_result(serde_json::Value::Null); + + let value = get_video_max_concurrent_tracks(&driver).await.unwrap(); + + assert_eq!(value, None); + assert_eq!( + driver.calls(), + vec![( + "return hyper.settings.media.videoMaxConcurrentTracks;".to_string(), + None, + )], + ); + } + + #[tokio::test] + async fn reads_integer_valued_max_concurrent_tracks_even_as_float() { + // The page may hand back a JSON number that serde sees as a float (e.g. 2.0). + let driver = RecordingDriver::with_result(json!(2.0)); + + let value = get_video_max_concurrent_tracks(&driver).await.unwrap(); + + assert_eq!(value, Some(2)); + } +} diff --git a/browser/src/participant/local/core.rs b/browser/src/participant/frontend/core.rs similarity index 57% rename from browser/src/participant/local/core.rs rename to browser/src/participant/frontend/core.rs index 6c443fd..129490f 100644 --- a/browser/src/participant/local/core.rs +++ b/browser/src/participant/frontend/core.rs @@ -10,29 +10,29 @@ use super::{ get_background_blur, get_force_webrtc, get_noise_suppression, - get_outgoing_camera_resolution, + get_video_constraint_publish_webcam, + get_video_constraint_subscribe, + get_video_max_concurrent_tracks, set_auto_gain_control, set_background_blur, set_force_webrtc, set_noise_suppression, - set_outgoing_camera_resolution, + set_video_constraint_publish_webcam, + set_video_constraint_subscribe, + set_video_max_concurrent_tracks, }, - frontend::{ - element_state, + driver::{ + decode_test_state, FrontendAutomation, FrontendContext, }, + selectors::classic, }; -use crate::{ - auth::BorrowedCookie, - participant::local::selectors::classic, - util::wait_for_element, -}; -use chromiumoxide::Element; +use crate::auth::BorrowedCookie; use client_simulator_config::{ NoiseSuppression, TransportMode, - WebcamResolution, + VideoConstraint, }; use eyre::{ Context as _, @@ -57,12 +57,17 @@ impl ParticipantInner { } async fn set_cookie(&self) -> Result<()> { - let domain = self.context.launch_spec.session_url.host_str().unwrap_or("localhost"); - let cookie = self.auth.as_browser_cookie_for(domain)?; - + let domain = self + .context + .launch_spec + .session_url + .host_str() + .unwrap_or("localhost") + .to_owned(); + let value = self.auth.raw_value().to_owned(); self.context - .page - .set_cookies(vec![cookie]) + .driver + .set_cookie(&domain, "hyper_session", &value) .await .context("failed to set cookie")?; @@ -81,26 +86,22 @@ impl ParticipantInner { self.set_cookie().await?; self.context - .page - .goto(self.context.launch_spec.session_url.to_string()) + .driver + .goto(self.context.launch_spec.session_url.as_str()) .await .context("failed to wait for navigation response")?; debug!(participant = %self.participant_name(), "Navigated to page"); self.context.send_log_message("debug", "Navigated to page"); - let input = wait_for_element(&self.context.page, classic::NAME_INPUT, Duration::from_secs(30)) + self.context + .driver + .wait_for(classic::NAME_INPUT, Duration::from_secs(30)) .await .context("failed to find input name field")?; - input - .focus() - .await - .context("failed to focus on the name input")? - .call_js_fn("function() { this.value = ''; }", true) - .await - .context("failed to empty current name")?; - input - .type_str(&self.context.launch_spec.username) + self.context + .driver + .fill(classic::NAME_INPUT, &self.context.launch_spec.username) .await .context("failed to insert name")?; @@ -113,7 +114,10 @@ impl ParticipantInner { ), ); - wait_for_element(&self.context.page, classic::JOIN_BUTTON, Duration::from_secs(30)).await?; + self.context + .driver + .wait_for(classic::JOIN_BUTTON, Duration::from_secs(30)) + .await?; if let Err(err) = self.apply_all_settings(true).await { error!( @@ -123,16 +127,17 @@ impl ParticipantInner { } self.context - .find_element(classic::JOIN_BUTTON) - .await? - .click() + .driver + .click(classic::JOIN_BUTTON) .await .context("failed to click join button")?; debug!(participant = %self.participant_name(), "Clicked on the join button"); self.context.send_log_message("debug", "Clicked on the join button"); - wait_for_element(&self.context.page, classic::LEAVE_BUTTON, Duration::from_secs(30)) + self.context + .driver + .wait_for(classic::LEAVE_BUTTON, Duration::from_secs(30)) .await .context("We haven't joined the space, cannot find the leave button")?; @@ -144,20 +149,27 @@ impl ParticipantInner { async fn apply_all_settings(&self, in_lobby: bool) -> Result<()> { let settings = &self.context.launch_spec.settings; + let driver = self.context.driver.as_ref(); - set_auto_gain_control(&self.context.page, settings.auto_gain_control) + set_auto_gain_control(driver, settings.auto_gain_control) .await .context("failed to set auto gain control")?; - set_noise_suppression(&self.context.page, settings.noise_suppression) + set_noise_suppression(driver, settings.noise_suppression) .await .context("failed to set noise suppression")?; - set_background_blur(&self.context.page, settings.blur) + set_background_blur(driver, settings.blur) .await .context("failed to set background blur")?; - set_outgoing_camera_resolution(&self.context.page, &settings.resolution) + set_video_constraint_publish_webcam(driver, settings.video_constraint_publish_webcam) .await - .context("failed to set outgoing camera resolution")?; - set_force_webrtc(&self.context.page, settings.transport == TransportMode::WebRTC) + .context("failed to set outgoing webcam video constraint")?; + set_video_constraint_subscribe(driver, settings.video_constraint_subscribe) + .await + .context("failed to set incoming video constraint")?; + set_video_max_concurrent_tracks(driver, settings.video_max_concurrent_tracks) + .await + .context("failed to set max concurrent video tracks")?; + set_force_webrtc(driver, settings.transport == TransportMode::WebRTC) .await .context("failed to set transport mode")?; @@ -177,9 +189,9 @@ impl ParticipantInner { } async fn leave_session(&mut self) -> Result<()> { - self.leave_button() - .await? - .click() + self.context + .driver + .click(classic::LEAVE_BUTTON) .await .context("Could not click on the leave space button")?; @@ -190,9 +202,9 @@ impl ParticipantInner { } async fn toggle_audio_inner(&self) -> Result<()> { - self.mute_button() - .await? - .click() + self.context + .driver + .click(classic::MUTE_BUTTON) .await .context("Could not click on the toggle audio button")?; @@ -203,9 +215,9 @@ impl ParticipantInner { } async fn toggle_video_inner(&self) -> Result<()> { - self.camera_button() - .await? - .click() + self.context + .driver + .click(classic::VIDEO_BUTTON) .await .context("Could not click on the toggle camera button")?; @@ -216,22 +228,33 @@ impl ParticipantInner { } async fn toggle_screen_share_inner(&self) -> Result<()> { - self.screen_share_button() - .await? - .click() + self.context + .driver + .click(classic::SCREEN_SHARE_BUTTON) .await .context("Could not click on the toggle screen share button") .map(|_| ()) } - async fn set_webcam_resolutions_inner(&self, value: WebcamResolution) -> Result<()> { - debug!(participant = %self.participant_name(), "Changing to {value} resolution"); + async fn set_video_constraint_publish_webcam_inner(&self, value: VideoConstraint) -> Result<()> { + info!(participant = %self.participant_name(), "Changing outgoing webcam video constraint to {value}"); + set_video_constraint_publish_webcam(self.context.driver.as_ref(), value) + .await + .context("Failed to set outgoing webcam video constraint") + } - set_outgoing_camera_resolution(&self.context.page, &value) + async fn set_video_constraint_subscribe_inner(&self, value: VideoConstraint) -> Result<()> { + info!(participant = %self.participant_name(), "Changing incoming video constraint to {value}"); + set_video_constraint_subscribe(self.context.driver.as_ref(), value) .await - .context("Failed to set outgoing camera resolution")?; + .context("Failed to set incoming video constraint") + } - Ok(()) + async fn set_video_max_concurrent_tracks_inner(&self, value: Option) -> Result<()> { + info!(participant = %self.participant_name(), ?value, "Changing max concurrent video tracks"); + set_video_max_concurrent_tracks(self.context.driver.as_ref(), value) + .await + .context("Failed to set max concurrent video tracks") } async fn set_noise_suppression_inner(&self, value: NoiseSuppression) -> Result<()> { @@ -242,7 +265,7 @@ impl ParticipantInner { self.context .send_log_message("info", format!("Changing noise suppression to {value}")); - set_noise_suppression(&self.context.page, value) + set_noise_suppression(self.context.driver.as_ref(), value) .await .context("Failed to set noise suppression level")?; @@ -250,39 +273,26 @@ impl ParticipantInner { } async fn toggle_auto_gain_control_inner(&self) -> Result<()> { - let auto_gain_control = get_auto_gain_control(&self.context.page).await?; - set_auto_gain_control(&self.context.page, !auto_gain_control) + let driver = self.context.driver.as_ref(); + let auto_gain_control = get_auto_gain_control(driver).await?; + set_auto_gain_control(driver, !auto_gain_control) .await .context("Failed to set auto gain control")?; Ok(()) } async fn toggle_background_blur_inner(&self) -> Result<()> { - let background_blur = get_background_blur(&self.context.page).await?; - set_background_blur(&self.context.page, !background_blur) + let driver = self.context.driver.as_ref(); + let background_blur = get_background_blur(driver).await?; + set_background_blur(driver, !background_blur) .await .context("Failed to set background blur")?; Ok(()) } - async fn leave_button(&self) -> Result { - self.context.find_element(classic::LEAVE_BUTTON).await - } - - async fn mute_button(&self) -> Result { - self.context.find_element(classic::MUTE_BUTTON).await - } - - async fn camera_button(&self) -> Result { - self.context.find_element(classic::VIDEO_BUTTON).await - } - - async fn screen_share_button(&self) -> Result { - self.context.find_element(classic::SCREEN_SHARE_BUTTON).await - } - async fn refresh_state_inner(&self) -> Result { - let joined = self.leave_button().await.is_ok(); + let driver = self.context.driver.as_ref(); + let joined = driver.exists(classic::LEAVE_BUTTON).await.unwrap_or(false); let mut state = ParticipantState { username: self.context.launch_spec.username.clone(), running: true, @@ -290,43 +300,51 @@ impl ParticipantInner { ..Default::default() }; - if let Ok(value) = get_noise_suppression(&self.context.page).await { + if let Ok(value) = get_noise_suppression(driver).await { state.noise_suppression = value; } - if let Ok(value) = get_auto_gain_control(&self.context.page).await { + if let Ok(value) = get_auto_gain_control(driver).await { state.auto_gain_control = value; } - if let Ok(mute_button) = self.mute_button().await { - if let Some(value) = element_state(&mute_button).await { - state.muted = !value; + if let Ok(value) = driver.attribute(classic::MUTE_BUTTON, "data-test-state").await { + if let Some(active) = decode_test_state(value) { + state.muted = !active; } } - if let Ok(camera_button) = self.camera_button().await { - if let Some(element_state) = element_state(&camera_button).await { - state.video_activated = element_state; + if let Ok(value) = driver.attribute(classic::VIDEO_BUTTON, "data-test-state").await { + if let Some(active) = decode_test_state(value) { + state.video_activated = active; } } - if let Ok(screen_share_button) = self.screen_share_button().await { - if let Some(element_state) = element_state(&screen_share_button).await { - state.screenshare_activated = element_state; + if let Ok(value) = driver.attribute(classic::SCREEN_SHARE_BUTTON, "data-test-state").await { + if let Some(active) = decode_test_state(value) { + state.screenshare_activated = active; } } - if let Ok(value) = get_force_webrtc(&self.context.page).await { + if let Ok(value) = get_force_webrtc(driver).await { if value { state.transport_mode = TransportMode::WebRTC; } } - if let Ok(value) = get_outgoing_camera_resolution(&self.context.page).await { - state.webcam_resolution = value; + if let Ok(value) = get_video_constraint_publish_webcam(driver).await { + state.video_constraint_publish_webcam = value; + } + + if let Ok(value) = get_video_constraint_subscribe(driver).await { + state.video_constraint_subscribe = value; + } + + if let Ok(value) = get_video_max_concurrent_tracks(driver).await { + state.video_max_concurrent_tracks = value; } - if let Ok(blur) = get_background_blur(&self.context.page).await { + if let Ok(blur) = get_background_blur(driver).await { state.background_blur = blur; } @@ -353,7 +371,15 @@ impl FrontendAutomation for ParticipantInner { ParticipantMessage::ToggleVideo => self.toggle_video_inner().await, ParticipantMessage::ToggleScreenshare => self.toggle_screen_share_inner().await, ParticipantMessage::ToggleAutoGainControl => self.toggle_auto_gain_control_inner().await, - ParticipantMessage::SetWebcamResolutions(value) => self.set_webcam_resolutions_inner(value).await, + ParticipantMessage::SetVideoConstraintPublishWebcam(value) => { + self.set_video_constraint_publish_webcam_inner(value).await + } + ParticipantMessage::SetVideoConstraintSubscribe(value) => { + self.set_video_constraint_subscribe_inner(value).await + } + ParticipantMessage::SetVideoMaxConcurrentTracks(value) => { + self.set_video_max_concurrent_tracks_inner(value).await + } ParticipantMessage::SetNoiseSuppression(value) => self.set_noise_suppression_inner(value).await, ParticipantMessage::ToggleBackgroundBlur => self.toggle_background_blur_inner().await, } diff --git a/browser/src/participant/frontend/driver.rs b/browser/src/participant/frontend/driver.rs new file mode 100644 index 0000000..836989a --- /dev/null +++ b/browser/src/participant/frontend/driver.rs @@ -0,0 +1,80 @@ +use super::super::shared::{ + messages::{ + ParticipantLogMessage, + ParticipantMessage, + }, + ParticipantLaunchSpec, + ParticipantState, +}; +use eyre::Result; +use futures::future::BoxFuture; +use std::time::Duration; +use tokio::sync::mpsc::UnboundedSender; + +/// Driver-agnostic browser operations used by the Hyper frontend automation. +/// +/// The `eval` contract is uniform across drivers: `js_body` is a JavaScript +/// statement list; the optional `arg` is available as `arguments[0]`; use +/// `return` to produce a value. Implementations adapt this to their engine +/// (chromiumoxide wraps it as `function() { }` invoked with `arg`; +/// WebDriver passes `js_body`/`arg` straight to `execute`). +pub(in crate::participant) trait BrowserDriver: Send + Sync { + fn goto(&self, url: &str) -> BoxFuture<'_, Result<()>>; + /// True if at least one element matches `selector` right now. + fn exists(&self, selector: &str) -> BoxFuture<'_, Result>; + /// Poll until `selector` exists or `timeout` elapses. + fn wait_for(&self, selector: &str, timeout: Duration) -> BoxFuture<'_, Result<()>>; + fn click(&self, selector: &str) -> BoxFuture<'_, Result<()>>; + /// Focus the element, clear its value, then type `text`. + fn fill(&self, selector: &str, text: &str) -> BoxFuture<'_, Result<()>>; + /// Read an attribute of the first element matching `selector`. + /// `Ok(None)` if the element exists but the attribute is absent. + fn attribute(&self, selector: &str, name: &str) -> BoxFuture<'_, Result>>; + fn eval(&self, js_body: &str, arg: Option) -> BoxFuture<'_, Result>; + /// Set a cookie for `domain`. Drivers that require being on-origin first + /// (WebDriver) must navigate to the origin before setting it. + fn set_cookie(&self, domain: &str, name: &str, value: &str) -> BoxFuture<'_, Result<()>>; +} + +/// Context shared by every frontend automation, parameterised over the driver. +pub(in crate::participant) struct FrontendContext { + pub(in crate::participant) launch_spec: ParticipantLaunchSpec, + pub(in crate::participant) driver: Box, + pub(in crate::participant) sender: UnboundedSender, +} + +impl std::fmt::Debug for FrontendContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FrontendContext") + .field("launch_spec", &self.launch_spec) + .finish_non_exhaustive() + } +} + +impl FrontendContext { + pub(in crate::participant) fn participant_name(&self) -> &str { + &self.launch_spec.username + } + + pub(in crate::participant) fn send_log_message(&self, level: &str, message: impl ToString) { + if let Err(err) = self + .sender + .send(ParticipantLogMessage::new(level, self.participant_name(), message)) + { + trace!(participant = %self.participant_name(), "Failed to send log message: {err}"); + } + } +} + +/// The DOM automation contract for a concrete Hyper frontend (HyperCore/HyperLite). +pub(in crate::participant) trait FrontendAutomation: Send { + fn join(&mut self) -> BoxFuture<'_, Result<()>>; + fn leave(&mut self) -> BoxFuture<'_, Result<()>>; + fn handle_command(&mut self, message: ParticipantMessage) -> BoxFuture<'_, Result<()>>; + fn refresh_state(&mut self) -> BoxFuture<'_, Result>; +} + +/// Decode the legacy `data-test-state="true"|"false"` attribute. +pub(in crate::participant) fn decode_test_state(value: Option) -> Option { + value.map(|value| value == "true") +} diff --git a/browser/src/participant/local/lite.rs b/browser/src/participant/frontend/lite.rs similarity index 79% rename from browser/src/participant/local/lite.rs rename to browser/src/participant/frontend/lite.rs index e08ec41..b623575 100644 --- a/browser/src/participant/local/lite.rs +++ b/browser/src/participant/frontend/lite.rs @@ -5,21 +5,18 @@ use super::{ messages::ParticipantMessage, ParticipantState, }, - frontend::{ - element_state, + driver::{ + decode_test_state, + BrowserDriver, FrontendAutomation, FrontendContext, }, + selectors::lite, }; -use crate::{ - participant::local::selectors::lite, - util::wait_for_element, -}; -use chromiumoxide::Element; use client_simulator_config::{ NoiseSuppression, TransportMode, - WebcamResolution, + VideoConstraint, }; use eyre::{ Context as _, @@ -57,8 +54,8 @@ impl ParticipantInnerLite { async fn join_session(&mut self) -> Result<()> { self.context - .page - .goto(self.context.launch_spec.session_url.to_string()) + .driver + .goto(self.context.launch_spec.session_url.as_str()) .await .context("failed to wait for navigation response")?; @@ -75,9 +72,8 @@ impl ParticipantInnerLite { self.prepare_lobby().await?; self.context - .find_element(lite::JOIN_BUTTON) - .await? - .click() + .driver + .click(lite::JOIN_BUTTON) .await .context("failed to click join button")?; @@ -86,7 +82,9 @@ impl ParticipantInnerLite { } } - wait_for_element(&self.context.page, lite::LEAVE_BUTTON, Duration::from_secs(30)) + self.context + .driver + .wait_for(lite::LEAVE_BUTTON, Duration::from_secs(30)) .await .context("We haven't joined the space, cannot find the leave button")?; @@ -109,11 +107,11 @@ impl ParticipantInnerLite { let start = Instant::now(); loop { - if self.context.page.find_element(lite::LEAVE_BUTTON).await.is_ok() { + if self.context.driver.exists(lite::LEAVE_BUTTON).await.unwrap_or(false) { return Ok(LiteEntryPoint::InCall); } - if self.context.page.find_element(lite::JOIN_BUTTON).await.is_ok() { + if self.context.driver.exists(lite::JOIN_BUTTON).await.unwrap_or(false) { return Ok(LiteEntryPoint::Lobby); } @@ -130,16 +128,10 @@ impl ParticipantInnerLite { } async fn prepare_lobby(&self) -> Result<()> { - if let Ok(input) = self.context.find_element(lite::NAME_INPUT).await { - input - .focus() - .await - .context("failed to focus on the Lite display name input")? - .call_js_fn("function() { this.value = ''; }", true) - .await - .context("failed to empty current Lite display name")?; - input - .type_str(&self.context.launch_spec.username) + if self.context.driver.exists(lite::NAME_INPUT).await.unwrap_or(false) { + self.context + .driver + .fill(lite::NAME_INPUT, &self.context.launch_spec.username) .await .context("failed to insert Lite display name")?; @@ -182,16 +174,22 @@ impl ParticipantInnerLite { } async fn leave_session(&mut self) -> Result<()> { - self.leave_button() - .await? - .click() + self.context + .driver + .click(lite::LEAVE_BUTTON) .await .context("Could not click on the leave space button")?; - match wait_for_element(&self.context.page, lite::LEAVE_CONFIRM_BUTTON, Duration::from_secs(5)).await { - Ok(button) => { - button - .click() + match self + .context + .driver + .wait_for(lite::LEAVE_CONFIRM_BUTTON, Duration::from_secs(5)) + .await + { + Ok(()) => { + self.context + .driver + .click(lite::LEAVE_CONFIRM_BUTTON) .await .context("Could not confirm leaving the Lite meeting")?; debug!(participant = %self.participant_name(), "Confirmed the Lite leave dialog"); @@ -212,9 +210,9 @@ impl ParticipantInnerLite { } async fn toggle_audio_inner(&self) -> Result<()> { - self.mute_button() - .await? - .click() + self.context + .driver + .click(lite::MUTE_BUTTON) .await .context("Could not click on the toggle audio button")?; info!(participant = %self.participant_name(), "Toggled audio"); @@ -223,9 +221,9 @@ impl ParticipantInnerLite { } async fn toggle_video_inner(&self) -> Result<()> { - self.camera_button() - .await? - .click() + self.context + .driver + .click(lite::VIDEO_BUTTON) .await .context("Could not click on the toggle camera button")?; info!(participant = %self.participant_name(), "Toggled camera"); @@ -234,9 +232,9 @@ impl ParticipantInnerLite { } async fn toggle_screen_share_inner(&self) -> Result<()> { - self.screen_share_button() - .await? - .click() + self.context + .driver + .click(lite::SCREEN_SHARE_BUTTON) .await .context("Could not click on the toggle screen share button")?; info!(participant = %self.participant_name(), "Toggled screen share"); @@ -274,11 +272,18 @@ impl ParticipantInnerLite { Ok(()) } - async fn set_webcam_resolutions_inner(&self, _value: WebcamResolution) -> Result<()> { - debug!( - participant = %self.participant_name(), - "Webcam resolution changes not supported in lite frontend" - ); + async fn set_video_constraint_publish_webcam_inner(&self, _value: VideoConstraint) -> Result<()> { + self.log_unsupported("Video constraint"); + Ok(()) + } + + async fn set_video_constraint_subscribe_inner(&self, _value: VideoConstraint) -> Result<()> { + self.log_unsupported("Video constraint"); + Ok(()) + } + + async fn set_video_max_concurrent_tracks_inner(&self, _value: Option) -> Result<()> { + self.log_unsupported("Video constraint"); Ok(()) } @@ -293,16 +298,17 @@ impl ParticipantInnerLite { } async fn click_if_present(&self, selector: &str, action: &str) -> Result { - let Ok(element) = self.context.page.find_element(selector).await else { + if !self.context.driver.exists(selector).await.unwrap_or(false) { debug!( participant = %self.participant_name(), "Could not find Lite control for {action}: {selector}" ); return Ok(false); - }; + } - element - .click() + self.context + .driver + .click(selector) .await .with_context(|| format!("Could not click Lite control for {action}: {selector}"))?; debug!(participant = %self.participant_name(), "Clicked Lite control for {action}"); @@ -318,66 +324,44 @@ impl ParticipantInnerLite { .send_log_message("debug", format!("{feature} changes not supported in lite frontend")); } - async fn leave_button(&self) -> Result { - self.context.find_element(lite::LEAVE_BUTTON).await - } - - async fn mute_button(&self) -> Result { - self.context.find_element(lite::MUTE_BUTTON).await - } - - async fn camera_button(&self) -> Result { - self.context.find_element(lite::VIDEO_BUTTON).await - } - - async fn screen_share_button(&self) -> Result { - self.context.find_element(lite::SCREEN_SHARE_BUTTON).await - } - async fn audio_enabled(&self) -> Result> { - let button = match self.mute_button().await { - Ok(button) => button, - Err(_) => return Ok(None), - }; + let driver = self.context.driver.as_ref(); Ok(audio_enabled_from_button_state( - element_state(&button).await, - aria_pressed(&button).await, - aria_label(&button).await.as_deref(), + decode_test_state(driver.attribute(lite::MUTE_BUTTON, "data-test-state").await?), + aria_pressed(driver, lite::MUTE_BUTTON).await, + aria_label(driver, lite::MUTE_BUTTON).await.as_deref(), )) } async fn video_enabled(&self) -> Result> { - let button = match self.camera_button().await { - Ok(button) => button, - Err(_) => return Ok(None), - }; + let driver = self.context.driver.as_ref(); Ok(video_enabled_from_button_state( - element_state(&button).await, - aria_pressed(&button).await, - aria_label(&button).await.as_deref(), + decode_test_state(driver.attribute(lite::VIDEO_BUTTON, "data-test-state").await?), + aria_pressed(driver, lite::VIDEO_BUTTON).await, + aria_label(driver, lite::VIDEO_BUTTON).await.as_deref(), )) } async fn screen_share_enabled(&self) -> Result> { - let button = match self.screen_share_button().await { - Ok(button) => button, - Err(_) => return Ok(None), - }; + let driver = self.context.driver.as_ref(); + let data_test_state = driver.attribute(lite::SCREEN_SHARE_BUTTON, "data-test-state").await?; - Ok(element_state(&button).await.or(aria_pressed(&button).await)) + Ok(decode_test_state(data_test_state).or(aria_pressed(driver, lite::SCREEN_SHARE_BUTTON).await)) } async fn refresh_state_inner(&self) -> Result { - let joined = self.leave_button().await.is_ok(); + let joined = self.context.driver.exists(lite::LEAVE_BUTTON).await.unwrap_or(false); let mut state = ParticipantState { username: self.context.launch_spec.username.clone(), running: true, joined, auto_gain_control: self.context.launch_spec.settings.auto_gain_control, transport_mode: TransportMode::default(), - webcam_resolution: WebcamResolution::default(), + video_constraint_publish_webcam: VideoConstraint::default(), + video_constraint_subscribe: VideoConstraint::default(), + video_max_concurrent_tracks: None, noise_suppression: NoiseSuppression::default(), muted: !self.context.launch_spec.settings.audio_enabled, video_activated: self.context.launch_spec.settings.video_enabled, @@ -400,17 +384,17 @@ impl ParticipantInnerLite { } } -async fn aria_pressed(element: &Element) -> Option { - element - .attribute("aria-pressed") +async fn aria_pressed(driver: &dyn BrowserDriver, selector: &str) -> Option { + driver + .attribute(selector, "aria-pressed") .await .ok() .flatten() .and_then(|value| value.parse().ok()) } -async fn aria_label(element: &Element) -> Option { - element.attribute("aria-label").await.ok().flatten() +async fn aria_label(driver: &dyn BrowserDriver, selector: &str) -> Option { + driver.attribute(selector, "aria-label").await.ok().flatten() } fn audio_enabled_from_button_state( @@ -472,7 +456,15 @@ impl FrontendAutomation for ParticipantInnerLite { ParticipantMessage::ToggleVideo => self.toggle_video_inner().await, ParticipantMessage::ToggleScreenshare => self.toggle_screen_share_inner().await, ParticipantMessage::ToggleAutoGainControl => self.toggle_auto_gain_control_inner().await, - ParticipantMessage::SetWebcamResolutions(value) => self.set_webcam_resolutions_inner(value).await, + ParticipantMessage::SetVideoConstraintPublishWebcam(value) => { + self.set_video_constraint_publish_webcam_inner(value).await + } + ParticipantMessage::SetVideoConstraintSubscribe(value) => { + self.set_video_constraint_subscribe_inner(value).await + } + ParticipantMessage::SetVideoMaxConcurrentTracks(value) => { + self.set_video_max_concurrent_tracks_inner(value).await + } ParticipantMessage::SetNoiseSuppression(value) => self.set_noise_suppression_inner(value).await, ParticipantMessage::ToggleBackgroundBlur => self.toggle_background_blur_inner().await, } diff --git a/browser/src/participant/frontend/mod.rs b/browser/src/participant/frontend/mod.rs new file mode 100644 index 0000000..daca152 --- /dev/null +++ b/browser/src/participant/frontend/mod.rs @@ -0,0 +1,19 @@ +//! Driver-agnostic Hyper frontend automation, shared by the Local (chromiumoxide) +//! and Device Farm (WebDriver) backends. + +mod builder; +mod commands; +mod core; +mod driver; +mod lite; +mod selectors; + +pub(in crate::participant) use builder::{ + FrontendAuth, + FrontendKindBuilder, +}; +pub(in crate::participant) use driver::{ + BrowserDriver, + FrontendAutomation, + FrontendContext, +}; diff --git a/browser/src/participant/local/selectors.rs b/browser/src/participant/frontend/selectors.rs similarity index 100% rename from browser/src/participant/local/selectors.rs rename to browser/src/participant/frontend/selectors.rs diff --git a/browser/src/participant/local/chromium_driver.rs b/browser/src/participant/local/chromium_driver.rs new file mode 100644 index 0000000..02f9f2d --- /dev/null +++ b/browser/src/participant/local/chromium_driver.rs @@ -0,0 +1,158 @@ +use crate::participant::frontend::BrowserDriver; +use chromiumoxide::{ + cdp::js_protocol::runtime::{ + CallArgument, + CallFunctionOnParams, + }, + js::Evaluation, + Page, +}; +use eyre::{ + Context as _, + Result, +}; +use futures::{ + future::BoxFuture, + FutureExt as _, +}; +use std::time::{ + Duration, + Instant, +}; + +/// `BrowserDriver` implementation backed by a local chromiumoxide `Page` (CDP). +pub(crate) struct ChromiumDriver { + page: Page, +} + +impl ChromiumDriver { + pub(crate) fn new(page: Page) -> Self { + Self { page } + } +} + +impl BrowserDriver for ChromiumDriver { + fn goto(&self, url: &str) -> BoxFuture<'_, Result<()>> { + let url = url.to_owned(); + async move { + self.page.goto(url).await.context("failed to navigate")?; + Ok(()) + } + .boxed() + } + + fn exists(&self, selector: &str) -> BoxFuture<'_, Result> { + let selector = selector.to_owned(); + async move { Ok(self.page.find_element(selector).await.is_ok()) }.boxed() + } + + fn wait_for(&self, selector: &str, timeout: Duration) -> BoxFuture<'_, Result<()>> { + let selector = selector.to_owned(); + async move { + let start = Instant::now(); + loop { + if self.page.find_element(&selector).await.is_ok() { + return Ok(()); + } + if start.elapsed() > timeout { + eyre::bail!("timeout waiting for selector {selector}"); + } + tokio::time::sleep(Duration::from_millis(100)).await; + } + } + .boxed() + } + + fn click(&self, selector: &str) -> BoxFuture<'_, Result<()>> { + let selector = selector.to_owned(); + async move { + self.page + .find_element(&selector) + .await + .with_context(|| format!("Could not find {selector}"))? + .click() + .await + .with_context(|| format!("Could not click {selector}"))?; + Ok(()) + } + .boxed() + } + + fn fill(&self, selector: &str, text: &str) -> BoxFuture<'_, Result<()>> { + let selector = selector.to_owned(); + let text = text.to_owned(); + async move { + let element = self + .page + .find_element(&selector) + .await + .with_context(|| format!("Could not find {selector}"))?; + element + .focus() + .await + .context("failed to focus element")? + .call_js_fn("function() { this.value = ''; }", true) + .await + .context("failed to clear element")?; + element.type_str(&text).await.context("failed to type into element")?; + Ok(()) + } + .boxed() + } + + fn attribute(&self, selector: &str, name: &str) -> BoxFuture<'_, Result>> { + let selector = selector.to_owned(); + let name = name.to_owned(); + async move { + match self.page.find_element(&selector).await { + Ok(element) => Ok(element.attribute(&name).await.ok().flatten()), + Err(_) => Ok(None), + } + } + .boxed() + } + + fn eval(&self, js_body: &str, arg: Option) -> BoxFuture<'_, Result> { + let declaration = format!("function() {{ {js_body} }}"); + async move { + let mut builder = CallFunctionOnParams::builder().function_declaration(declaration); + if let Some(arg) = arg { + let argument = CallArgument::builder().value(arg).build(); + builder = builder.arguments(vec![argument]); + } + let function = builder + .build() + .map_err(|e| eyre::eyre!("failed to build eval command: {e}"))?; + let result = self + .page + .evaluate(Evaluation::Function(function)) + .await + .context("failed to evaluate script")?; + Ok(result.into_value().unwrap_or(serde_json::Value::Null)) + } + .boxed() + } + + fn set_cookie(&self, domain: &str, name: &str, value: &str) -> BoxFuture<'_, Result<()>> { + use chromiumoxide::cdp::browser_protocol::network::CookieParam; + + let domain = domain.to_owned(); + let name = name.to_owned(); + let value = value.to_owned(); + async move { + let cookie = CookieParam::builder() + .name(name) + .value(value) + .domain(domain) + .path("/") + .build() + .map_err(|e| eyre::eyre!("failed to build cookie: {e}"))?; + self.page + .set_cookies(vec![cookie]) + .await + .context("failed to set cookie")?; + Ok(()) + } + .boxed() + } +} diff --git a/browser/src/participant/local/commands.rs b/browser/src/participant/local/commands.rs deleted file mode 100644 index 2fb9bfa..0000000 --- a/browser/src/participant/local/commands.rs +++ /dev/null @@ -1,135 +0,0 @@ -use chromiumoxide::{ - cdp::js_protocol::runtime::{ - CallArgument, - CallFunctionOnParams, - }, - js::Evaluation, - Page, -}; -use client_simulator_config::{ - NoiseSuppression, - WebcamResolution, -}; -use eyre::{ - Context as _, - Result, -}; - -macro_rules! create_eval_getter { - ($fn_name:ident, $js_function:expr, $result_type:ty, $convert_into:ty) => { - pub async fn $fn_name(page: &Page) -> Result<$convert_into> { - create_eval_getter!($fn_name, $js_function, $result_type); - let value = $fn_name(page).await?; - value.parse::<$convert_into>().context(format!( - "failed to parse {} from string", - stringify!($result_type) - )) - } - }; - - ($fn_name:ident, $js_function:expr, $result_type:ty) => { - pub async fn $fn_name(page: &Page) -> Result<$result_type> { - let function = CallFunctionOnParams::builder() - .function_declaration($js_function) - .build() - .map_err(|e| eyre::eyre!("failed to build {} command: {e}", stringify!($fn_name)))?; - - let evaluation = Evaluation::Function(function); - - page.evaluate(evaluation) - .await - .context(format!("failed to evaluate {}", stringify!($fn_name))) - .and_then(|e| { - e.into_value::<$result_type>().context(format!( - "failed to convert evaluation result to {}", - stringify!($result_type) - )) - }) - } - }; -} - -macro_rules! create_eval_setter { - ($fn_name:ident, $js_function:expr) => { - pub async fn $fn_name(page: &Page, value: S) -> Result<()> - where - S: serde::Serialize, - { - let value = serde_json::to_value(value) - .context(format!("failed to serialize value for {}", stringify!($fn_name)))?; - let argument = CallArgument::builder().value(value).build(); - let function = CallFunctionOnParams::builder() - .function_declaration($js_function) - .arguments(vec![argument]) - .build() - .map_err(|e| eyre::eyre!("failed to build {} command: {e}", stringify!($fn_name)))?; - - debug!("evaluating {function:?}"); - - let evaluation = Evaluation::Function(function); - - page.evaluate(evaluation) - .await - .context(format!("failed to evaluate {}", stringify!($fn_name)))?; - - Ok(()) - } - }; -} - -create_eval_getter!( - get_noise_suppression, - "function f() { return hyper.settings.media.noiseSuppression; }", - String, - NoiseSuppression -); - -create_eval_setter!( - set_noise_suppression, - "function f(noiseSuppression) { hyper.settings.media.actions.setNoiseSuppression(noiseSuppression); }" -); - -create_eval_getter!( - get_auto_gain_control, - "function f() { return hyper.settings.media.autoGainControl; }", - bool -); - -create_eval_setter!( - set_auto_gain_control, - "function f(value) { hyper.settings.media.actions.setAutoGainControl(value); }" -); - -create_eval_getter!( - get_background_blur, - "function f() { return hyper.settings.media.backgroundBlur; }", - bool -); - -create_eval_setter!( - set_background_blur, - "function f(value) { return hyper.settings.media.actions.setBackgroundBlur(value); }" -); - -create_eval_getter!( - get_outgoing_camera_resolution, - "function f() { return hyper.settings.videoCodec.videoResolutionForWebcamEncoder.name; }", - String, - WebcamResolution -); - -create_eval_setter!( - set_outgoing_camera_resolution, - "function f(value) { return hyper.settings.videoCodec.actions.setVideoResolutionForWebcamEncoder(value); }" -); - -create_eval_getter!( - get_force_webrtc, - "function f() { return hyper.settings.sessionDebug.forceWebrtc; }", - bool -); - -create_eval_setter!( - set_force_webrtc, - "function f(value) { return hyper.settings.sessionDebug.actions.setForceWebrtc(value); }" -); diff --git a/browser/src/participant/local/frontend.rs b/browser/src/participant/local/frontend.rs deleted file mode 100644 index ba5becd..0000000 --- a/browser/src/participant/local/frontend.rs +++ /dev/null @@ -1,64 +0,0 @@ -use super::super::shared::{ - messages::{ - ParticipantLogMessage, - ParticipantMessage, - }, - ParticipantLaunchSpec, - ParticipantState, -}; -use chromiumoxide::{ - Element, - Page, -}; -use eyre::{ - Context as _, - Result, -}; -use futures::future::BoxFuture; -use tokio::sync::mpsc::UnboundedSender; - -/// Local-only context shared by the Chromium session and frontend automation. -#[derive(Debug)] -pub(super) struct FrontendContext { - pub(super) launch_spec: ParticipantLaunchSpec, - pub(super) page: Page, - pub(super) sender: UnboundedSender, -} - -impl FrontendContext { - pub(super) fn participant_name(&self) -> &str { - &self.launch_spec.username - } - - pub(super) fn send_log_message(&self, level: &str, message: impl ToString) { - if let Err(err) = self - .sender - .send(ParticipantLogMessage::new(level, self.participant_name(), message)) - { - trace!(participant = %self.participant_name(), "Failed to send log message: {err}"); - } - } - - pub(super) async fn find_element(&self, selector: &str) -> Result { - self.page - .find_element(selector) - .await - .context(format!("Could not find the {selector} element")) - } -} - -/// Local DOM automation contract for a concrete Hyper frontend. -pub(super) trait FrontendAutomation: Send { - fn join(&mut self) -> BoxFuture<'_, Result<()>>; - fn leave(&mut self) -> BoxFuture<'_, Result<()>>; - fn handle_command(&mut self, message: ParticipantMessage) -> BoxFuture<'_, Result<()>>; - fn refresh_state(&mut self) -> BoxFuture<'_, Result>; -} - -pub(super) async fn element_state(el: &Element) -> Option { - el.attribute("data-test-state") - .await - .ok() - .unwrap_or(None) - .map(|value| value == "true") -} diff --git a/browser/src/participant/local/mod.rs b/browser/src/participant/local/mod.rs index f27431b..0bb6ba8 100644 --- a/browser/src/participant/local/mod.rs +++ b/browser/src/participant/local/mod.rs @@ -1,6 +1,2 @@ -pub(super) mod commands; -pub(super) mod core; -pub(super) mod frontend; -pub(super) mod lite; -pub(super) mod selectors; +mod chromium_driver; pub(super) mod session; diff --git a/browser/src/participant/local/session.rs b/browser/src/participant/local/session.rs index 6f57143..aacaeff 100644 --- a/browser/src/participant/local/session.rs +++ b/browser/src/participant/local/session.rs @@ -1,16 +1,15 @@ +use super::chromium_driver::ChromiumDriver; use crate::{ auth::{ BorrowedCookie, HyperSessionCookieManger, }, participant::{ - local::{ - core::ParticipantInner, - frontend::{ - FrontendAutomation, - FrontendContext, - }, - lite::ParticipantInnerLite, + frontend::{ + FrontendAuth, + FrontendAutomation, + FrontendContext, + FrontendKindBuilder, }, shared::{ messages::{ @@ -20,7 +19,6 @@ use crate::{ DriverTermination, ParticipantDriverSession, ParticipantLaunchSpec, - ResolvedFrontendKind, }, }, }; @@ -57,7 +55,13 @@ use std::{ Path, PathBuf, }, - sync::Arc, + sync::{ + atomic::{ + AtomicBool, + Ordering, + }, + Arc, + }, time::Duration, }; use tokio::{ @@ -66,13 +70,14 @@ use tokio::{ watch, }, task::JoinHandle, + time::timeout, }; pub(crate) struct LocalChromiumSession { launch_spec: ParticipantLaunchSpec, browser_config: BrowserConfig, sender: UnboundedSender, - frontend_builder: Option, + frontend_builder: Option, automation: Option>, browser: Option, page: Option, @@ -80,14 +85,7 @@ pub(crate) struct LocalChromiumSession { detached_target_task: Option>, termination_tx: watch::Sender>, termination_rx: watch::Receiver>, -} - -enum LocalFrontendBuilder { - HyperCore { - auth: Option, - cookie_manager: HyperSessionCookieManger, - }, - HyperLite, + closing: Arc, } impl LocalChromiumSession { @@ -98,11 +96,9 @@ impl LocalChromiumSession { auth: Option, cookie_manager: HyperSessionCookieManger, ) -> Self { - let frontend_builder = match launch_spec.frontend_kind { - ResolvedFrontendKind::HyperCore => LocalFrontendBuilder::HyperCore { auth, cookie_manager }, - ResolvedFrontendKind::HyperLite => LocalFrontendBuilder::HyperLite, - }; + let frontend_builder = FrontendAuth::for_kind(launch_spec.frontend_kind, auth, cookie_manager); let (termination_tx, termination_rx) = watch::channel(None); + let closing = Arc::new(AtomicBool::new(false)); Self { launch_spec, @@ -116,38 +112,45 @@ impl LocalChromiumSession { detached_target_task: None, termination_tx, termination_rx, + closing, } } fn log_message(&self, level: &str, message: impl ToString) { - let log_message = ParticipantLogMessage::new(level, &self.launch_spec.username, message); - log_message.write(); - if let Err(err) = self.sender.send(log_message) { - trace!( - participant = %self.launch_spec.username, - "Failed to send local driver log message: {err}" - ); - } + log_local_message(&self.sender, &self.launch_spec.username, level, message); } async fn start_inner(&mut self) -> Result<()> { + self.closing.store(false, Ordering::SeqCst); let (mut browser, handler) = create_browser(&self.browser_config).await?; - let browser_event_task = drive_browser_events(&self.launch_spec.username, handler, self.termination_tx.clone()); + let browser_event_task = drive_browser_events( + &self.launch_spec.username, + handler, + self.termination_tx.clone(), + Arc::clone(&self.closing), + ); let page = create_page_retry(&self.launch_spec, &mut browser).await?; - let detached_target_task = - drive_detached_target_events(&self.launch_spec.username, &mut browser, self.termination_tx.clone()).await?; - - let frontend_builder = self + let detached_target_task = drive_detached_target_events( + &self.launch_spec.username, + &mut browser, + self.termination_tx.clone(), + Arc::clone(&self.closing), + ) + .await?; + + let auth = self .frontend_builder .take() - .context("local frontend builder already consumed")?; - let automation = frontend_builder - .build(FrontendContext { + .context("local frontend auth already consumed")?; + let automation = FrontendKindBuilder::build( + FrontendContext { launch_spec: self.launch_spec.clone(), - page: page.clone(), + driver: Box::new(ChromiumDriver::new(page.clone())), sender: self.sender.clone(), - }) - .await?; + }, + auth, + ) + .await?; self.browser = Some(browser); self.page = Some(page); @@ -170,6 +173,8 @@ impl LocalChromiumSession { } async fn close_inner(&mut self) -> Result<()> { + self.closing.store(true, Ordering::SeqCst); + if let Some(handle) = self.detached_target_task.take() { handle.abort(); } @@ -199,15 +204,53 @@ impl LocalChromiumSession { } } - if let Some(page) = self.page.take() { - if let Err(err) = page.close().await { - self.log_message("error", format!("Error closing page: {err}")); - } - } + self.page = None; if let Some(browser) = self.browser.as_mut() { - browser.close().await?; - browser.wait().await?; + let sender = self.sender.clone(); + let participant_name = self.launch_spec.username.clone(); + + match timeout(BROWSER_CLOSE_COMMAND_TIMEOUT, browser.close()).await { + Ok(Ok(_)) => {} + Ok(Err(err)) => { + log_local_message( + &sender, + &participant_name, + "debug", + format!("Browser close command failed during shutdown: {err}"), + ); + } + Err(_) => { + log_local_message( + &sender, + &participant_name, + "debug", + "Timed out waiting for browser close command response, waiting for browser process exit", + ); + } + } + + match timeout(BROWSER_EXIT_TIMEOUT, browser.wait()).await { + Ok(Ok(_)) => {} + Ok(Err(err)) => { + log_local_message( + &sender, + &participant_name, + "error", + format!("Failed waiting for browser process to exit: {err}"), + ); + self.kill_browser().await; + } + Err(_) => { + log_local_message( + &sender, + &participant_name, + "warn", + "Timed out waiting for browser process to exit, killing browser", + ); + self.kill_browser().await; + } + } } if let Some(handle) = self.browser_event_task.take() { @@ -223,6 +266,8 @@ impl LocalChromiumSession { } async fn kill_browser(&mut self) { + self.closing.store(true, Ordering::SeqCst); + if let Some(handle) = self.detached_target_task.take() { handle.abort(); } @@ -283,25 +328,9 @@ impl ParticipantDriverSession for LocalChromiumSession { } } -impl LocalFrontendBuilder { - async fn build(self, context: FrontendContext) -> Result> { - match self { - Self::HyperCore { auth, cookie_manager } => { - let auth = if let Some(cookie) = auth { - cookie - } else { - cookie_manager - .fetch_new_cookie(context.launch_spec.base_url(), context.participant_name()) - .await? - }; - Ok(Box::new(ParticipantInner::new(context, auth))) - } - Self::HyperLite => Ok(Box::new(ParticipantInnerLite::new(context))), - } - } -} - const CHROME_BINARY_NAMES: &[&str] = &["chromium", "google-chrome", "google-chrome-stable", "chrome"]; +const BROWSER_CLOSE_COMMAND_TIMEOUT: Duration = Duration::from_secs(1); +const BROWSER_EXIT_TIMEOUT: Duration = Duration::from_secs(2); #[cfg(any(test, target_os = "macos"))] const MACOS_GOOGLE_CHROME_APP_BINARY: &str = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; #[cfg(any(test, target_os = "macos"))] @@ -395,6 +424,22 @@ fn is_executable_file(path: &Path) -> bool { } } +fn log_local_message( + sender: &UnboundedSender, + participant_name: &str, + level: &str, + message: impl ToString, +) { + let log_message = ParticipantLogMessage::new(level, participant_name, message); + log_message.write(); + if let Err(err) = sender.send(log_message) { + trace!( + participant = %participant_name, + "Failed to send local driver log message: {err}" + ); + } +} + async fn create_browser(browser_config: &BrowserConfig) -> Result<(Browser, Handler)> { let binary = get_binary()?; @@ -545,19 +590,70 @@ mod tests { assert!(!audio_arg.starts_with("--")); assert!(!video_arg.starts_with("--")); } + + #[test] + fn chromiumoxide_parses_chrome_146_request_extra_info_events() { + use chromiumoxide::{ + cdp::events::CdpEvent, + types::Message, + }; + + let raw_event = serde_json::json!({ + "method": "Network.requestWillBeSentExtraInfo", + "params": { + "requestId": "67875.221", + "associatedCookies": [], + "headers": {}, + "connectTiming": { + "requestTime": 1590260.969965 + }, + "clientSecurityState": { + "initiatorIsSecureContext": true, + "initiatorIPAddressSpace": "Public", + "localNetworkAccessRequestPolicy": "PermissionBlock" + }, + "siteHasCookieInOtherPartition": false + }, + "sessionId": "CA7D85B4BA74D473D259E672BB4C4FB2" + }); + + let message: Message = + serde_json::from_value(raw_event).expect("Chrome 146 request extra info event should parse"); + + let Message::Event(event) = message else { + panic!("expected CDP event"); + }; + + match event.params { + CdpEvent::NetworkRequestWillBeSentExtraInfo(event) => { + assert_eq!(event.request_id.as_ref(), "67875.221"); + assert!(event.client_security_state.is_some()); + } + other => panic!("expected Network.requestWillBeSentExtraInfo, got {other:?}"), + } + } } fn drive_browser_events( name: &str, mut handler: Handler, termination_tx: watch::Sender>, + closing: Arc, ) -> JoinHandle<()> { let participant_name = name.to_string(); tokio::spawn(async move { while let Some(event) = handler.next().await { if let Err(err) = event { + if closing.load(Ordering::SeqCst) { + debug!( + participant = %participant_name, + "Browser event handler stopped during shutdown: {err:?}" + ); + break; + } + if err.to_string().contains("ResetWithoutClosingHandshake") { - error!(participant = %participant_name, "Browser unexpectedly closed"); + warn!(participant = %participant_name, "Browser unexpectedly closed"); signal_termination( &termination_tx, DriverTermination::new("warn", "Browser unexpectedly closed"), @@ -566,6 +662,11 @@ fn drive_browser_events( } error!(participant = %participant_name, "error in browser handler: {err:?}"); + signal_termination( + &termination_tx, + DriverTermination::new("error", format!("error in browser handler: {err:?}")), + ); + break; } } @@ -577,6 +678,7 @@ async fn drive_detached_target_events( name: &str, browser: &mut Browser, termination_tx: watch::Sender>, + closing: Arc, ) -> Result> { let participant_name = Arc::new(name.to_string()); let mut detached_event = browser @@ -586,6 +688,11 @@ async fn drive_detached_target_events( Ok(tokio::spawn(async move { if detached_event.next().await.is_some() { + if closing.load(Ordering::SeqCst) { + debug!(participant = %participant_name, "Browser target detached during shutdown"); + return; + } + warn!(participant = %participant_name, "Browser unexpectedly closed"); signal_termination( &termination_tx, diff --git a/browser/src/participant/mod.rs b/browser/src/participant/mod.rs index 7776c31..06a57be 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -26,7 +26,17 @@ use eyre::{ OptionExt as _, Result, }; -use std::sync::Arc; +use futures::{ + future::{ + BoxFuture, + Shared, + }, + FutureExt as _, +}; +use std::{ + fmt, + sync::Arc, +}; use tokio::{ sync::{ mpsc::{ @@ -36,6 +46,7 @@ use tokio::{ }, watch, }, + task::JoinHandle, time::{ timeout, Duration, @@ -47,6 +58,8 @@ use tokio_util::sync::{ }; mod cloudflare; +pub mod device_farm; +mod frontend; mod local; mod remote_stub; pub mod shared; @@ -56,14 +69,20 @@ pub use shared::{ ParticipantStore, }; +#[cfg(not(test))] +const PARTICIPANT_CLOSE_PROGRESS_INTERVAL: Duration = Duration::from_secs(10); +#[cfg(test)] +const PARTICIPANT_CLOSE_PROGRESS_INTERVAL: Duration = Duration::from_millis(10); + /// Handle to a participant session managed by the participant runtime. #[derive(Debug, Clone)] pub struct Participant { pub name: String, pub created: chrono::DateTime, pub state: watch::Receiver, - _participant_task_guard: Arc, + participant_task: ParticipantTaskControl, sender: UnboundedSender, + close_strategy: CloseStrategy, } impl PartialEq for Participant { @@ -72,6 +91,49 @@ impl PartialEq for Participant { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum CloseStrategy { + DriverCloseOnly, + LeaveBeforeClose, +} + +#[derive(Clone)] +struct ParticipantTaskControl { + cancellation_token: CancellationToken, + _drop_guard: Arc, + completion: Shared>>, +} + +impl fmt::Debug for ParticipantTaskControl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ParticipantTaskControl").finish_non_exhaustive() + } +} + +impl ParticipantTaskControl { + fn new(cancellation_token: CancellationToken, drop_guard: DropGuard, handle: JoinHandle<()>) -> Self { + let completion = async move { handle.await.err().map(|err| err.to_string()) } + .boxed() + .shared(); + + Self { + cancellation_token, + _drop_guard: Arc::new(drop_guard), + completion, + } + } + + fn cancel(&self) { + self.cancellation_token.cancel(); + } + + async fn wait(&self, participant_name: &str) { + if let Some(err) = self.completion.clone().await { + error!(participant = %participant_name, "Participant task failed while closing: {err}"); + } + } +} + impl Participant { pub fn spawn_with_app_config(config: &Config, cookie_manager: HyperSessionCookieManger) -> Result { let (participant, _) = Self::spawn_with_app_config_and_receiver(config, cookie_manager)?; @@ -83,6 +145,7 @@ impl Participant { ParticipantBackendKind::Local => Self::spawn_with_app_config(config, cookie_manager), ParticipantBackendKind::Cloudflare => Self::spawn_cloudflare(config, cookie_manager), ParticipantBackendKind::RemoteStub => Self::spawn_remote_stub(config, cookie_manager), + ParticipantBackendKind::AwsDeviceFarm => Self::spawn_device_farm(config, cookie_manager), } } @@ -123,8 +186,9 @@ impl Participant { name, created: chrono::Utc::now(), state: state_receiver, - _participant_task_guard: task_guard, + participant_task: task_guard, sender: sender_tx, + close_strategy: CloseStrategy::DriverCloseOnly, }, receiver_rx, )) @@ -152,8 +216,9 @@ impl Participant { name, created: Utc::now(), state: state_receiver, - _participant_task_guard: task_guard, + participant_task: task_guard, sender, + close_strategy: CloseStrategy::DriverCloseOnly, }) } @@ -189,8 +254,70 @@ impl Participant { name, created: Utc::now(), state: state_receiver, - _participant_task_guard: task_guard, + participant_task: task_guard, + sender, + close_strategy: CloseStrategy::LeaveBeforeClose, + }) + } + + pub fn spawn_device_farm(config: &Config, cookie_manager: HyperSessionCookieManger) -> Result { + let device_farm_config = config.device_farm.clone(); + let api = Arc::new(crate::participant::device_farm::AwsTestGrid::new( + &device_farm_config.region, + )); + Self::spawn_device_farm_with_api(config, cookie_manager, api) + } + + #[doc(hidden)] + pub fn spawn_device_farm_with_api( + config: &Config, + cookie_manager: HyperSessionCookieManger, + api: Arc, + ) -> Result { + use crate::participant::device_farm::{ + DeviceFarmLaunchOptions, + DeviceFarmSession, + }; + + let session_url = config.url.clone().ok_or_eyre("No session URL provided in the config")?; + let frontend_kind = ResolvedFrontendKind::from_session_url(&session_url); + let base_url = session_url.origin().unicode_serialization(); + let cookie = matches!(frontend_kind, ResolvedFrontendKind::HyperCore) + .then(|| cookie_manager.give_cookie(&base_url)) + .flatten(); + let name = cookie.as_ref().map(BorrowedCookie::username); + let participant_config = ParticipantConfig::new(config, name)?; + let launch_spec = ParticipantLaunchSpec::from(participant_config); + let name = launch_spec.username.clone(); + + let device_farm_config = config.device_farm.clone(); + let launch_options = DeviceFarmLaunchOptions::from(config); + + let (sender, receiver) = unbounded_channel::(); + let (log_sender, _log_receiver) = unbounded_channel::(); + + let (state_receiver, task_guard) = spawn_session( + name.clone(), + receiver, + log_sender.clone(), + DeviceFarmSession::new( + launch_spec, + launch_options, + device_farm_config, + log_sender, + cookie, + cookie_manager, + api, + ), + ); + + Ok(Self { + name, + created: Utc::now(), + state: state_receiver, + participant_task: task_guard, sender, + close_strategy: CloseStrategy::DriverCloseOnly, }) } } @@ -200,24 +327,19 @@ fn spawn_session( receiver: UnboundedReceiver, log_sender: UnboundedSender, session: S, -) -> (watch::Receiver, Arc) +) -> (watch::Receiver, ParticipantTaskControl) where S: ParticipantDriverSession + 'static, { let task_cancellation_token = CancellationToken::new(); + let task_token = task_cancellation_token.clone(); let task_cancellation_guard = task_cancellation_token.clone().drop_guard(); let (state_sender, state_receiver) = watch::channel(Default::default()); let task_sender_for_task = log_sender.clone(); - tokio::task::spawn(async move { - let result = run_participant_runtime( - receiver, - log_sender.clone(), - state_sender, - session, - task_cancellation_token.clone(), - ) - .await; + let handle = tokio::task::spawn(async move { + let result = + run_participant_runtime(receiver, log_sender.clone(), state_sender, session, task_token.clone()).await; if let Err(err) = result { error!(participant = %name, "Failed to create participant: {err}"); @@ -236,18 +358,38 @@ where )); }); - (state_receiver, Arc::new(task_cancellation_guard)) + ( + state_receiver, + ParticipantTaskControl::new(task_cancellation_token, task_cancellation_guard, handle), + ) +} + +async fn wait_for_close_observed(state: &mut watch::Receiver) { + let mut saw_running = state.borrow().running; + + loop { + let running = state.borrow().running; + if running { + saw_running = true; + } else if saw_running { + return; + } + + if state.changed().await.is_err() { + return; + } + } } impl Participant { pub async fn close(mut self) { let initial_state = self.state.borrow().clone(); if !initial_state.running { - debug!(participant = %self.name, "Already closed the browser"); - return; + debug!(participant = %self.name, "Closing participant before it reported running"); + self.participant_task.cancel(); } - if initial_state.joined { + if self.close_strategy == CloseStrategy::LeaveBeforeClose && initial_state.joined { if self.sender.send(ParticipantMessage::Leave).is_err() { error!(participant = %self.name, "Was not able to send ParticipantMessage::Leave message"); } else { @@ -269,18 +411,28 @@ impl Participant { } if self.sender.send(ParticipantMessage::Close).is_ok() { - match timeout(Duration::from_secs(10), self.state.wait_for(|state| !state.running)).await { - Ok(Ok(_)) => {} - Ok(Err(err)) => { - error!(participant = %self.name, "Failed to wait for participant to close: {err}"); - } - Err(_) => { - warn!(participant = %self.name, "Timed out waiting for participant to close"); + // The timeout is only a progress heartbeat. Returning while the + // participant task is still running lets the TUI exit and drop + // driver state during Tokio shutdown. + loop { + match timeout( + PARTICIPANT_CLOSE_PROGRESS_INTERVAL, + wait_for_close_observed(&mut self.state), + ) + .await + { + Ok(()) => break, + Err(_) => { + warn!(participant = %self.name, "Still waiting for participant to close"); + } } } } else { error!(participant = %self.name, "Was not able to send ParticipantMessage::Close message"); + self.participant_task.cancel(); } + + self.participant_task.wait(&self.name).await; } pub fn join(&self) { @@ -347,15 +499,446 @@ impl Participant { self.send_message(ParticipantMessage::SetNoiseSuppression(value)); } - pub fn set_webcam_resolutions(&self, value: client_simulator_config::WebcamResolution) { - self.send_message(ParticipantMessage::SetWebcamResolutions(value)); + pub fn set_video_constraint_publish_webcam(&self, value: client_simulator_config::VideoConstraint) { + self.send_message(ParticipantMessage::SetVideoConstraintPublishWebcam(value)); } - pub fn set_webcam_resolution(&self, value: client_simulator_config::WebcamResolution) { - self.set_webcam_resolutions(value); + pub fn set_video_constraint_subscribe(&self, value: client_simulator_config::VideoConstraint) { + self.send_message(ParticipantMessage::SetVideoConstraintSubscribe(value)); + } + + pub fn set_video_max_concurrent_tracks(&self, value: Option) { + self.send_message(ParticipantMessage::SetVideoMaxConcurrentTracks(value)); } pub fn toggle_background_blur(&self) { self.send_message(ParticipantMessage::ToggleBackgroundBlur); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::participant::shared::{ + DriverTermination, + ParticipantState, + }; + use futures::future::BoxFuture; + use std::{ + future::pending, + sync::{ + atomic::{ + AtomicUsize, + Ordering, + }, + Arc, + Mutex, + }, + }; + use tokio::{ + sync::{ + mpsc::unbounded_channel, + oneshot, + }, + time::sleep, + }; + + struct RecordingCloseDriver { + commands: Arc>>, + close_count: Arc, + } + + impl ParticipantDriverSession for RecordingCloseDriver { + fn participant_name(&self) -> &str { + "sim-user" + } + + fn start(&mut self) -> BoxFuture<'_, Result<()>> { + async move { Ok(()) }.boxed() + } + + fn handle_command(&mut self, message: ParticipantMessage) -> BoxFuture<'_, Result<()>> { + async move { + self.commands.lock().unwrap().push(message); + Ok(()) + } + .boxed() + } + + fn refresh_state(&mut self) -> BoxFuture<'_, Result> { + async move { + Ok(ParticipantState { + username: "sim-user".to_string(), + running: true, + joined: true, + ..Default::default() + }) + } + .boxed() + } + + fn close(&mut self) -> BoxFuture<'_, Result<()>> { + async move { + self.close_count.fetch_add(1, Ordering::SeqCst); + Ok(()) + } + .boxed() + } + + fn wait_for_termination(&mut self) -> BoxFuture<'_, DriverTermination> { + async move { pending::().await }.boxed() + } + } + + #[tokio::test] + async fn close_uses_driver_close_without_sending_leave_first() { + let (command_tx, command_rx) = unbounded_channel(); + let (log_tx, _log_rx) = unbounded_channel(); + let commands = Arc::new(Mutex::new(Vec::new())); + let close_count = Arc::new(AtomicUsize::new(0)); + let (state, task_guard) = spawn_session( + "sim-user".to_string(), + command_rx, + log_tx, + RecordingCloseDriver { + commands: Arc::clone(&commands), + close_count: Arc::clone(&close_count), + }, + ); + + let participant = Participant { + name: "sim-user".to_string(), + created: Utc::now(), + state, + participant_task: task_guard, + sender: command_tx, + close_strategy: CloseStrategy::DriverCloseOnly, + }; + + participant + .state + .clone() + .wait_for(|state| state.running && state.joined) + .await + .unwrap(); + + participant.close().await; + + assert_eq!(close_count.load(Ordering::SeqCst), 1); + assert!(commands.lock().unwrap().is_empty()); + } + + struct BlockingCloseDriver { + close_rx: Option>, + } + + impl ParticipantDriverSession for BlockingCloseDriver { + fn participant_name(&self) -> &str { + "sim-user" + } + + fn start(&mut self) -> BoxFuture<'_, Result<()>> { + async move { Ok(()) }.boxed() + } + + fn handle_command(&mut self, _message: ParticipantMessage) -> BoxFuture<'_, Result<()>> { + async move { Ok(()) }.boxed() + } + + fn refresh_state(&mut self) -> BoxFuture<'_, Result> { + async move { + Ok(ParticipantState { + username: "sim-user".to_string(), + running: true, + joined: true, + ..Default::default() + }) + } + .boxed() + } + + fn close(&mut self) -> BoxFuture<'_, Result<()>> { + let close_rx = self.close_rx.take().expect("close should be called once"); + async move { + let _ = close_rx.await; + Ok(()) + } + .boxed() + } + + fn wait_for_termination(&mut self) -> BoxFuture<'_, DriverTermination> { + async move { pending::().await }.boxed() + } + } + + #[tokio::test] + async fn close_keeps_waiting_after_progress_timeout_until_driver_stops() { + let (command_tx, command_rx) = unbounded_channel(); + let (log_tx, _log_rx) = unbounded_channel(); + let (close_tx, close_rx) = oneshot::channel(); + let (state, task_guard) = spawn_session( + "sim-user".to_string(), + command_rx, + log_tx, + BlockingCloseDriver { + close_rx: Some(close_rx), + }, + ); + + let participant = Participant { + name: "sim-user".to_string(), + created: Utc::now(), + state, + participant_task: task_guard, + sender: command_tx, + close_strategy: CloseStrategy::DriverCloseOnly, + }; + + participant + .state + .clone() + .wait_for(|state| state.running && state.joined) + .await + .unwrap(); + + let state = participant.state.clone(); + let close_task = tokio::spawn(participant.close()); + + sleep(PARTICIPANT_CLOSE_PROGRESS_INTERVAL * 2).await; + assert!(!close_task.is_finished()); + + close_tx.send(()).unwrap(); + timeout(Duration::from_secs(1), close_task).await.unwrap().unwrap(); + assert!(!state.borrow().running); + } + + struct CancelGuard { + cancel_count: Arc, + disarmed: bool, + } + + impl CancelGuard { + fn new(cancel_count: Arc) -> Self { + Self { + cancel_count, + disarmed: false, + } + } + + fn disarm(&mut self) { + self.disarmed = true; + } + } + + impl Drop for CancelGuard { + fn drop(&mut self) { + if !self.disarmed { + self.cancel_count.fetch_add(1, Ordering::SeqCst); + } + } + } + + struct BlockingStartDriver { + start_rx: Option>, + start_cancel_count: Arc, + close_count: Arc, + } + + impl ParticipantDriverSession for BlockingStartDriver { + fn participant_name(&self) -> &str { + "sim-user" + } + + fn start(&mut self) -> BoxFuture<'_, Result<()>> { + let start_rx = self.start_rx.take().expect("start should be called once"); + let start_cancel_count = Arc::clone(&self.start_cancel_count); + async move { + let mut cancel_guard = CancelGuard::new(start_cancel_count); + let _ = start_rx.await; + cancel_guard.disarm(); + Ok(()) + } + .boxed() + } + + fn handle_command(&mut self, _message: ParticipantMessage) -> BoxFuture<'_, Result<()>> { + async move { Ok(()) }.boxed() + } + + fn refresh_state(&mut self) -> BoxFuture<'_, Result> { + async move { + Ok(ParticipantState { + username: "sim-user".to_string(), + running: true, + joined: true, + ..Default::default() + }) + } + .boxed() + } + + fn close(&mut self) -> BoxFuture<'_, Result<()>> { + async move { + self.close_count.fetch_add(1, Ordering::SeqCst); + Ok(()) + } + .boxed() + } + + fn wait_for_termination(&mut self) -> BoxFuture<'_, DriverTermination> { + async move { pending::().await }.boxed() + } + } + + #[tokio::test] + async fn close_does_not_cancel_start_after_running_state_is_published() { + let (command_tx, command_rx) = unbounded_channel(); + let (log_tx, _log_rx) = unbounded_channel(); + let (start_tx, start_rx) = oneshot::channel(); + let start_cancel_count = Arc::new(AtomicUsize::new(0)); + let close_count = Arc::new(AtomicUsize::new(0)); + let (state, task_guard) = spawn_session( + "sim-user".to_string(), + command_rx, + log_tx, + BlockingStartDriver { + start_rx: Some(start_rx), + start_cancel_count: Arc::clone(&start_cancel_count), + close_count: Arc::clone(&close_count), + }, + ); + + let participant = Participant { + name: "sim-user".to_string(), + created: Utc::now(), + state: state.clone(), + participant_task: task_guard, + sender: command_tx, + close_strategy: CloseStrategy::DriverCloseOnly, + }; + + state + .clone() + .wait_for(|state| state.running) + .await + .expect("participant should publish running state before start completes"); + + let close_task = tokio::spawn(participant.close()); + + sleep(PARTICIPANT_CLOSE_PROGRESS_INTERVAL * 2).await; + assert!(!close_task.is_finished()); + assert_eq!(start_cancel_count.load(Ordering::SeqCst), 0); + assert_eq!(close_count.load(Ordering::SeqCst), 0); + + start_tx.send(()).unwrap(); + timeout(Duration::from_secs(1), close_task).await.unwrap().unwrap(); + assert_eq!(start_cancel_count.load(Ordering::SeqCst), 0); + assert_eq!(close_count.load(Ordering::SeqCst), 1); + assert!(!state.borrow().running); + } + + struct PendingStartDriver { + close_count: Arc, + } + + impl ParticipantDriverSession for PendingStartDriver { + fn participant_name(&self) -> &str { + "sim-user" + } + + fn start(&mut self) -> BoxFuture<'_, Result<()>> { + async move { pending::>().await }.boxed() + } + + fn handle_command(&mut self, _message: ParticipantMessage) -> BoxFuture<'_, Result<()>> { + async move { Ok(()) }.boxed() + } + + fn refresh_state(&mut self) -> BoxFuture<'_, Result> { + async move { + Ok(ParticipantState { + username: "sim-user".to_string(), + running: true, + joined: true, + ..Default::default() + }) + } + .boxed() + } + + fn close(&mut self) -> BoxFuture<'_, Result<()>> { + async move { + self.close_count.fetch_add(1, Ordering::SeqCst); + Ok(()) + } + .boxed() + } + + fn wait_for_termination(&mut self) -> BoxFuture<'_, DriverTermination> { + async move { pending::().await }.boxed() + } + } + + #[tokio::test] + async fn close_cleans_up_participant_before_running_state_is_published() { + let (command_tx, command_rx) = unbounded_channel(); + let (log_tx, _log_rx) = unbounded_channel(); + let close_count = Arc::new(AtomicUsize::new(0)); + let (state, task_guard) = spawn_session( + "sim-user".to_string(), + command_rx, + log_tx, + PendingStartDriver { + close_count: Arc::clone(&close_count), + }, + ); + + let participant = Participant { + name: "sim-user".to_string(), + created: Utc::now(), + state: state.clone(), + participant_task: task_guard, + sender: command_tx, + close_strategy: CloseStrategy::DriverCloseOnly, + }; + + participant.close().await; + + assert_eq!(close_count.load(Ordering::SeqCst), 1); + assert!(!state.borrow().running); + } + + #[tokio::test] + async fn racing_close_callers_all_wait_for_runtime_task_completion() { + let (command_tx, command_rx) = unbounded_channel(); + drop(command_rx); + let (_state_tx, state) = watch::channel(ParticipantState::default()); + let task_cancellation_token = CancellationToken::new(); + let task_cancellation_guard = task_cancellation_token.clone().drop_guard(); + let (release_task_tx, release_task_rx) = oneshot::channel(); + let task_handle = tokio::spawn(async move { + let _ = release_task_rx.await; + }); + let participant_task = + ParticipantTaskControl::new(task_cancellation_token, task_cancellation_guard, task_handle); + let participant = Participant { + name: "sim-user".to_string(), + created: Utc::now(), + state, + participant_task, + sender: command_tx, + close_strategy: CloseStrategy::DriverCloseOnly, + }; + + let close_task_one = tokio::spawn(participant.clone().close()); + let close_task_two = tokio::spawn(participant.close()); + + sleep(PARTICIPANT_CLOSE_PROGRESS_INTERVAL * 2).await; + assert!(!close_task_one.is_finished()); + assert!(!close_task_two.is_finished()); + + release_task_tx.send(()).unwrap(); + timeout(Duration::from_secs(1), close_task_one).await.unwrap().unwrap(); + timeout(Duration::from_secs(1), close_task_two).await.unwrap().unwrap(); + } +} diff --git a/browser/src/participant/remote_stub.rs b/browser/src/participant/remote_stub.rs index 00a7a63..a8e6bc5 100644 --- a/browser/src/participant/remote_stub.rs +++ b/browser/src/participant/remote_stub.rs @@ -62,7 +62,9 @@ impl ParticipantDriverSession for RemoteStubSession { auto_gain_control: self.launch_spec.settings.auto_gain_control, noise_suppression: self.launch_spec.settings.noise_suppression, transport_mode: self.launch_spec.settings.transport, - webcam_resolution: self.launch_spec.settings.resolution, + video_constraint_publish_webcam: self.launch_spec.settings.video_constraint_publish_webcam, + video_constraint_subscribe: self.launch_spec.settings.video_constraint_subscribe, + video_max_concurrent_tracks: self.launch_spec.settings.video_max_concurrent_tracks, background_blur: self.launch_spec.settings.blur, screenshare_activated: self.launch_spec.settings.screenshare_enabled, }; @@ -106,9 +108,23 @@ impl ParticipantDriverSession for RemoteStubSession { self.state.noise_suppression = value; self.log_message("debug", format!("remote stub set noise suppression to {value}")); } - ParticipantMessage::SetWebcamResolutions(value) => { - self.state.webcam_resolution = value; - self.log_message("debug", format!("remote stub set camera resolution to {value}")); + ParticipantMessage::SetVideoConstraintPublishWebcam(value) => { + self.state.video_constraint_publish_webcam = value; + self.log_message( + "debug", + format!("remote stub set outgoing webcam video constraint to {value}"), + ); + } + ParticipantMessage::SetVideoConstraintSubscribe(value) => { + self.state.video_constraint_subscribe = value; + self.log_message("debug", format!("remote stub set incoming video constraint to {value}")); + } + ParticipantMessage::SetVideoMaxConcurrentTracks(value) => { + self.state.video_max_concurrent_tracks = value; + self.log_message( + "debug", + format!("remote stub set max concurrent video tracks to {value:?}"), + ); } ParticipantMessage::ToggleBackgroundBlur => { self.state.background_blur = !self.state.background_blur; diff --git a/browser/src/participant/shared/messages.rs b/browser/src/participant/shared/messages.rs index dd00abf..b29ca22 100644 --- a/browser/src/participant/shared/messages.rs +++ b/browser/src/participant/shared/messages.rs @@ -1,10 +1,10 @@ use client_simulator_config::{ NoiseSuppression, - WebcamResolution, + VideoConstraint, }; -use derive_more::Display; +use std::fmt; -#[derive(Clone, Display, serde::Serialize, serde::Deserialize, Debug)] +#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)] pub enum ParticipantMessage { Join, Leave, @@ -14,10 +14,18 @@ pub enum ParticipantMessage { ToggleScreenshare, ToggleAutoGainControl, SetNoiseSuppression(NoiseSuppression), - SetWebcamResolutions(WebcamResolution), + SetVideoConstraintPublishWebcam(VideoConstraint), + SetVideoConstraintSubscribe(VideoConstraint), + SetVideoMaxConcurrentTracks(Option), ToggleBackgroundBlur, } +impl fmt::Display for ParticipantMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } +} + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ParticipantLogMessage { pub username: String, diff --git a/browser/src/participant/shared/runtime.rs b/browser/src/participant/shared/runtime.rs index be6225c..f805a7f 100644 --- a/browser/src/participant/shared/runtime.rs +++ b/browser/src/participant/shared/runtime.rs @@ -5,14 +5,27 @@ use super::{ }, ParticipantState, }; -use eyre::Result; +use eyre::{ + Report, + Result, +}; use futures::future::BoxFuture; -use tokio::sync::{ - mpsc::{ - UnboundedReceiver, - UnboundedSender, +use std::{ + future::pending, + time::Duration, +}; +use tokio::{ + sync::{ + mpsc::{ + UnboundedReceiver, + UnboundedSender, + }, + watch, + }, + time::{ + Instant, + MissedTickBehavior, }, - watch, }; use tokio_util::sync::CancellationToken; @@ -40,6 +53,14 @@ pub(in crate::participant) trait ParticipantDriverSession: Send { fn refresh_state(&mut self) -> BoxFuture<'_, Result>; fn close(&mut self) -> BoxFuture<'_, Result<()>>; fn wait_for_termination(&mut self) -> BoxFuture<'_, DriverTermination>; + + fn state_refresh_interval(&self) -> Option { + None + } + + fn state_refresh_error_termination(&self, _err: &Report) -> Option { + None + } } /// Drive one participant session by translating runtime messages into backend operations. @@ -95,13 +116,33 @@ where return Ok(()); } - sync_state(&mut driver, &state, &sender).await; + if let Some(termination) = sync_state(&mut driver, &state, &sender).await { + let participant_name = driver.participant_name().to_string(); + log_runtime_message(&sender, termination.level, &participant_name, termination.message); + if let Err(err) = driver.close().await { + log_runtime_message( + &sender, + "error", + &participant_name, + format!("Failed closing participant after state refresh failure: {err}"), + ); + } + mark_stopped(&state); + return Ok(()); + } + + let mut state_refresh_ticker = driver.state_refresh_interval().map(|interval| { + let mut ticker = tokio::time::interval_at(Instant::now() + interval, interval); + ticker.set_missed_tick_behavior(MissedTickBehavior::Delay); + ticker + }); loop { enum RuntimeEvent { Command(ParticipantMessage), ChannelClosed, Terminated(DriverTermination), + RefreshState, Cancelled, } @@ -114,6 +155,13 @@ where Some(message) => RuntimeEvent::Command(message), None => RuntimeEvent::ChannelClosed, }, + _ = async { + if let Some(ticker) = state_refresh_ticker.as_mut() { + ticker.tick().await; + } else { + pending::<()>().await; + } + } => RuntimeEvent::RefreshState, }; match event { @@ -152,6 +200,21 @@ where } break; } + RuntimeEvent::RefreshState => { + if let Some(termination) = sync_state(&mut driver, &state, &sender).await { + let participant_name = driver.participant_name().to_string(); + log_runtime_message(&sender, termination.level, &participant_name, termination.message); + if let Err(err) = driver.close().await { + log_runtime_message( + &sender, + "error", + &participant_name, + format!("Failed closing participant after state refresh failure: {err}"), + ); + } + break; + } + } RuntimeEvent::Command(ParticipantMessage::Close) => { if let Err(err) = driver.close().await { log_runtime_message( @@ -173,7 +236,19 @@ where ); } - sync_state(&mut driver, &state, &sender).await; + if let Some(termination) = sync_state(&mut driver, &state, &sender).await { + let participant_name = driver.participant_name().to_string(); + log_runtime_message(&sender, termination.level, &participant_name, termination.message); + if let Err(err) = driver.close().await { + log_runtime_message( + &sender, + "error", + &participant_name, + format!("Failed closing participant after state refresh failure: {err}"), + ); + } + break; + } } } } @@ -188,7 +263,8 @@ async fn sync_state( driver: &mut D, state: &watch::Sender, sender: &UnboundedSender, -) where +) -> Option +where D: ParticipantDriverSession, { match driver.refresh_state().await { @@ -198,14 +274,19 @@ async fn sync_state( state.send_modify(|current| { *current = next_state; }); + None } Err(err) => { - log_runtime_message( - sender, - "error", - driver.participant_name(), - format!("Failed refreshing participant state: {err}"), - ); + let termination = driver.state_refresh_error_termination(&err); + if termination.is_none() { + log_runtime_message( + sender, + "error", + driver.participant_name(), + format!("Failed refreshing participant state: {err}"), + ); + } + termination } } } @@ -325,7 +406,9 @@ mod tests { | ParticipantMessage::ToggleScreenshare | ParticipantMessage::ToggleAutoGainControl | ParticipantMessage::SetNoiseSuppression(_) - | ParticipantMessage::SetWebcamResolutions(_) + | ParticipantMessage::SetVideoConstraintPublishWebcam(_) + | ParticipantMessage::SetVideoConstraintSubscribe(_) + | ParticipantMessage::SetVideoMaxConcurrentTracks(_) | ParticipantMessage::ToggleBackgroundBlur => {} } Ok(()) diff --git a/browser/src/participant/shared/spec.rs b/browser/src/participant/shared/spec.rs index 79fcd64..9b80857 100644 --- a/browser/src/participant/shared/spec.rs +++ b/browser/src/participant/shared/spec.rs @@ -2,7 +2,7 @@ use client_simulator_config::{ NoiseSuppression, ParticipantConfig, TransportMode, - WebcamResolution, + VideoConstraint, }; use url::Url; @@ -31,7 +31,9 @@ pub(in crate::participant) struct ParticipantSettings { pub(in crate::participant) auto_gain_control: bool, pub(in crate::participant) noise_suppression: NoiseSuppression, pub(in crate::participant) transport: TransportMode, - pub(in crate::participant) resolution: WebcamResolution, + pub(in crate::participant) video_constraint_publish_webcam: VideoConstraint, + pub(in crate::participant) video_constraint_subscribe: VideoConstraint, + pub(in crate::participant) video_max_concurrent_tracks: Option, pub(in crate::participant) blur: bool, } @@ -45,7 +47,9 @@ impl From<&ParticipantConfig> for ParticipantSettings { auto_gain_control: app_config.auto_gain_control, noise_suppression: app_config.noise_suppression, transport: app_config.transport, - resolution: app_config.resolution, + video_constraint_publish_webcam: app_config.video_constraint_publish_webcam, + video_constraint_subscribe: app_config.video_constraint_subscribe, + video_max_concurrent_tracks: app_config.video_max_concurrent_tracks, blur: app_config.blur, } } @@ -89,7 +93,7 @@ mod tests { NoiseSuppression, ParticipantConfig, TransportMode, - WebcamResolution, + VideoConstraint, }; use url::Url; @@ -105,7 +109,9 @@ mod tests { auto_gain_control: true, noise_suppression: NoiseSuppression::RNNoise, transport: TransportMode::WebRTC, - resolution: WebcamResolution::P720, + video_constraint_publish_webcam: VideoConstraint::P480, + video_constraint_subscribe: VideoConstraint::P720, + video_max_concurrent_tracks: Some(2), blur: true, ..Default::default() }, @@ -122,7 +128,9 @@ mod tests { assert!(spec.settings.auto_gain_control); assert_eq!(spec.settings.noise_suppression, NoiseSuppression::RNNoise); assert_eq!(spec.settings.transport, TransportMode::WebRTC); - assert_eq!(spec.settings.resolution, WebcamResolution::P720); + assert_eq!(spec.settings.video_constraint_publish_webcam, VideoConstraint::P480); + assert_eq!(spec.settings.video_constraint_subscribe, VideoConstraint::P720); + assert_eq!(spec.settings.video_max_concurrent_tracks, Some(2)); assert!(spec.settings.blur); } } diff --git a/browser/src/participant/shared/state.rs b/browser/src/participant/shared/state.rs index a1302c0..c1db5a4 100644 --- a/browser/src/participant/shared/state.rs +++ b/browser/src/participant/shared/state.rs @@ -1,7 +1,7 @@ use client_simulator_config::{ NoiseSuppression, TransportMode, - WebcamResolution, + VideoConstraint, }; #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] @@ -14,7 +14,9 @@ pub struct ParticipantState { pub auto_gain_control: bool, pub noise_suppression: NoiseSuppression, pub transport_mode: TransportMode, - pub webcam_resolution: WebcamResolution, + pub video_constraint_publish_webcam: VideoConstraint, + pub video_constraint_subscribe: VideoConstraint, + pub video_max_concurrent_tracks: Option, pub background_blur: bool, pub screenshare_activated: bool, } diff --git a/browser/src/util.rs b/browser/src/util.rs deleted file mode 100644 index 5765f38..0000000 --- a/browser/src/util.rs +++ /dev/null @@ -1,24 +0,0 @@ -use chromiumoxide::{ - Element, - Page, -}; -use eyre::Result; -use std::time::Duration; - -/// Poll for a DOM element until it appears or the timeout elapses. -pub(crate) async fn wait_for_element(page: &Page, selector: &str, timeout: Duration) -> Result { - let now = std::time::Instant::now(); - - loop { - if let Ok(element) = page.find_element(selector).await { - return Ok(element); - } - - // Sleep for a short duration to avoid busy waiting. - tokio::time::sleep(Duration::from_millis(100)).await; - - if now.elapsed() > timeout { - return Err(eyre::eyre!("timeout waiting for element: {}", selector)); - } - } -} diff --git a/browser/tests/cloudflare_driver.rs b/browser/tests/cloudflare_driver.rs index a60892b..3e588dc 100644 --- a/browser/tests/cloudflare_driver.rs +++ b/browser/tests/cloudflare_driver.rs @@ -9,7 +9,7 @@ use client_simulator_config::{ CloudflareConfig, Config, ParticipantBackendKind, - WebcamResolution, + VideoConstraint, }; use serde_json::{ json, @@ -50,7 +50,7 @@ async fn cloudflare_runtime_updates_public_participant_state_from_worker_command json!({ "ok": true, "sessionId": "cf-runtime-commands", - "state": worker_state_json(false, false, false, "p720"), + "state": worker_state_json(false, false, false, "720p"), "log": [], }), ), @@ -59,7 +59,7 @@ async fn cloudflare_runtime_updates_public_participant_state_from_worker_command json!({ "ok": true, "sessionId": "cf-runtime-commands", - "state": worker_state_json(true, false, false, "p720"), + "state": worker_state_json(true, false, false, "720p"), "log": [], }), ), @@ -68,7 +68,7 @@ async fn cloudflare_runtime_updates_public_participant_state_from_worker_command json!({ "ok": true, "sessionId": "cf-runtime-commands", - "state": worker_state_json(true, true, false, "p720"), + "state": worker_state_json(true, true, false, "720p"), "log": [], }), ), @@ -77,7 +77,7 @@ async fn cloudflare_runtime_updates_public_participant_state_from_worker_command json!({ "ok": true, "sessionId": "cf-runtime-commands", - "state": worker_state_json(true, true, true, "p720"), + "state": worker_state_json(true, true, true, "720p"), "log": [], }), ), @@ -86,7 +86,7 @@ async fn cloudflare_runtime_updates_public_participant_state_from_worker_command json!({ "ok": true, "sessionId": "cf-runtime-commands", - "state": worker_state_json(false, true, true, "p720"), + "state": worker_state_json(false, true, true, "720p"), "log": [], }), ), @@ -109,7 +109,7 @@ async fn cloudflare_runtime_updates_public_participant_state_from_worker_command let state = participant.state.clone(); let started = wait_for_state(&state, |current| { - current.running && !current.joined && current.webcam_resolution == WebcamResolution::P720 + current.running && !current.joined && current.video_constraint_publish_webcam == VideoConstraint::P720 }) .await; assert!(started.running); @@ -157,7 +157,7 @@ async fn cloudflare_runtime_survives_command_failures_and_can_still_close() { json!({ "ok": true, "sessionId": "cf-runtime-errors", - "state": worker_state_json(false, false, false, "p720"), + "state": worker_state_json(false, false, false, "720p"), "log": [], }), ), @@ -187,7 +187,7 @@ async fn cloudflare_runtime_survives_command_failures_and_can_still_close() { let state = participant.state.clone(); wait_for_state(&state, |current| { - current.running && !current.joined && current.webcam_resolution == WebcamResolution::P720 + current.running && !current.joined && current.video_constraint_publish_webcam == VideoConstraint::P720 }) .await; @@ -218,7 +218,7 @@ async fn cloudflare_runtime_marks_participant_stopped_when_worker_state_poll_fai json!({ "ok": true, "sessionId": "cf-runtime-terminated", - "state": worker_state_json(true, false, false, "p720"), + "state": worker_state_json(true, false, false, "720p"), "log": [], }), ), @@ -240,7 +240,7 @@ async fn cloudflare_runtime_marks_participant_stopped_when_worker_state_poll_fai let state = participant.state.clone(); wait_for_state(&state, |current| { - current.running && current.joined && current.webcam_resolution == WebcamResolution::P720 + current.running && current.joined && current.video_constraint_publish_webcam == VideoConstraint::P720 }) .await; wait_for_state(&state, |current| !current.running).await; @@ -277,7 +277,9 @@ async fn cloudflare_runtime_fetches_hyper_core_cookie_before_creating_worker_ses "autoGainControl": true, "noiseSuppression": "ai-coustics-rook-s-48khz", "transportMode": "webrtc", - "webcamResolution": "p1080", + "videoConstraintPublishWebcam": "1080p", + "videoConstraintSubscribe": "none", + "videoMaxConcurrentTracks": null, "backgroundBlur": false, }, "log": [], @@ -288,7 +290,7 @@ async fn cloudflare_runtime_fetches_hyper_core_cookie_before_creating_worker_ses json!({ "ok": true, "sessionId": "cf-runtime-core", - "state": worker_state_json(false, false, false, "p720"), + "state": worker_state_json(false, false, false, "720p"), "log": [], }), ), @@ -314,7 +316,7 @@ async fn cloudflare_runtime_fetches_hyper_core_cookie_before_creating_worker_ses current.running && current.joined && current.video_activated - && current.webcam_resolution == WebcamResolution::P1080 + && current.video_constraint_publish_webcam == VideoConstraint::P1080 }) .await; assert!(started.running); @@ -370,7 +372,7 @@ fn cloudflare_config(session_url: &str, base_url: &str, health_poll_interval_ms: config } -fn worker_state_json(joined: bool, muted: bool, video_activated: bool, webcam_resolution: &str) -> Value { +fn worker_state_json(joined: bool, muted: bool, video_activated: bool, video_constraint_publish_webcam: &str) -> Value { json!({ "running": true, "joined": joined, @@ -380,7 +382,9 @@ fn worker_state_json(joined: bool, muted: bool, video_activated: bool, webcam_re "autoGainControl": true, "noiseSuppression": "none", "transportMode": "webrtc", - "webcamResolution": webcam_resolution, + "videoConstraintPublishWebcam": video_constraint_publish_webcam, + "videoConstraintSubscribe": "none", + "videoMaxConcurrentTracks": null, "backgroundBlur": false, }) } diff --git a/browser/tests/device_farm_driver.rs b/browser/tests/device_farm_driver.rs new file mode 100644 index 0000000..b5f5c95 --- /dev/null +++ b/browser/tests/device_farm_driver.rs @@ -0,0 +1,655 @@ +//! Integration test for the AWS Device Farm backend. +//! +//! Uses an in-process TCP server that speaks just enough of the W3C WebDriver +//! HTTP protocol for thirtyfour to drive a session, plus a stubbed TestGridApi +//! that points thirtyfour at that server. No real AWS or browser is involved. + +use client_simulator_browser::{ + auth::HyperSessionCookieManger, + participant::{ + device_farm::{ + close_test_grid_session, + DeviceFarmCloseResult, + }, + Participant, + ParticipantState, + }, + testing::TestGridApi, +}; +use client_simulator_config::{ + Config, + DeviceFarmConfig, + ParticipantBackendKind, +}; +use eyre::Result; +use futures::{ + future::BoxFuture, + FutureExt as _, +}; +use serde_json::{ + json, + Value, +}; +use std::{ + fs, + path::PathBuf, + sync::{ + Arc, + Mutex, + }, + time::{ + Duration, + SystemTime, + UNIX_EPOCH, + }, +}; +use tokio::{ + io::{ + AsyncReadExt as _, + AsyncWriteExt as _, + }, + net::TcpListener, + sync::watch, + time::timeout, +}; + +#[tokio::test] +async fn device_farm_session_creates_url_connects_joins_and_closes() { + let (base_url, requests, server) = spawn_webdriver_mock().await; + let cookie_manager = HyperSessionCookieManger::new(unique_temp_dir().join("cookies.json")); + let participant = Participant::spawn_device_farm_with_api( + &device_farm_config(), + cookie_manager, + Arc::new(TestGridStub { url: base_url }), + ) + .expect("device farm participant should spawn"); + let state = participant.state.clone(); + + let joined = wait_for_state(&state, |current| current.running && current.joined).await; + assert!(joined.running); + assert!(joined.joined); + assert!(!joined.muted); + assert!(joined.video_activated); + assert!(!joined.screenshare_activated); + + participant.close().await; + assert!(!state.borrow().running); + + server.abort(); + + let requests = requests.lock().unwrap().clone(); + let paths = requests + .iter() + .map(|request| (request.method.as_str(), request.path.as_str())) + .collect::>(); + assert!(paths.contains(&("POST", "/session"))); + assert!(paths.contains(&("POST", "/session/df-1/url"))); + assert!(paths.iter().any(|(_, path)| path == &"/session/df-1/element")); + assert!(paths.iter().any(|(_, path)| path.ends_with("/click"))); + assert!(paths.contains(&("DELETE", "/session/df-1"))); +} + +#[tokio::test] +async fn device_farm_session_preserves_signed_test_grid_url_path_and_query() { + let signature = "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=test-signature"; + let (base_url, requests, server) = spawn_webdriver_mock_with_options(WebDriverMockOptions { + path_prefix: "/signed-grid/wd/hub".to_string(), + required_query: Some(signature.to_string()), + ..Default::default() + }) + .await; + let cookie_manager = HyperSessionCookieManger::new(unique_temp_dir().join("cookies.json")); + let participant = Participant::spawn_device_farm_with_api( + &device_farm_config(), + cookie_manager, + Arc::new(TestGridStub { + url: format!("{base_url}/signed-grid/wd/hub?{signature}"), + }), + ) + .expect("device farm participant should spawn"); + let state = participant.state.clone(); + + let joined = wait_for_state(&state, |current| current.running && current.joined).await; + assert!(joined.running); + assert!(joined.joined); + + participant.close().await; + server.abort(); + + let requests = requests.lock().unwrap().clone(); + assert!(requests + .iter() + .all(|request| { request.path.starts_with("/signed-grid/wd/hub/") && request.path.contains(signature) })); +} + +#[tokio::test] +async fn close_test_grid_session_preserves_signed_test_grid_url_path_and_query() { + let signature = "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=test-signature"; + let (base_url, requests, server) = spawn_webdriver_mock_with_options(WebDriverMockOptions { + path_prefix: "/signed-grid/wd/hub".to_string(), + required_query: Some(signature.to_string()), + ..Default::default() + }) + .await; + let api = TestGridStub { + url: format!("{base_url}/signed-grid/wd/hub?{signature}"), + }; + + let result = close_test_grid_session( + &api, + "arn:aws:devicefarm:us-west-2:123456789012:testgrid-project:abc", + 300, + "df-1", + ) + .await + .expect("close helper should send signed WebDriver delete"); + + assert_eq!(result, DeviceFarmCloseResult::Closed); + + server.abort(); + + let requests = requests.lock().unwrap().clone(); + assert_eq!(requests.len(), 1); + assert_eq!( + requests[0].path, + format!("/signed-grid/wd/hub/session/df-1?{signature}") + ); + assert_eq!(requests[0].method, "DELETE"); +} + +#[tokio::test] +async fn device_farm_session_polls_and_publishes_frontend_state_changes() { + let (base_url, requests, server) = spawn_webdriver_mock_with_options(WebDriverMockOptions { + media_change_after_attribute_reads: Some(5), + ..Default::default() + }) + .await; + let cookie_manager = HyperSessionCookieManger::new(unique_temp_dir().join("cookies.json")); + let mut config = device_farm_config(); + config.device_farm.health_poll_interval_ms = 100; + let participant = + Participant::spawn_device_farm_with_api(&config, cookie_manager, Arc::new(TestGridStub { url: base_url })) + .expect("device farm participant should spawn"); + let state = participant.state.clone(); + + let initial = { + let mut state = state.clone(); + timeout(Duration::from_secs(2), async move { + state + .wait_for(|current| { + current.running + && current.joined + && !current.muted + && current.video_activated + && !current.screenshare_activated + }) + .await + .unwrap() + .clone() + }) + .await + }; + + if initial.is_err() { + participant.clone().close().await; + server.abort(); + } + + let initial = initial.unwrap_or_else(|_| { + panic!( + "timed out waiting for initial Device Farm state; current: {:?}; requests: {:?}", + state.borrow(), + request_summary(&requests.lock().unwrap()) + ) + }); + assert!(!initial.muted); + assert!(initial.video_activated); + assert!(!initial.screenshare_activated); + + let updated = { + let mut state = state.clone(); + timeout(Duration::from_secs(2), async move { + state + .wait_for(|current| { + current.running + && current.joined + && current.muted + && !current.video_activated + && current.screenshare_activated + }) + .await + .unwrap() + .clone() + }) + .await + }; + + if updated.is_err() { + participant.clone().close().await; + server.abort(); + } + + let updated = updated.unwrap_or_else(|_| { + panic!( + "timed out waiting for Device Farm state refresh; requests: {:?}", + request_summary(&requests.lock().unwrap()) + ) + }); + assert!(updated.muted); + assert!(!updated.video_activated); + assert!(updated.screenshare_activated); + + participant.close().await; + server.abort(); +} + +#[tokio::test] +async fn device_farm_session_stops_when_webdriver_health_check_fails() { + let (base_url, _requests, server) = spawn_webdriver_mock_with_options(WebDriverMockOptions { + fail_current_url_after: Some(1), + ..Default::default() + }) + .await; + let cookie_manager = HyperSessionCookieManger::new(unique_temp_dir().join("cookies.json")); + let mut config = device_farm_config(); + config.device_farm.health_poll_interval_ms = 20; + let participant = + Participant::spawn_device_farm_with_api(&config, cookie_manager, Arc::new(TestGridStub { url: base_url })) + .expect("device farm participant should spawn"); + let state = participant.state.clone(); + + wait_for_state(&state, |current| current.running && current.joined).await; + + let stopped = { + let mut state = state.clone(); + timeout(Duration::from_secs(2), async move { + state + .wait_for(|current| !current.running && !current.joined) + .await + .unwrap() + .clone() + }) + .await + }; + + participant.close().await; + server.abort(); + + assert!( + stopped.is_ok(), + "Device Farm participant should stop when WebDriver stops responding" + ); +} + +#[derive(Debug)] +struct TestGridStub { + url: String, +} + +impl TestGridApi for TestGridStub { + fn create_test_grid_url(&self, _project_arn: &str, _expires_seconds: u64) -> BoxFuture<'_, Result> { + async move { Ok(self.url.clone()) }.boxed() + } +} + +fn device_farm_config() -> Config { + let mut config = Config::default(); + config.url = Some("https://example.com/m/demo".parse().unwrap()); + config.backend = ParticipantBackendKind::AwsDeviceFarm; + config.headless = true; + config.device_farm = DeviceFarmConfig { + project_arn: "arn:aws:devicefarm:us-west-2:123456789012:testgrid-project:abc".to_string(), + region: "us-west-2".to_string(), + url_expires_seconds: 300, + session_max_duration_ms: 60_000, + idle_timeout_ms: 30_000, + health_poll_interval_ms: 30_000, + debug: false, + }; + config +} + +async fn wait_for_state(state: &watch::Receiver, mut predicate: F) -> ParticipantState +where + F: FnMut(&ParticipantState) -> bool, +{ + let mut state = state.clone(); + timeout(Duration::from_secs(2), async move { + state.wait_for(|current| predicate(current)).await.unwrap().clone() + }) + .await + .expect("timed out waiting for participant state") +} + +#[derive(Clone, Debug)] +struct CapturedRequest { + method: String, + path: String, + body: String, +} + +#[derive(Debug, Default)] +struct WebDriverState { + joined: bool, + current_url_requests: usize, + media_attribute_reads: usize, +} + +#[derive(Debug, Default)] +struct WebDriverMockOptions { + path_prefix: String, + required_query: Option, + media_change_after_attribute_reads: Option, + fail_current_url_after: Option, +} + +async fn spawn_webdriver_mock() -> (String, Arc>>, tokio::task::JoinHandle<()>) { + spawn_webdriver_mock_with_options(WebDriverMockOptions::default()).await +} + +async fn spawn_webdriver_mock_with_options( + options: WebDriverMockOptions, +) -> (String, Arc>>, tokio::task::JoinHandle<()>) { + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let base_url = format!("http://{}", listener.local_addr().unwrap()); + let requests = Arc::new(Mutex::new(Vec::new())); + let requests_for_task = Arc::clone(&requests); + let state = Arc::new(Mutex::new(WebDriverState::default())); + let options = Arc::new(options); + + let task = tokio::spawn(async move { + loop { + let (mut stream, _) = listener.accept().await.unwrap(); + let request = read_request(&mut stream).await; + let response = webdriver_response(&request, &state, &options); + requests_for_task.lock().unwrap().push(request); + write_response(&mut stream, response).await; + } + }); + + (base_url, requests, task) +} + +fn webdriver_response( + request: &CapturedRequest, + state: &Arc>, + options: &WebDriverMockOptions, +) -> MockResponse { + let Some(path) = normalize_signed_path(request, options) else { + return no_such_element(); + }; + + match (request.method.as_str(), path.as_str()) { + ("POST", "/session") => MockResponse::json( + 200, + json!({ + "value": { + "sessionId": "df-1", + "capabilities": { + "browserName": "chrome" + } + } + }), + ), + ("POST", "/session/df-1/url") => MockResponse::json(200, json!({ "value": null })), + ("GET", "/session/df-1/url") => current_url_response(state, options), + ("DELETE", "/session/df-1") => MockResponse::json(200, json!({ "value": null })), + ("POST", "/session/df-1/element") => element_response(request, state), + _ if path.starts_with("/session/df-1/element/") => element_command_response(&path, state, options), + _ if path == "/session/df-1/execute/sync" => MockResponse::json(200, json!({ "value": null })), + _ => MockResponse::json(200, json!({ "value": null })), + } +} + +fn current_url_response(state: &Arc>, options: &WebDriverMockOptions) -> MockResponse { + let mut state = state.lock().unwrap(); + state.current_url_requests += 1; + if options + .fail_current_url_after + .is_some_and(|limit| state.current_url_requests > limit) + { + return MockResponse::json( + 500, + json!({ + "value": { + "error": "invalid session id", + "message": "session is no longer available", + "stacktrace": "" + } + }), + ); + } + + MockResponse::json(200, json!({ "value": "https://example.com/m/demo" })) +} + +fn normalize_signed_path(request: &CapturedRequest, options: &WebDriverMockOptions) -> Option { + let (path, query) = request + .path + .split_once('?') + .map_or((request.path.as_str(), ""), |(path, query)| (path, query)); + + if let Some(required_query) = &options.required_query { + if query != required_query { + return None; + } + } + + if options.path_prefix.is_empty() { + return Some(path.to_string()); + } + + path.strip_prefix(&options.path_prefix) + .filter(|path| path.starts_with('/')) + .map(ToOwned::to_owned) +} + +fn element_response(request: &CapturedRequest, state: &Arc>) -> MockResponse { + let selector = request_json(request)["value"].as_str().unwrap_or_default().to_string(); + let joined = state.lock().unwrap().joined; + + if selector.contains("trigger-leave-call") || selector.contains("button[aria-label=\"Leave\"]") { + if joined { + return element("leave"); + } + return no_such_element(); + } + + if selector.contains("join-button") { + return element("join"); + } + if selector.contains("meeting-lobby-display-name") { + return element("name"); + } + if selector.contains("toggle-audio") { + return element("audio"); + } + if selector.contains("toggle-video") { + return element("video"); + } + if selector.contains("toggle-screen-share") { + return element("screen"); + } + if selector.contains("alert-dialog-footer") { + return element("confirm"); + } + + element("generic") +} + +fn element_command_response( + path: &str, + state: &Arc>, + options: &WebDriverMockOptions, +) -> MockResponse { + if path.ends_with("/click") { + if path.contains("/element/join/") { + state.lock().unwrap().joined = true; + } else if path.contains("/element/leave/") || path.contains("/element/confirm/") { + state.lock().unwrap().joined = false; + } + return MockResponse::json(200, json!({ "value": null })); + } + + if let Some(attribute) = path.rsplit('/').next().filter(|_| path.contains("/attribute/")) { + let media_attribute = attribute == "data-test-state" + && (path.contains("/element/audio/") + || path.contains("/element/video/") + || path.contains("/element/screen/")); + let media_changed = { + let mut state = state.lock().unwrap(); + if media_attribute { + state.media_attribute_reads += 1; + } + options + .media_change_after_attribute_reads + .is_some_and(|limit| state.media_attribute_reads > limit) + }; + let value = match attribute { + "data-test-state" if path.contains("/element/audio/") => { + json!(if media_changed { "false" } else { "true" }) + } + "data-test-state" if path.contains("/element/video/") => { + json!(if media_changed { "false" } else { "true" }) + } + "data-test-state" if path.contains("/element/screen/") => { + json!(if media_changed { "true" } else { "false" }) + } + "aria-pressed" => Value::Null, + "aria-label" => Value::Null, + _ => Value::Null, + }; + return MockResponse::json(200, json!({ "value": value })); + } + + MockResponse::json(200, json!({ "value": null })) +} + +fn element(id: &str) -> MockResponse { + MockResponse::json( + 200, + json!({ + "value": { + "element-6066-11e4-a52e-4f735466cecf": id, + "ELEMENT": id + } + }), + ) +} + +fn no_such_element() -> MockResponse { + MockResponse::json( + 404, + json!({ + "value": { + "error": "no such element", + "message": "no such element", + "stacktrace": "" + } + }), + ) +} + +#[derive(Debug)] +struct MockResponse { + status: u16, + body: String, +} + +impl MockResponse { + fn json(status: u16, body: Value) -> Self { + Self { + status, + body: serde_json::to_string(&body).unwrap(), + } + } +} + +async fn write_response(stream: &mut tokio::net::TcpStream, response: MockResponse) { + let reply = format!( + "HTTP/1.1 {} {}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", + response.status, + status_text(response.status), + response.body.len(), + response.body, + ); + stream.write_all(reply.as_bytes()).await.unwrap(); +} + +async fn read_request(stream: &mut tokio::net::TcpStream) -> CapturedRequest { + let mut buffer = Vec::new(); + let mut chunk = [0_u8; 4096]; + let header_end; + + loop { + let read = stream.read(&mut chunk).await.unwrap(); + assert!(read > 0, "unexpected EOF while reading request headers"); + buffer.extend_from_slice(&chunk[..read]); + + if let Some(end) = find_header_end(&buffer) { + header_end = end; + break; + } + } + + let headers_bytes = &buffer[..header_end]; + let headers_text = String::from_utf8(headers_bytes.to_vec()).unwrap(); + let mut lines = headers_text.split("\r\n"); + let request_line = lines.next().unwrap(); + let mut request_line = request_line.split_whitespace(); + let method = request_line.next().unwrap().to_owned(); + let path = request_line.next().unwrap().to_owned(); + + let mut content_length = 0_usize; + for line in lines.filter(|line| !line.is_empty()) { + let (name, value) = line.split_once(':').unwrap(); + if name.eq_ignore_ascii_case("content-length") { + content_length = value.trim().parse().unwrap(); + } + } + + let body_start = header_end + 4; + let mut body = buffer[body_start..].to_vec(); + while body.len() < content_length { + let read = stream.read(&mut chunk).await.unwrap(); + assert!(read > 0, "unexpected EOF while reading request body"); + body.extend_from_slice(&chunk[..read]); + } + + CapturedRequest { + method, + path, + body: String::from_utf8(body).unwrap(), + } +} + +fn find_header_end(buffer: &[u8]) -> Option { + buffer.windows(4).position(|window| window == b"\r\n\r\n") +} + +fn request_json(request: &CapturedRequest) -> Value { + serde_json::from_str(&request.body).unwrap_or(Value::Null) +} + +fn request_summary(requests: &[CapturedRequest]) -> Vec { + requests + .iter() + .map(|request| format!("{} {}", request.method, request.path)) + .collect() +} + +fn unique_temp_dir() -> PathBuf { + let nonce = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos(); + let dir = std::env::temp_dir().join(format!("hyper-browser-simulator-device-farm-it-{nonce}")); + fs::create_dir_all(&dir).unwrap(); + dir +} + +fn status_text(status: u16) -> &'static str { + match status { + 200 => "OK", + 404 => "Not Found", + 500 => "Internal Server Error", + _ => "OK", + } +} diff --git a/cloudflare-browser-simulator b/cloudflare-browser-simulator new file mode 160000 index 0000000..888c36a --- /dev/null +++ b/cloudflare-browser-simulator @@ -0,0 +1 @@ +Subproject commit 888c36acb64277f78b116ede29e7ce005dea9573 diff --git a/cloudflare-worker-client/build.rs b/cloudflare-worker-client/build.rs index 186ca9f..905b138 100644 --- a/cloudflare-worker-client/build.rs +++ b/cloudflare-worker-client/build.rs @@ -6,13 +6,17 @@ use std::{ fn main() { let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("missing CARGO_MANIFEST_DIR")); - let spec_path = manifest_dir.join("openapi/cloudflare-browser-simulator.json"); + let spec_path = manifest_dir.join("../cloudflare-browser-simulator/cli/openapi/cloudflare-browser-simulator.json"); println!("cargo:rerun-if-changed={}", spec_path.display()); - let spec = - serde_json::from_slice::(&fs::read(&spec_path).expect("failed to read OpenAPI spec")) - .expect("failed to parse OpenAPI spec"); + let spec_bytes = fs::read(&spec_path).unwrap_or_else(|error| { + panic!( + "failed to read OpenAPI spec at {}: {error}. Initialize the cloudflare-browser-simulator submodule with `git submodule update --init --recursive`.", + spec_path.display() + ) + }); + let spec = serde_json::from_slice::(&spec_bytes).expect("failed to parse OpenAPI spec"); let mut generator = progenitor::Generator::default(); let tokens = generator diff --git a/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json b/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json deleted file mode 100644 index b52fe5a..0000000 --- a/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json +++ /dev/null @@ -1,1326 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "cloudflare-browser-simulator", - "version": "0.3.0", - "description": "Cloudflare Worker that launches Browser Rendering sessions, joins Hyper rooms, and exposes its API contract from code." - }, - "components": { - "schemas": { - "HealthzResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - } - }, - "required": [ - "ok" - ] - }, - "SessionsResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "summary": { - "$ref": "#/components/schemas/SessionSummary" - }, - "sessions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Session" - } - } - }, - "required": [ - "ok", - "summary", - "sessions" - ] - }, - "SessionSummary": { - "type": "object", - "properties": { - "openSessions": { - "type": "number" - }, - "connectedSessions": { - "type": "number" - }, - "idleSessions": { - "type": "number" - }, - "activeSessions": { - "type": "number" - }, - "maxConcurrentSessions": { - "type": "number" - }, - "allowedBrowserAcquisitions": { - "type": "number" - }, - "timeUntilNextAllowedBrowserAcquisition": { - "type": "number" - } - }, - "required": [ - "openSessions", - "connectedSessions", - "idleSessions", - "activeSessions", - "maxConcurrentSessions", - "allowedBrowserAcquisitions", - "timeUntilNextAllowedBrowserAcquisition" - ] - }, - "Session": { - "type": "object", - "properties": { - "sessionId": { - "type": "string" - }, - "startTime": { - "type": "number" - }, - "connectionId": { - "type": "string" - }, - "connectionStartTime": { - "type": "number" - }, - "isActive": { - "type": "boolean" - } - }, - "required": [ - "sessionId", - "startTime", - "isActive" - ] - }, - "LimitsResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "limits": { - "$ref": "#/components/schemas/Limits" - }, - "docs": { - "$ref": "#/components/schemas/LimitsDocs" - } - }, - "required": [ - "ok", - "limits", - "docs" - ] - }, - "Limits": { - "type": "object", - "properties": { - "activeSessions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ActiveSessionRef" - } - }, - "maxConcurrentSessions": { - "type": "number" - }, - "allowedBrowserAcquisitions": { - "type": "number" - }, - "timeUntilNextAllowedBrowserAcquisition": { - "type": "number" - } - }, - "required": [ - "activeSessions", - "maxConcurrentSessions", - "allowedBrowserAcquisitions", - "timeUntilNextAllowedBrowserAcquisition" - ] - }, - "ActiveSessionRef": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - "LimitsDocs": { - "type": "object", - "properties": { - "activeSessions": { - "type": "string" - }, - "maxConcurrentSessions": { - "type": "string" - }, - "allowedBrowserAcquisitions": { - "type": "string" - }, - "timeUntilNextAllowedBrowserAcquisition": { - "type": "string" - } - }, - "required": [ - "activeSessions", - "maxConcurrentSessions", - "allowedBrowserAcquisitions", - "timeUntilNextAllowedBrowserAcquisition" - ] - }, - "SessionCreateResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "sessionId": { - "type": "string" - }, - "state": { - "$ref": "#/components/schemas/ParticipantState" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - } - }, - "required": [ - "ok", - "sessionId", - "state", - "log" - ] - }, - "ParticipantState": { - "type": "object", - "properties": { - "running": { - "type": "boolean" - }, - "joined": { - "type": "boolean" - }, - "muted": { - "type": "boolean" - }, - "videoActivated": { - "type": "boolean" - }, - "screenshareActivated": { - "type": "boolean" - }, - "autoGainControl": { - "type": "boolean" - }, - "noiseSuppression": { - "type": "string", - "enum": [ - "none", - "deepfilternet", - "rnnoise", - "iris-carthy", - "krisp-high", - "krisp-medium", - "krisp-low", - "krisp-high-with-bvc", - "krisp-medium-with-bvc", - "ai-coustics-sparrow-xxs", - "ai-coustics-sparrow-xs", - "ai-coustics-sparrow-s", - "ai-coustics-sparrow-l", - "ai-coustics-sparrow-xxs-48khz", - "ai-coustics-sparrow-xs-48khz", - "ai-coustics-rook-s-48khz", - "ai-coustics-rook-l-48khz" - ] - }, - "transportMode": { - "type": "string", - "enum": [ - "webrtc", - "webtransport" - ] - }, - "webcamResolution": { - "type": "string", - "enum": [ - "auto", - "p144", - "p240", - "p360", - "p480", - "p720", - "p1080", - "p1440", - "p2160", - "p4320" - ] - }, - "backgroundBlur": { - "type": "boolean" - } - }, - "required": [ - "running", - "joined", - "muted", - "videoActivated", - "screenshareActivated", - "autoGainControl", - "noiseSuppression", - "transportMode", - "webcamResolution", - "backgroundBlur" - ] - }, - "AutomationLogEntry": { - "type": "object", - "properties": { - "at": { - "type": "string", - "format": "date-time" - }, - "step": { - "type": "string" - } - }, - "required": [ - "at", - "step" - ] - }, - "ApiErrorResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - false - ] - }, - "errors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ValidationErrorItem" - } - }, - "ok": { - "type": "boolean", - "enum": [ - false - ] - }, - "sessionId": { - "type": "string" - }, - "error": { - "type": "string" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - }, - "screenshot": { - "$ref": "#/components/schemas/Screenshot" - } - } - }, - "ValidationErrorItem": { - "type": "object", - "properties": { - "code": { - "type": "number" - }, - "message": { - "type": "string" - }, - "path": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "code", - "message", - "path" - ] - }, - "Screenshot": { - "type": "object", - "properties": { - "mimeType": { - "type": "string" - }, - "base64": { - "type": "string" - } - }, - "required": [ - "mimeType", - "base64" - ] - }, - "SessionCreateRequest": { - "type": "object", - "properties": { - "roomUrl": { - "type": "string", - "format": "uri", - "description": "Hyper room URL to join." - }, - "displayName": { - "type": "string", - "minLength": 1, - "description": "Display name shown in the room." - }, - "frontendKind": { - "type": "string", - "enum": [ - "hyper-core", - "hyper-lite" - ] - }, - "hyperSessionCookie": { - "type": "string", - "minLength": 1, - "description": "Optional Hyper Core `hyper_session` cookie value to inject before navigation." - }, - "settings": { - "$ref": "#/components/schemas/ParticipantSettings" - }, - "sessionTimeoutMs": { - "type": "number", - "description": "Desired Browser Rendering inactivity timeout (`keep_alive`) in milliseconds." - }, - "navigationTimeoutMs": { - "type": "number", - "description": "Navigation timeout in milliseconds." - }, - "selectorTimeoutMs": { - "type": "number", - "description": "Selector timeout in milliseconds." - }, - "debug": { - "type": "boolean", - "description": "Include a debug screenshot in 500 responses." - } - }, - "required": [ - "roomUrl", - "displayName", - "frontendKind", - "settings" - ] - }, - "ParticipantSettings": { - "type": "object", - "properties": { - "audioEnabled": { - "type": "boolean", - "description": "Whether the participant should join with audio enabled." - }, - "videoEnabled": { - "type": "boolean", - "description": "Whether the participant should join with video enabled." - }, - "screenshareEnabled": { - "type": "boolean", - "description": "Whether the participant should start screensharing after join." - }, - "autoGainControl": { - "type": "boolean", - "description": "Whether Hyper should automatically adjust microphone volume." - }, - "noiseSuppression": { - "type": "string", - "enum": [ - "none", - "deepfilternet", - "rnnoise", - "iris-carthy", - "krisp-high", - "krisp-medium", - "krisp-low", - "krisp-high-with-bvc", - "krisp-medium-with-bvc", - "ai-coustics-sparrow-xxs", - "ai-coustics-sparrow-xs", - "ai-coustics-sparrow-s", - "ai-coustics-sparrow-l", - "ai-coustics-sparrow-xxs-48khz", - "ai-coustics-sparrow-xs-48khz", - "ai-coustics-rook-s-48khz", - "ai-coustics-rook-l-48khz" - ] - }, - "transport": { - "type": "string", - "enum": [ - "webrtc", - "webtransport" - ] - }, - "resolution": { - "type": "string", - "enum": [ - "auto", - "p144", - "p240", - "p360", - "p480", - "p720", - "p1080", - "p1440", - "p2160", - "p4320" - ] - }, - "blur": { - "type": "boolean", - "description": "Whether background blur should be enabled." - } - }, - "required": [ - "audioEnabled", - "videoEnabled", - "screenshareEnabled", - "autoGainControl", - "noiseSuppression", - "transport", - "resolution", - "blur" - ] - }, - "SessionBatchCloseResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SessionBatchCloseResult" - } - }, - "summary": { - "$ref": "#/components/schemas/SessionBatchCloseSummary" - } - }, - "required": [ - "ok", - "results", - "summary" - ] - }, - "SessionBatchCloseResult": { - "type": "object", - "properties": { - "ok": { - "type": "boolean" - }, - "sessionId": { - "type": "string" - }, - "error": { - "type": "string" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - } - }, - "required": [ - "ok", - "sessionId", - "log" - ] - }, - "SessionBatchCloseSummary": { - "type": "object", - "properties": { - "requestedSessions": { - "type": "number" - }, - "closedSessions": { - "type": "number" - }, - "failedSessions": { - "type": "number" - } - }, - "required": [ - "requestedSessions", - "closedSessions", - "failedSessions" - ] - }, - "SessionBatchCloseRequest": { - "type": "object", - "properties": { - "sessionIds": { - "type": "array", - "items": { - "type": "string", - "minLength": 1, - "description": "Cloudflare Browser Rendering session ID." - }, - "minItems": 1, - "description": "Browser Rendering session IDs to close in a single request." - } - }, - "required": [ - "sessionIds" - ] - }, - "SessionCommandResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "sessionId": { - "type": "string" - }, - "state": { - "$ref": "#/components/schemas/ParticipantState" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - } - }, - "required": [ - "ok", - "sessionId", - "state", - "log" - ] - }, - "SessionCommandRequest": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "join" - ] - } - }, - "required": [ - "type" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "leave" - ] - } - }, - "required": [ - "type" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "toggle-audio" - ] - } - }, - "required": [ - "type" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "toggle-video" - ] - } - }, - "required": [ - "type" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "toggle-screenshare" - ] - } - }, - "required": [ - "type" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "toggle-auto-gain-control" - ] - } - }, - "required": [ - "type" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "set-noise-suppression" - ] - }, - "noiseSuppression": { - "type": "string", - "enum": [ - "none", - "deepfilternet", - "rnnoise", - "iris-carthy", - "krisp-high", - "krisp-medium", - "krisp-low", - "krisp-high-with-bvc", - "krisp-medium-with-bvc", - "ai-coustics-sparrow-xxs", - "ai-coustics-sparrow-xs", - "ai-coustics-sparrow-s", - "ai-coustics-sparrow-l", - "ai-coustics-sparrow-xxs-48khz", - "ai-coustics-sparrow-xs-48khz", - "ai-coustics-rook-s-48khz", - "ai-coustics-rook-l-48khz" - ] - } - }, - "required": [ - "type", - "noiseSuppression" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "set-webcam-resolution" - ] - }, - "webcamResolution": { - "type": "string", - "enum": [ - "auto", - "p144", - "p240", - "p360", - "p480", - "p720", - "p1080", - "p1440", - "p2160", - "p4320" - ] - } - }, - "required": [ - "type", - "webcamResolution" - ] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "toggle-background-blur" - ] - } - }, - "required": [ - "type" - ] - } - ] - }, - "SessionStateResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "sessionId": { - "type": "string" - }, - "state": { - "$ref": "#/components/schemas/ParticipantState" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - } - }, - "required": [ - "ok", - "sessionId", - "state" - ] - }, - "SessionKeepAliveResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "sessionId": { - "type": "string" - }, - "state": { - "$ref": "#/components/schemas/ParticipantState" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - } - }, - "required": [ - "ok", - "sessionId", - "state", - "log" - ] - }, - "SessionCloseResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ] - }, - "sessionId": { - "type": "string" - }, - "log": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AutomationLogEntry" - } - } - }, - "required": [ - "ok", - "sessionId", - "log" - ] - } - }, - "parameters": {} - }, - "paths": { - "": { - "get": { - "operationId": "redirectToDocs", - "tags": [ - "meta" - ], - "summary": "Redirect to the interactive API docs", - "responses": { - "302": { - "description": "Redirects to /docs." - } - } - } - }, - "/healthz": { - "get": { - "operationId": "getHealthz", - "tags": [ - "meta" - ], - "summary": "Check worker health", - "responses": { - "200": { - "description": "Simple worker health response.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HealthzResponse" - } - } - } - } - } - } - }, - "/sessions": { - "get": { - "operationId": "listSessions", - "tags": [ - "sessions" - ], - "summary": "List active browser rendering sessions", - "responses": { - "200": { - "description": "Active Cloudflare Browser Rendering sessions and a summarized count.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionsResponse" - } - } - } - } - } - }, - "post": { - "operationId": "createSession", - "tags": [ - "sessions" - ], - "summary": "Create a participant browser session", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionCreateRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Participant session created.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionCreateResponse" - } - } - } - }, - "400": { - "description": "Input validation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "500": { - "description": "Session creation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "501": { - "description": "Requested create-session behavior is not implemented yet.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - } - } - } - }, - "/limits": { - "get": { - "operationId": "getLimits", - "tags": [ - "sessions" - ], - "summary": "Show browser rendering capacity and rate limits", - "responses": { - "200": { - "description": "Current browser rendering limits and field-level notes.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LimitsResponse" - } - } - } - } - } - } - }, - "/sessions/close": { - "post": { - "operationId": "closeSessions", - "tags": [ - "sessions" - ], - "summary": "Close multiple participant browser sessions", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionBatchCloseRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Participant sessions closed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionBatchCloseResponse" - } - } - } - }, - "400": { - "description": "Input validation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "500": { - "description": "Participant session close handler failed before processing results.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - } - } - } - }, - "/sessions/{sessionId}/commands": { - "post": { - "operationId": "commandSession", - "tags": [ - "sessions" - ], - "summary": "Apply a runtime participant command", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Cloudflare Browser Rendering session ID." - }, - "required": true, - "description": "Cloudflare Browser Rendering session ID.", - "name": "sessionId", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionCommandRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Participant command applied.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionCommandResponse" - } - } - } - }, - "400": { - "description": "Input validation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "500": { - "description": "Participant command failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "501": { - "description": "Participant commands are not implemented yet.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - } - } - } - }, - "/sessions/{sessionId}/state": { - "get": { - "operationId": "getSessionState", - "tags": [ - "sessions" - ], - "summary": "Fetch the latest participant state", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Cloudflare Browser Rendering session ID." - }, - "required": true, - "description": "Cloudflare Browser Rendering session ID.", - "name": "sessionId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Participant state fetched.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionStateResponse" - } - } - } - }, - "400": { - "description": "Input validation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "500": { - "description": "Participant state lookup failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "501": { - "description": "Participant state scraping is not implemented yet.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - } - } - } - }, - "/sessions/{sessionId}/keep-alive": { - "post": { - "operationId": "keepAliveSession", - "tags": [ - "sessions" - ], - "summary": "Send a keep-alive command to a participant browser session", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Cloudflare Browser Rendering session ID." - }, - "required": true, - "description": "Cloudflare Browser Rendering session ID.", - "name": "sessionId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Keep-alive command sent and participant state refreshed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionKeepAliveResponse" - } - } - } - }, - "400": { - "description": "Input validation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "500": { - "description": "Keep-alive failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - } - } - } - }, - "/sessions/{sessionId}/close": { - "post": { - "operationId": "closeSession", - "tags": [ - "sessions" - ], - "summary": "Close a participant browser session", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Cloudflare Browser Rendering session ID." - }, - "required": true, - "description": "Cloudflare Browser Rendering session ID.", - "name": "sessionId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Participant session closed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SessionCloseResponse" - } - } - } - }, - "400": { - "description": "Input validation failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - }, - "500": { - "description": "Participant session close failed.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApiErrorResponse" - } - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/cloudflare-worker-client/src/client.rs b/cloudflare-worker-client/src/client.rs index 5973d4d..5377a30 100644 --- a/cloudflare-worker-client/src/client.rs +++ b/cloudflare-worker-client/src/client.rs @@ -220,8 +220,9 @@ mod tests { use crate::generated::types::{ ParticipantSettings, ParticipantSettingsNoiseSuppression, - ParticipantSettingsResolution, ParticipantSettingsTransport, + ParticipantSettingsVideoConstraintPublishWebcam, + ParticipantSettingsVideoConstraintSubscribe, SessionCreateRequest, SessionCreateRequestDisplayName, SessionCreateRequestFrontendKind, @@ -269,7 +270,7 @@ mod tests { #[tokio::test] async fn translates_schema_mismatches_into_rebuild_hints() { let response = concat!( - r#"{"ok":true,"sessionId":"cf-session-123","state":{"running":true,"joined":true,"muted":false,"videoActivated":true,"screenshareActivated":false,"noiseSuppression":"future-noise-model","transportMode":"webrtc","webcamResolution":"auto","backgroundBlur":false},"log":[]}"# + r#"{"ok":true,"sessionId":"cf-session-123","state":{"running":true,"joined":true,"muted":false,"videoActivated":true,"screenshareActivated":false,"noiseSuppression":"future-noise-model","transportMode":"webrtc","videoConstraintPublishWebcam":"none","videoConstraintSubscribe":"none","videoMaxConcurrentTracks":null,"backgroundBlur":false},"log":[]}"# ); let (base_url, _request_task) = spawn_json_server(200, response).await; let client = CloudflareWorkerClient::new(&base_url, Duration::from_secs(5)).unwrap(); @@ -300,9 +301,11 @@ mod tests { auto_gain_control: true, blur: false, noise_suppression: ParticipantSettingsNoiseSuppression::None, - resolution: ParticipantSettingsResolution::Auto, screenshare_enabled: false, transport: ParticipantSettingsTransport::Webrtc, + video_constraint_publish_webcam: ParticipantSettingsVideoConstraintPublishWebcam::None, + video_constraint_subscribe: ParticipantSettingsVideoConstraintSubscribe::None, + video_max_concurrent_tracks: None, video_enabled: true, }, } diff --git a/config/Cargo.toml b/config/Cargo.toml index eb18a7c..fbcee7e 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -22,5 +22,8 @@ tracing.workspace = true url.workspace = true yaml_serde.workspace = true +[dev-dependencies] +serde_json.workspace = true + [lints] workspace = true diff --git a/config/src/args.rs b/config/src/args.rs index 85dc523..b4f9bd5 100644 --- a/config/src/args.rs +++ b/config/src/args.rs @@ -1,10 +1,6 @@ /// Client Simulator TUI #[derive(clap::Args, Default, Debug, Clone)] pub struct TuiArgs { - /// Verbosity level (set from parent command) - #[clap(skip)] - pub debug: u8, - /// Optional URL to override the stored configuration. #[clap(long, value_name = "URL")] pub url: Option, @@ -45,9 +41,6 @@ mod config_ext { fn collect(&self) -> Result, config::ConfigError> { let mut cache = HashMap::::new(); - if self.debug > 0 { - cache.insert("debug".to_string(), (self.debug as i64).into()); - } if let Some(url) = &self.url { cache.insert("url".to_string(), url.clone().into()); } diff --git a/config/src/client_config.rs b/config/src/client_config.rs index 9544125..6930110 100644 --- a/config/src/client_config.rs +++ b/config/src/client_config.rs @@ -17,21 +17,79 @@ pub enum TransportMode { WebRTC, } -#[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, Serialize, Deserialize, PartialEq, PartialOrd)] -pub enum WebcamResolution { +#[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, Serialize, Deserialize, PartialEq, Eq)] +pub enum VideoConstraint { #[default] - #[strum(to_string = "auto")] - #[serde(rename = "auto")] - Auto, + #[strum(to_string = "none")] + #[serde(rename = "none")] + None, + #[strum(to_string = "90p")] + #[serde(rename = "90p")] + P90, + #[strum(to_string = "144p")] + #[serde(rename = "144p")] P144, + #[strum(to_string = "240p")] + #[serde(rename = "240p")] P240, + #[strum(to_string = "360p")] + #[serde(rename = "360p")] P360, + #[strum(to_string = "480p")] + #[serde(rename = "480p")] P480, + #[strum(to_string = "720p")] + #[serde(rename = "720p")] P720, + #[strum(to_string = "1080p")] + #[serde(rename = "1080p")] P1080, + #[strum(to_string = "1440p")] + #[serde(rename = "1440p")] P1440, + #[strum(to_string = "2160p")] + #[serde(rename = "2160p")] P2160, - P4320, +} + +#[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, PartialEq, Eq)] +pub enum VideoMaxConcurrentTracksPreset { + #[default] + #[strum(to_string = "unlimited")] + Unlimited, + #[strum(to_string = "1")] + One, + #[strum(to_string = "2")] + Two, + #[strum(to_string = "3")] + Three, + #[strum(to_string = "4")] + Four, +} + +impl VideoMaxConcurrentTracksPreset { + pub const fn to_option(self) -> Option { + match self { + Self::Unlimited => None, + Self::One => Some(1), + Self::Two => Some(2), + Self::Three => Some(3), + Self::Four => Some(4), + } + } + + pub const fn from_option(value: Option) -> Self { + match value { + None => Self::Unlimited, + Some(1) => Self::One, + Some(2) => Self::Two, + Some(3) => Self::Three, + Some(4) => Self::Four, + // Off-list values (e.g. set via config/CLI) have no preset; the TUI + // selector falls back to Unlimited, while the table shows the real value. + Some(_) => Self::Unlimited, + } + } } #[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, Serialize, Deserialize, PartialEq, PartialOrd)] @@ -98,6 +156,7 @@ pub enum ParticipantBackendKind { Local, Cloudflare, RemoteStub, + AwsDeviceFarm, } impl ParticipantBackendKind { @@ -105,3 +164,71 @@ impl ParticipantBackendKind { matches!(self, Self::Local) } } + +#[cfg(test)] +mod tests { + use super::{ + ParticipantBackendKind, + VideoConstraint, + VideoMaxConcurrentTracksPreset, + }; + use std::str::FromStr as _; + + #[test] + fn video_constraint_round_trips_hyper_media_setting_names() { + let values = [ + ("none", VideoConstraint::None), + ("90p", VideoConstraint::P90), + ("144p", VideoConstraint::P144), + ("240p", VideoConstraint::P240), + ("360p", VideoConstraint::P360), + ("480p", VideoConstraint::P480), + ("720p", VideoConstraint::P720), + ("1080p", VideoConstraint::P1080), + ("1440p", VideoConstraint::P1440), + ("2160p", VideoConstraint::P2160), + ]; + + for (raw, expected) in values { + assert_eq!(VideoConstraint::from_str(raw).unwrap(), expected); + assert_eq!(expected.to_string(), raw); + assert_eq!(serde_json::to_value(expected).unwrap(), raw); + assert_eq!(serde_json::from_value::(raw.into()).unwrap(), expected); + } + } + + #[test] + fn video_track_limit_presets_convert_to_nullable_track_counts() { + assert_eq!(VideoMaxConcurrentTracksPreset::Unlimited.to_option(), None); + assert_eq!(VideoMaxConcurrentTracksPreset::One.to_option(), Some(1)); + assert_eq!(VideoMaxConcurrentTracksPreset::Two.to_option(), Some(2)); + assert_eq!(VideoMaxConcurrentTracksPreset::Three.to_option(), Some(3)); + assert_eq!(VideoMaxConcurrentTracksPreset::Four.to_option(), Some(4)); + assert_eq!( + VideoMaxConcurrentTracksPreset::from_option(None), + VideoMaxConcurrentTracksPreset::Unlimited + ); + assert_eq!( + VideoMaxConcurrentTracksPreset::from_option(Some(1)), + VideoMaxConcurrentTracksPreset::One + ); + assert_eq!( + VideoMaxConcurrentTracksPreset::from_option(Some(4)), + VideoMaxConcurrentTracksPreset::Four + ); + // Off-list live values fall back to Unlimited for TUI preselection only; + // the participants table displays the real number separately. + assert_eq!( + VideoMaxConcurrentTracksPreset::from_option(Some(8)), + VideoMaxConcurrentTracksPreset::Unlimited + ); + } + + #[test] + fn aws_device_farm_round_trips_kebab_case() { + let kind = ParticipantBackendKind::from_str("aws-device-farm").unwrap(); + assert_eq!(kind, ParticipantBackendKind::AwsDeviceFarm); + assert_eq!(kind.to_string(), "aws-device-farm"); + assert!(!kind.is_local()); + } +} diff --git a/config/src/default-config.yaml b/config/src/default-config.yaml index 0ea0fb8..a6bfcce 100644 --- a/config/src/default-config.yaml +++ b/config/src/default-config.yaml @@ -33,10 +33,20 @@ cloudflare: selector_timeout_ms: 20000 debug: false health_poll_interval_ms: 5000 +device_farm: + project_arn: '' + region: us-west-2 + url_expires_seconds: 300 + session_max_duration_ms: 1800000 + idle_timeout_ms: 180000 + health_poll_interval_ms: 30000 + debug: false audio_enabled: true video_enabled: true auto_gain_control: true noise_suppression: none transport: webtransport -resolution: auto +video_constraint_publish_webcam: none +video_constraint_subscribe: none +video_max_concurrent_tracks: blur: false diff --git a/config/src/device_farm_config.rs b/config/src/device_farm_config.rs new file mode 100644 index 0000000..2154995 --- /dev/null +++ b/config/src/device_farm_config.rs @@ -0,0 +1,64 @@ +use serde::{ + Deserialize, + Serialize, +}; + +pub const DEVICE_FARM_AWS_ACCESS_KEY_ID: &str = env!("DEVICE_FARM_AWS_ACCESS_KEY_ID"); +pub const DEVICE_FARM_AWS_SECRET_ACCESS_KEY: &str = env!("DEVICE_FARM_AWS_SECRET_ACCESS_KEY"); +pub const DEVICE_FARM_AWS_REGION: &str = env!("DEVICE_FARM_AWS_REGION"); +pub const DEVICE_FARM_PROJECT_ARN: &str = env!("DEVICE_FARM_PROJECT_ARN"); + +/// Configuration for the AWS Device Farm desktop-browser ("Test Grid") backend. +/// +/// Credentials are embedded at compile time. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct DeviceFarmConfig { + /// ARN of the Device Farm Test Grid project (from Terraform output). + pub project_arn: String, + /// AWS region; Device Farm Test Grid is only available in us-west-2. + pub region: String, + /// Lifetime requested for the generated Selenium connection URL. + pub url_expires_seconds: u64, + /// `aws:maxDurationSecs` capability - hard cap on total session length. + pub session_max_duration_ms: u64, + /// `aws:idleTimeoutSecs` capability - max gap between WebDriver commands. + pub idle_timeout_ms: u64, + /// How often the keep-alive poller pings the live session. + pub health_poll_interval_ms: u64, + pub debug: bool, +} + +impl DeviceFarmConfig { + pub fn is_default(&self) -> bool { + self == &Self::default() + } +} + +impl Default for DeviceFarmConfig { + fn default() -> Self { + Self { + project_arn: DEVICE_FARM_PROJECT_ARN.to_owned(), + region: DEVICE_FARM_AWS_REGION.to_owned(), + url_expires_seconds: 300, + session_max_duration_ms: 1_800_000, + idle_timeout_ms: 180_000, + health_poll_interval_ms: 30_000, + debug: false, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_targets_us_west_2_and_is_default() { + let config = DeviceFarmConfig::default(); + assert_eq!(config.region, DEVICE_FARM_AWS_REGION); + assert_eq!(config.project_arn, DEVICE_FARM_PROJECT_ARN); + assert!(config.is_default()); + // Poll interval must stay safely inside the idle timeout window. + assert!(config.health_poll_interval_ms < config.idle_timeout_ms); + } +} diff --git a/config/src/lib.rs b/config/src/lib.rs index 2d7fef7..c672444 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -6,6 +6,7 @@ mod args; mod browser_config; mod client_config; mod cloudflare_config; +mod device_farm_config; pub mod media; mod participant_config; @@ -26,11 +27,20 @@ pub use client_config::{ ParticipantBackendKind, TransportMode, TransportModeIter, - WebcamResolution, - WebcamResolutionIter, + VideoConstraint, + VideoConstraintIter, + VideoMaxConcurrentTracksPreset, + VideoMaxConcurrentTracksPresetIter, }; pub use cloudflare_config::CloudflareConfig; use color_eyre::Result; +pub use device_farm_config::{ + DeviceFarmConfig, + DEVICE_FARM_AWS_ACCESS_KEY_ID, + DEVICE_FARM_AWS_REGION, + DEVICE_FARM_AWS_SECRET_ACCESS_KEY, + DEVICE_FARM_PROJECT_ARN, +}; use eyre::Context as _; pub use participant_config::{ generate_random_name, @@ -61,6 +71,8 @@ pub struct Config { pub backend: ParticipantBackendKind, #[serde(default, skip_serializing_if = "CloudflareConfig::is_default")] pub cloudflare: CloudflareConfig, + #[serde(default, skip_serializing_if = "DeviceFarmConfig::is_default")] + pub device_farm: DeviceFarmConfig, #[serde(default)] pub audio_enabled: bool, #[serde(default)] @@ -74,7 +86,11 @@ pub struct Config { #[serde(default)] pub transport: TransportMode, #[serde(default)] - pub resolution: WebcamResolution, + pub video_constraint_publish_webcam: VideoConstraint, + #[serde(default)] + pub video_constraint_subscribe: VideoConstraint, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub video_max_concurrent_tracks: Option, #[serde(default)] pub blur: bool, } @@ -87,7 +103,10 @@ const fn default_auto_gain_control() -> bool { impl Default for Config { fn default() -> Self { - yaml_serde::from_str(DEFAULT_CONFIG).expect("Failed to parse default config") + let mut config: Self = yaml_serde::from_str(DEFAULT_CONFIG).expect("Failed to parse default config"); + config.device_farm.project_arn = DEVICE_FARM_PROJECT_ARN.to_owned(); + config.device_farm.region = DEVICE_FARM_AWS_REGION.to_owned(); + config } } @@ -129,6 +148,30 @@ impl config::Source for Config { .into(), ); } + if !self.device_farm.is_default() { + cache.insert( + "device_farm".to_string(), + config::ValueKind::Table(HashMap::from_iter([ + ("project_arn".to_string(), self.device_farm.project_arn.clone().into()), + ("region".to_string(), self.device_farm.region.clone().into()), + ( + "url_expires_seconds".to_string(), + self.device_farm.url_expires_seconds.into(), + ), + ( + "session_max_duration_ms".to_string(), + self.device_farm.session_max_duration_ms.into(), + ), + ("idle_timeout_ms".to_string(), self.device_farm.idle_timeout_ms.into()), + ( + "health_poll_interval_ms".to_string(), + self.device_farm.health_poll_interval_ms.into(), + ), + ("debug".to_string(), self.device_farm.debug.into()), + ])) + .into(), + ); + } if let Some(url) = &self.url { cache.insert("url".to_string(), url.to_string().into()); } @@ -142,7 +185,17 @@ impl config::Source for Config { self.noise_suppression.to_string().into(), ); cache.insert("transport".to_string(), self.transport.to_string().into()); - cache.insert("resolution".to_string(), self.resolution.to_string().into()); + cache.insert( + "video_constraint_publish_webcam".to_string(), + self.video_constraint_publish_webcam.to_string().into(), + ); + cache.insert( + "video_constraint_subscribe".to_string(), + self.video_constraint_subscribe.to_string().into(), + ); + if let Some(value) = self.video_max_concurrent_tracks { + cache.insert("video_max_concurrent_tracks".to_string(), (value as i64).into()); + } cache.insert("blur".to_string(), self.blur.into()); if let Some(value) = self.fake_media_selected { cache.insert("fake_media_selected".to_string(), (value as u64).into()); @@ -345,4 +398,80 @@ cloudflare: assert!(config.cloudflare.debug); assert_eq!(config.cloudflare.health_poll_interval_ms, 2_000); } + + #[test] + fn parses_aws_device_farm_backend_and_nested_device_farm_config() { + let config: Config = config::Config::builder() + .add_source(Config::default()) + .add_source(config::File::from_str( + r#" +backend: aws-device-farm +device_farm: + project_arn: arn:aws:devicefarm:us-west-2:123456789012:testgrid-project:abc + region: us-west-2 + url_expires_seconds: 300 + session_max_duration_ms: 1800000 + idle_timeout_ms: 180000 + health_poll_interval_ms: 30000 + debug: true +"#, + config::FileFormat::Yaml, + )) + .build() + .expect("failed to build config") + .try_deserialize() + .expect("failed to deserialize config"); + + assert_eq!(config.backend, ParticipantBackendKind::AwsDeviceFarm); + assert_eq!( + config.device_farm.project_arn, + "arn:aws:devicefarm:us-west-2:123456789012:testgrid-project:abc" + ); + assert_eq!(config.device_farm.region, "us-west-2"); + assert!(config.device_farm.debug); + } + + #[test] + fn default_device_farm_config_omits_unused_timeouts() { + let defaults = include_str!("default-config.yaml"); + let device_farm = defaults + .split_once("device_farm:\n") + .and_then(|(_, rest)| rest.split_once("\naudio_enabled:")) + .map(|(device_farm, _)| device_farm) + .expect("default config should contain a device_farm section"); + + assert!(!device_farm.contains("navigation_timeout_ms")); + assert!(!device_farm.contains("selector_timeout_ms")); + } + + #[test] + fn parses_new_video_constraint_settings() { + let config: Config = config::Config::builder() + .add_source(Config::default()) + .add_source(config::File::from_str( + r#" +video_constraint_publish_webcam: 480p +video_constraint_subscribe: 720p +video_max_concurrent_tracks: 2 +"#, + config::FileFormat::Yaml, + )) + .build() + .expect("failed to build config") + .try_deserialize() + .expect("failed to deserialize config"); + + assert_eq!(config.video_constraint_publish_webcam, VideoConstraint::P480); + assert_eq!(config.video_constraint_subscribe, VideoConstraint::P720); + assert_eq!(config.video_max_concurrent_tracks, Some(2)); + } + + #[test] + fn default_video_max_concurrent_tracks_is_unlimited() { + let config = Config::default(); + + assert_eq!(config.video_constraint_publish_webcam, VideoConstraint::None); + assert_eq!(config.video_constraint_subscribe, VideoConstraint::None); + assert_eq!(config.video_max_concurrent_tracks, None); + } } diff --git a/config/src/participant_config.rs b/config/src/participant_config.rs index 7985559..99fa682 100644 --- a/config/src/participant_config.rs +++ b/config/src/participant_config.rs @@ -20,7 +20,7 @@ impl ParticipantConfig { let name = if let Some(name) = name { name.to_string() } else { - generate_random_name() + format!("{}{}", backend_name_prefix(config.backend), generate_random_name()) }; let url = config.url.clone().ok_or_eyre("No session URL provided")?; Ok(Self { @@ -41,3 +41,57 @@ pub fn generate_random_name() -> String { let mut generator = Generator::with_naming(Name::Numbered); generator.next().unwrap() } + +const fn backend_name_prefix(backend: super::ParticipantBackendKind) -> &'static str { + match backend { + super::ParticipantBackendKind::Local => "local-", + super::ParticipantBackendKind::RemoteStub => "stub-", + super::ParticipantBackendKind::Cloudflare => "cf-", + super::ParticipantBackendKind::AwsDeviceFarm => "aws-", + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ParticipantBackendKind; + + fn config_with_backend(backend: ParticipantBackendKind) -> super::super::Config { + super::super::Config { + url: Some(Url::parse("https://example.com/space/test").expect("valid URL")), + backend, + ..Default::default() + } + } + + #[test] + fn generated_names_are_prefixed_by_backend() { + let cases = [ + (ParticipantBackendKind::Local, "local-"), + (ParticipantBackendKind::RemoteStub, "stub-"), + (ParticipantBackendKind::Cloudflare, "cf-"), + (ParticipantBackendKind::AwsDeviceFarm, "aws-"), + ]; + + for (backend, prefix) in cases { + let config = config_with_backend(backend); + let participant = ParticipantConfig::new(&config, None::).expect("participant config"); + + assert!( + participant.username.starts_with(prefix), + "expected {:?} generated name {:?} to start with {:?}", + backend, + participant.username, + prefix + ); + } + } + + #[test] + fn provided_names_are_not_prefixed() { + let config = config_with_backend(ParticipantBackendKind::Cloudflare); + let participant = ParticipantConfig::new(&config, Some("Ada")).expect("participant config"); + + assert_eq!(participant.username, "Ada"); + } +} diff --git a/dist-workspace.toml b/dist-workspace.toml index 4364451..b221357 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -5,6 +5,9 @@ members = ["cargo:."] [dist] # The preferred dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.30.4" +# Keep the generated workflow hand-maintained for private submodule checkout and +# Device Farm build-time secrets. +allow-dirty = ["ci"] # CI backends to support ci = "github" # The installers to generate for each app diff --git a/flake.lock b/flake.lock index 088a973..96f37ec 100644 --- a/flake.lock +++ b/flake.lock @@ -20,16 +20,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1774273680, - "narHash": "sha256-a++tZ1RQsDb1I0NHrFwdGuRlR5TORvCEUksM459wKUA=", + "lastModified": 1780243769, + "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fdc7b8f7b30fdbedec91b71ed82f36e1637483ed", + "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-unstable", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 63b33bc..8c6af5a 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; diff --git a/justfile b/justfile index 8aa59b4..7e636c1 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,10 @@ default: just --list +setup: + git submodule update --init --recursive + cat .env.tpl | op inject > .env + # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- run *flags="": diff --git a/src/aws.rs b/src/aws.rs new file mode 100644 index 0000000..73d4bcd --- /dev/null +++ b/src/aws.rs @@ -0,0 +1,571 @@ +use clap::{ + Args, + Subcommand, +}; +use client_simulator_browser::participant::device_farm::{ + self, + AwsTestGrid, + DeviceFarmCloseResult, + DeviceFarmSessionInfo, +}; +use client_simulator_config::{ + Config, + TuiArgs, +}; +use eyre::{ + bail, + Context as _, + Result, +}; +use serde::Serialize; +use tracing_subscriber::{ + fmt, + prelude::*, + registry, + EnvFilter, +}; + +const DEVICE_FARM_DESKTOP_INSTANCE_MINUTE_USD: f64 = 0.005; + +#[derive(Args, Debug, Clone)] +pub struct AwsArgs { + #[command(subcommand)] + pub command: AwsCommand, +} + +#[derive(Subcommand, Debug, Clone)] +pub enum AwsCommand { + /// List AWS Device Farm Test Grid sessions for the configured project. + ListSessions(ListSessionsArgs), + /// Force-close AWS Device Farm Test Grid sessions for the configured project. + CloseSessions(CloseSessionsArgs), +} + +#[derive(Args, Debug, Clone)] +pub struct ListSessionsArgs { + /// Print machine-readable JSON instead of a human table. + #[clap(long)] + pub json: bool, + /// Filter sessions by Device Farm status. + #[clap(long, value_enum, default_value_t = ListSessionsStatus::Active)] + pub status: ListSessionsStatus, + /// Only show sessions created at or after this local date/time, RFC3339 timestamp, or relative duration. + #[clap(long, value_name = "DATE_OR_DURATION", value_parser = parse_since_arg)] + pub since: Option, +} + +#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)] +pub enum ListSessionsStatus { + Active, + Closed, + All, +} + +impl ListSessionsStatus { + fn test_grid_status(self) -> Option { + match self { + Self::Active => Some(device_farm::TestGridSessionStatus::Active), + Self::Closed => Some(device_farm::TestGridSessionStatus::Closed), + Self::All => None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SinceDate { + epoch_seconds: i64, +} + +#[derive(Args, Debug, Clone)] +pub struct CloseSessionsArgs { + /// Comma-separated Device Farm session IDs. If absent, all active sessions are closed. + #[clap(value_name = "SESSION_IDS", value_delimiter = ',')] + pub session_ids: Vec, +} + +pub async fn run(args: AwsArgs, filter: EnvFilter) -> Result<()> { + init_logging(filter)?; + + let config = Config::new(TuiArgs::default()).context("Failed to create config")?; + if config.device_farm.project_arn.trim().is_empty() { + bail!("device_farm.project_arn is not configured; set it in config.yaml before using `aws`"); + } + + let api = AwsTestGrid::new(&config.device_farm.region); + match args.command { + AwsCommand::ListSessions(args) => list_sessions(&api, &config.device_farm.project_arn, args).await, + AwsCommand::CloseSessions(args) => close_sessions(&api, &config, args).await, + } +} + +async fn list_sessions(api: &AwsTestGrid, project_arn: &str, args: ListSessionsArgs) -> Result<()> { + let sessions = device_farm::list_project_sessions(api, project_arn, args.status.test_grid_status()).await?; + let sessions = filter_sessions_since(sessions, args.since.as_ref()); + if args.json { + print!("{}", format_sessions_json(&sessions)?); + } else { + print!("{}", format_sessions_human(&sessions)); + } + Ok(()) +} + +async fn close_sessions(api: &AwsTestGrid, config: &Config, args: CloseSessionsArgs) -> Result<()> { + let listed_sessions = if args.session_ids.is_empty() { + device_farm::list_active_project_sessions(api, &config.device_farm.project_arn).await? + } else { + Vec::new() + }; + let targets = close_targets(&args.session_ids, &listed_sessions); + + if targets.is_empty() { + println!("No active Device Farm sessions to close."); + return Ok(()); + } + + let mut failures = Vec::new(); + for session_id in targets { + match device_farm::close_test_grid_session( + api, + &config.device_farm.project_arn, + config.device_farm.url_expires_seconds, + &session_id, + ) + .await + { + Ok(DeviceFarmCloseResult::Closed) => println!("closed {session_id}"), + Ok(DeviceFarmCloseResult::AlreadyClosed) => println!("already closed {session_id}"), + Err(err) => { + println!("failed {session_id}: {err}"); + failures.push(format!("{session_id}: {err}")); + } + } + } + + if failures.is_empty() { + Ok(()) + } else { + bail!("failed to close Device Farm sessions: {}", failures.join("; ")) + } +} + +fn init_logging(filter: EnvFilter) -> Result<()> { + registry() + .with( + fmt::layer() + .with_writer(std::io::stderr) + .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_filter(filter), + ) + .with(tracing_error::ErrorLayer::default()) + .try_init()?; + + Ok(()) +} + +fn format_sessions_json(sessions: &[DeviceFarmSessionInfo]) -> Result { + let summary = SessionListOutput::new(sessions); + let mut output = serde_json::to_string_pretty(&summary)?; + output.push('\n'); + Ok(output) +} + +fn format_sessions_human(sessions: &[DeviceFarmSessionInfo]) -> String { + if sessions.is_empty() { + return format!( + "No Device Farm sessions found.\n{}\n", + cost_summary_text(total_billing_minutes(sessions)) + ); + } + + let headers = ["ID", "STATUS", "CREATED", "AGE", "ENDED", "BILLING"]; + let rows = sessions + .iter() + .map(|session| { + [ + session.session_id.clone(), + session.status.clone(), + session.created.clone().unwrap_or_else(|| "-".to_string()), + age_text(session.age_seconds), + session.ended.clone().unwrap_or_else(|| "-".to_string()), + billing_text(session.billing_minutes), + ] + }) + .collect::>(); + let widths = (0..headers.len()) + .map(|index| { + rows.iter() + .map(|row| row[index].len()) + .max() + .unwrap_or_default() + .max(headers[index].len()) + }) + .collect::>(); + + let mut output = String::new(); + write_table_row(&mut output, &headers, &widths); + for row in &rows { + write_table_row(&mut output, row, &widths); + } + output.push_str(&cost_summary_text(total_billing_minutes(sessions))); + output.push('\n'); + output +} + +fn write_table_row(output: &mut String, columns: &[impl AsRef; N], widths: &[usize]) { + for (index, column) in columns.iter().enumerate() { + if index > 0 { + output.push_str(" "); + } + let value = column.as_ref(); + output.push_str(value); + for _ in value.len()..widths[index] { + output.push(' '); + } + } + output.push('\n'); +} + +fn age_text(seconds: Option) -> String { + let Some(seconds) = seconds.filter(|seconds| *seconds >= 0) else { + return "-".to_string(); + }; + let hours = seconds / 3600; + let minutes = (seconds % 3600) / 60; + let seconds = seconds % 60; + + if hours > 0 { + format!("{hours}h {minutes:02}m") + } else if minutes > 0 { + format!("{minutes}m {seconds:02}s") + } else { + format!("{seconds}s") + } +} + +fn billing_text(minutes: Option) -> String { + minutes.map_or_else(|| "-".to_string(), |minutes| format!("{minutes:.2}")) +} + +fn parse_since_arg(value: &str) -> std::result::Result { + parse_since_epoch_seconds(value) + .map(|epoch_seconds| SinceDate { epoch_seconds }) + .ok_or_else(|| { + "expected YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], YYYY-MM-DDTHH:MM[:SS], RFC3339 timestamp, or human duration like '1 day'".to_string() + }) +} + +fn parse_since_epoch_seconds(value: &str) -> Option { + use chrono::{ + Local, + NaiveDate, + NaiveDateTime, + TimeZone as _, + }; + + if let Ok(parsed) = chrono::DateTime::parse_from_rfc3339(value) { + return Some(parsed.timestamp()); + } + + for format in [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%dT%H:%M", + ] { + if let Ok(parsed) = NaiveDateTime::parse_from_str(value, format) { + return Local.from_local_datetime(&parsed).single().map(|date| date.timestamp()); + } + } + + if let Some(timestamp) = NaiveDate::parse_from_str(value, "%Y-%m-%d") + .ok() + .and_then(|date| date.and_hms_opt(0, 0, 0)) + .and_then(|date| Local.from_local_datetime(&date).single()) + .map(|date| date.timestamp()) + { + return Some(timestamp); + } + + let duration = humantime::parse_duration(value).ok()?; + let duration = chrono::Duration::from_std(duration).ok()?; + Local::now().checked_sub_signed(duration).map(|date| date.timestamp()) +} + +fn filter_sessions_since( + sessions: Vec, + since: Option<&SinceDate>, +) -> Vec { + let Some(since) = since else { + return sessions; + }; + + sessions + .into_iter() + .filter(|session| { + session + .created + .as_deref() + .and_then(parse_created_epoch_seconds) + .is_some_and(|created| created >= since.epoch_seconds) + }) + .collect() +} + +fn parse_created_epoch_seconds(value: &str) -> Option { + chrono::DateTime::parse_from_rfc3339(value) + .ok() + .map(|date| date.timestamp()) +} + +fn total_billing_minutes(sessions: &[DeviceFarmSessionInfo]) -> f64 { + normalize_zero(sessions.iter().filter_map(|session| session.billing_minutes).sum()) +} + +fn total_cost_usd(total_billing_minutes: f64) -> f64 { + normalize_zero(total_billing_minutes * DEVICE_FARM_DESKTOP_INSTANCE_MINUTE_USD) +} + +fn normalize_zero(value: f64) -> f64 { + if value.abs() < f64::EPSILON { + 0.0 + } else { + value + } +} + +fn cost_summary_text(total_billing_minutes: f64) -> String { + format!( + "Total cost: ${:.4} ({:.2} billing minutes at ${:.4}/min)", + total_cost_usd(total_billing_minutes), + total_billing_minutes, + DEVICE_FARM_DESKTOP_INSTANCE_MINUTE_USD + ) +} + +#[derive(Serialize)] +struct SessionListOutput<'a> { + sessions: &'a [DeviceFarmSessionInfo], + total_billing_minutes: f64, + total_cost_usd: f64, + instance_minute_usd: f64, +} + +impl<'a> SessionListOutput<'a> { + fn new(sessions: &'a [DeviceFarmSessionInfo]) -> Self { + let total_billing_minutes = total_billing_minutes(sessions); + Self { + sessions, + total_billing_minutes, + total_cost_usd: total_cost_usd(total_billing_minutes), + instance_minute_usd: DEVICE_FARM_DESKTOP_INSTANCE_MINUTE_USD, + } + } +} + +fn close_targets(requested_session_ids: &[String], sessions: &[DeviceFarmSessionInfo]) -> Vec { + let candidates = if requested_session_ids.is_empty() { + sessions + .iter() + .filter(|session| session.status == "ACTIVE") + .map(|session| session.session_id.clone()) + .collect::>() + } else { + requested_session_ids.to_vec() + }; + + let mut targets = Vec::new(); + for session_id in candidates { + if !session_id.is_empty() && !targets.contains(&session_id) { + targets.push(session_id); + } + } + targets +} + +#[cfg(test)] +mod tests { + use super::*; + use client_simulator_browser::participant::device_farm::DeviceFarmSessionInfo; + use serde_json::json; + + #[test] + fn human_output_includes_session_table_columns() { + let output = format_sessions_human(&[session_fixture("session-1", "ACTIVE")]); + + assert!(output.contains("ID")); + assert!(output.contains("STATUS")); + assert!(output.contains("CREATED")); + assert!(output.contains("AGE")); + assert!(output.contains("BILLING")); + assert!(output.contains("session-1")); + assert!(output.contains("ACTIVE")); + assert!(output.contains("12m 34s")); + } + + #[test] + fn json_output_includes_sessions_and_total_cost() { + let output = format_sessions_json(&[session_fixture_with_billing("session-1", "ACTIVE", 12.5)]).unwrap(); + let value: serde_json::Value = serde_json::from_str(&output).unwrap(); + + assert_eq!( + value["sessions"], + json!([session_fixture_with_billing("session-1", "ACTIVE", 12.5)]) + ); + assert_eq!(value["total_billing_minutes"], json!(12.5)); + assert_eq!(value["total_cost_usd"], json!(0.0625)); + assert_eq!(value["instance_minute_usd"], json!(0.005)); + } + + #[test] + fn empty_list_output_is_explicit() { + assert_eq!( + format_sessions_human(&[]), + "No Device Farm sessions found.\nTotal cost: $0.0000 (0.00 billing minutes at $0.0050/min)\n" + ); + let value: serde_json::Value = serde_json::from_str(&format_sessions_json(&[]).unwrap()).unwrap(); + assert_eq!(value["sessions"], json!([])); + assert_eq!(value["total_cost_usd"], json!(0.0)); + } + + #[test] + fn human_output_includes_total_cost_for_shown_sessions() { + let output = format_sessions_human(&[ + session_fixture_with_billing("session-1", "CLOSED", 10.0), + session_fixture_with_billing("session-2", "CLOSED", 20.5), + ]); + + assert!(output.contains("Total cost: $0.1525 (30.50 billing minutes at $0.0050/min)")); + } + + #[test] + fn list_status_maps_to_aws_status_filter() { + assert_eq!( + ListSessionsStatus::Active.test_grid_status(), + Some(device_farm::TestGridSessionStatus::Active) + ); + assert_eq!( + ListSessionsStatus::Closed.test_grid_status(), + Some(device_farm::TestGridSessionStatus::Closed) + ); + assert_eq!(ListSessionsStatus::All.test_grid_status(), None); + } + + #[test] + fn parse_since_accepts_date_and_optional_time() { + assert!(parse_since_arg("2026-06-04").is_ok()); + assert!(parse_since_arg("2026-06-04 10:11").is_ok()); + assert!(parse_since_arg("2026-06-04T10:11:12").is_ok()); + assert!(parse_since_arg("2026-06-04T10:11:12Z").is_ok()); + } + + #[test] + fn parse_since_accepts_human_duration_relative_to_now() { + let before = chrono::Local::now().timestamp(); + let since = parse_since_arg("1 day").unwrap(); + let after = chrono::Local::now().timestamp(); + + assert!(since.epoch_seconds >= before - 86_400); + assert!(since.epoch_seconds <= after - 86_400); + } + + #[test] + fn parse_since_rejects_unknown_text() { + assert!(parse_since_arg("last tuesday").is_err()); + } + + #[test] + fn since_filter_keeps_sessions_created_at_or_after_threshold() { + let since = parse_since_arg("2026-06-04T10:11:12Z").unwrap(); + let sessions = vec![ + session_fixture_with_created("before", "CLOSED", "2026-06-04T10:11:11Z"), + session_fixture_with_created("at", "CLOSED", "2026-06-04T10:11:12Z"), + session_fixture_with_created("after", "CLOSED", "2026-06-04T10:11:13Z"), + DeviceFarmSessionInfo { + created: None, + ..session_fixture("missing-created", "CLOSED") + }, + ]; + + let filtered = filter_sessions_since(sessions, Some(&since)); + let ids = filtered + .iter() + .map(|session| session.session_id.as_str()) + .collect::>(); + + assert_eq!(ids, vec!["at", "after"]); + } + + #[test] + fn provided_close_ids_target_exactly_those_ids() { + let targets = close_targets( + &["a".to_string(), "b".to_string()], + &[session_fixture("active-session", "ACTIVE")], + ); + + assert_eq!(targets, vec!["a", "b"]); + } + + #[test] + fn empty_close_ids_selects_all_active_sessions() { + let targets = close_targets( + &[], + &[ + session_fixture("active-1", "ACTIVE"), + session_fixture("closed-1", "CLOSED"), + session_fixture("active-2", "ACTIVE"), + ], + ); + + assert_eq!(targets, vec!["active-1", "active-2"]); + } + + #[test] + fn empty_close_ids_do_not_select_closed_or_errored_sessions() { + let targets = close_targets( + &[], + &[ + session_fixture("closed-1", "CLOSED"), + session_fixture("errored-1", "ERRORED"), + ], + ); + + assert!(targets.is_empty()); + } + + #[test] + fn close_targets_deduplicates_preserving_first_seen_order() { + let targets = close_targets( + &["b".to_string(), "a".to_string(), "b".to_string(), "c".to_string()], + &[], + ); + + assert_eq!(targets, vec!["b", "a", "c"]); + } + + fn session_fixture(session_id: &str, status: &str) -> DeviceFarmSessionInfo { + DeviceFarmSessionInfo { + arn: format!("arn:aws:devicefarm:us-west-2:123456789012:testgrid-session:project/{session_id}"), + session_id: session_id.to_string(), + status: status.to_string(), + created: Some("2026-06-04T10:11:12Z".to_string()), + ended: None, + age_seconds: Some(754), + billing_minutes: None, + selenium_properties: Some(json!({"browserName": "chrome"})), + } + } + + fn session_fixture_with_billing(session_id: &str, status: &str, billing_minutes: f64) -> DeviceFarmSessionInfo { + DeviceFarmSessionInfo { + billing_minutes: Some(billing_minutes), + ..session_fixture(session_id, status) + } + } + + fn session_fixture_with_created(session_id: &str, status: &str, created: &str) -> DeviceFarmSessionInfo { + DeviceFarmSessionInfo { + created: Some(created.to_string()), + ..session_fixture(session_id, status) + } + } +} diff --git a/src/headless.rs b/src/headless.rs new file mode 100644 index 0000000..88d8b79 --- /dev/null +++ b/src/headless.rs @@ -0,0 +1,572 @@ +use clap::Args; +use client_simulator_config::{ + Config, + NoiseSuppression, + ParticipantBackendKind, + TransportMode, + TuiArgs, + VideoConstraint, +}; +use eyre::{ + Context as _, + Result, +}; +use std::future::Future; +use tokio::sync::watch; +use tracing_subscriber::{ + fmt::{ + self, + writer::MakeWriterExt as _, + }, + prelude::*, + EnvFilter, +}; + +#[derive(Args, Debug, Clone, Default)] +pub struct HeadlessArgs { + #[clap(long, value_name = "URL")] + pub url: Option, + + #[clap(long, value_name = "BACKEND")] + pub backend: Option, + + #[clap(long = "headless", value_parser = clap::builder::BoolishValueParser::new())] + pub headless: Option, + + #[clap(long = "audio-enabled", value_parser = clap::builder::BoolishValueParser::new())] + pub audio_enabled: Option, + + #[clap(long = "video-enabled", value_parser = clap::builder::BoolishValueParser::new())] + pub video_enabled: Option, + + #[clap(long = "screenshare-enabled", value_parser = clap::builder::BoolishValueParser::new())] + pub screenshare_enabled: Option, + + #[clap(long = "auto-gain-control", value_parser = clap::builder::BoolishValueParser::new())] + pub auto_gain_control: Option, + + #[clap(long = "noise-suppression", value_name = "MODEL")] + pub noise_suppression: Option, + + #[clap(long, value_name = "MODE")] + pub transport: Option, + + #[clap(long = "video-constraint-publish-webcam", value_name = "CONSTRAINT")] + pub video_constraint_publish_webcam: Option, + + #[clap(long = "video-constraint-subscribe", value_name = "CONSTRAINT")] + pub video_constraint_subscribe: Option, + + #[clap(long = "video-max-concurrent-tracks", value_name = "TRACKS")] + pub video_max_concurrent_tracks: Option, + + #[clap(long, value_parser = clap::builder::BoolishValueParser::new())] + pub blur: Option, + + #[clap(long = "participant", value_name = "JSON")] + pub participants: Vec, +} + +#[derive(Debug, Clone, Default, serde::Deserialize)] +#[serde(default, deny_unknown_fields)] +struct ParticipantOverride { + url: Option, + backend: Option, + headless: Option, + audio_enabled: Option, + video_enabled: Option, + screenshare_enabled: Option, + auto_gain_control: Option, + noise_suppression: Option, + transport: Option, + video_constraint_publish_webcam: Option, + video_constraint_subscribe: Option, + video_max_concurrent_tracks: Option, + blur: Option, +} + +pub async fn run(args: HeadlessArgs, filter: EnvFilter) -> Result { + init_logging(filter)?; + + let mut global_config = Config::new(TuiArgs::default()).context("Failed to create config")?; + apply_cli_overrides(&mut global_config, &args); + + let participant_configs = build_participant_configs(global_config.clone(), &args.participants)?; + let store = client_simulator_browser::participant::ParticipantStore::new(global_config.data_dir()); + + spawn_participants_or_shutdown(&store, &participant_configs).await?; + + Ok(wait_for_exit(store).await) +} + +async fn spawn_participants_or_shutdown( + store: &client_simulator_browser::participant::ParticipantStore, + participant_configs: &[Config], +) -> Result<()> { + for config in participant_configs { + if let Err(err) = store.spawn(config).context("Failed to spawn participant") { + store.shutdown_all().await; + return Err(err); + } + } + + Ok(()) +} + +fn apply_cli_overrides(config: &mut Config, args: &HeadlessArgs) { + if let Some(url) = &args.url { + config.url = Some(url.clone()); + } + if let Some(backend) = args.backend { + config.backend = backend; + } + if let Some(headless) = args.headless { + config.headless = headless; + } + if let Some(audio_enabled) = args.audio_enabled { + config.audio_enabled = audio_enabled; + } + if let Some(video_enabled) = args.video_enabled { + config.video_enabled = video_enabled; + } + if let Some(screenshare_enabled) = args.screenshare_enabled { + config.screenshare_enabled = screenshare_enabled; + } + if let Some(auto_gain_control) = args.auto_gain_control { + config.auto_gain_control = auto_gain_control; + } + if let Some(noise_suppression) = args.noise_suppression { + config.noise_suppression = noise_suppression; + } + if let Some(transport) = args.transport { + config.transport = transport; + } + if let Some(value) = args.video_constraint_publish_webcam { + config.video_constraint_publish_webcam = value; + } + if let Some(value) = args.video_constraint_subscribe { + config.video_constraint_subscribe = value; + } + if let Some(value) = args.video_max_concurrent_tracks { + config.video_max_concurrent_tracks = Some(value); + } + if let Some(blur) = args.blur { + config.blur = blur; + } +} + +fn apply_participant_override(mut config: Config, override_: ParticipantOverride) -> Config { + if let Some(url) = override_.url { + config.url = Some(url); + } + if let Some(backend) = override_.backend { + config.backend = backend; + } + if let Some(headless) = override_.headless { + config.headless = headless; + } + if let Some(audio_enabled) = override_.audio_enabled { + config.audio_enabled = audio_enabled; + } + if let Some(video_enabled) = override_.video_enabled { + config.video_enabled = video_enabled; + } + if let Some(screenshare_enabled) = override_.screenshare_enabled { + config.screenshare_enabled = screenshare_enabled; + } + if let Some(auto_gain_control) = override_.auto_gain_control { + config.auto_gain_control = auto_gain_control; + } + if let Some(noise_suppression) = override_.noise_suppression { + config.noise_suppression = noise_suppression; + } + if let Some(transport) = override_.transport { + config.transport = transport; + } + if let Some(value) = override_.video_constraint_publish_webcam { + config.video_constraint_publish_webcam = value; + } + if let Some(value) = override_.video_constraint_subscribe { + config.video_constraint_subscribe = value; + } + if let Some(value) = override_.video_max_concurrent_tracks { + config.video_max_concurrent_tracks = Some(value); + } + if let Some(blur) = override_.blur { + config.blur = blur; + } + config +} + +fn build_participant_configs(global_config: Config, participants: &[String]) -> Result> { + if participants.is_empty() { + return Ok(vec![global_config]); + } + + participants + .iter() + .map(|json| { + let override_: ParticipantOverride = + serde_json::from_str(json).wrap_err_with(|| format!("Invalid --participant JSON: {json}"))?; + Ok(apply_participant_override(global_config.clone(), override_)) + }) + .collect() +} + +fn init_logging(filter: EnvFilter) -> Result<()> { + let writer = std::io::stderr + .with_min_level(tracing::Level::WARN) + .or_else(std::io::stdout); + + tracing_subscriber::registry() + .with(tracing_error::ErrorLayer::default()) + .with(fmt::layer().with_writer(writer).with_filter(filter)) + .try_init()?; + + Ok(()) +} + +async fn wait_for_exit(store: client_simulator_browser::participant::ParticipantStore) -> i32 { + wait_for_exit_with( + wait_for_all_stopped(&store), + wait_for_ctrl_c(), + || store.shutdown_all(), + wait_for_ctrl_c, + ) + .await +} + +async fn wait_for_exit_with( + all_stopped: AllStopped, + first_signal: FirstSignal, + shutdown: ShutdownFn, + second_signal: SecondSignalFn, +) -> i32 +where + AllStopped: Future, + FirstSignal: Future, + ShutdownFn: FnOnce() -> Shutdown, + Shutdown: Future, + SecondSignalFn: FnOnce() -> SecondSignal, + SecondSignal: Future, +{ + tokio::select! { + () = all_stopped => 0, + () = first_signal => { + tracing::info!("Shutting down participants. Press Ctrl-C again to force exit."); + tokio::select! { + () = shutdown() => 0, + () = second_signal() => 130, + } + } + } +} + +async fn wait_for_ctrl_c() { + if let Err(err) = tokio::signal::ctrl_c().await { + tracing::error!("Failed to listen for Ctrl-C: {err}"); + } +} + +async fn wait_for_all_stopped(store: &client_simulator_browser::participant::ParticipantStore) { + let states = store + .values() + .into_iter() + .map(|participant| participant.state.clone()) + .collect::>(); + + wait_for_states_stopped(states).await; +} + +async fn wait_for_states_stopped( + states: Vec>, +) { + for state in states { + wait_for_state_stopped_after_start(state).await; + } +} + +async fn wait_for_state_stopped_after_start( + mut state: watch::Receiver, +) { + let mut saw_running = false; + + loop { + let running = state.borrow().running; + if running { + saw_running = true; + } else if saw_running { + return; + } + + if state.changed().await.is_err() { + return; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + use client_simulator_browser::participant::ParticipantState; + use client_simulator_config::{ + Config, + ParticipantBackendKind, + VideoConstraint, + }; + use std::{ + fs, + future::{ + pending, + ready, + }, + path::PathBuf, + time::{ + SystemTime, + UNIX_EPOCH, + }, + }; + use url::Url; + + #[derive(Parser)] + struct TestHeadlessCli { + #[command(flatten)] + args: HeadlessArgs, + } + + #[test] + fn cli_parsing_accepts_repeated_participants() { + let cli = TestHeadlessCli::parse_from([ + "headless", + "--url", + "https://latest.dev.hyper.video/F27-T5F-DXY", + "--participant", + r#"{"audio_enabled": false, "backend": "local"}"#, + "--participant", + r#"{"audio_enabled": true, "backend": "cloudflare"}"#, + ]); + let args = cli.args; + + assert_eq!( + args.url.as_ref().map(Url::as_str), + Some("https://latest.dev.hyper.video/F27-T5F-DXY") + ); + assert_eq!(args.participants.len(), 2); + } + + #[test] + fn cli_bool_options_accept_explicit_false() { + let cli = TestHeadlessCli::parse_from(["headless", "--headless", "false", "--audio-enabled", "false"]); + let args = cli.args; + + assert_eq!(args.headless, Some(false)); + assert_eq!(args.audio_enabled, Some(false)); + } + + #[test] + fn cli_parsing_accepts_video_constraint_options() { + let cli = TestHeadlessCli::parse_from([ + "headless", + "--video-constraint-publish-webcam", + "480p", + "--video-constraint-subscribe", + "720p", + "--video-max-concurrent-tracks", + "2", + "--participant", + r#"{"video_constraint_publish_webcam":"360p","video_constraint_subscribe":"none","video_max_concurrent_tracks":1}"#, + ]); + let args = cli.args; + + assert_eq!(args.video_constraint_publish_webcam, Some(VideoConstraint::P480)); + assert_eq!(args.video_constraint_subscribe, Some(VideoConstraint::P720)); + assert_eq!(args.video_max_concurrent_tracks, Some(2)); + assert_eq!(args.participants.len(), 1); + } + + #[test] + fn participant_json_overrides_global_config() { + let global_config = Config { + url: Some(Url::parse("https://example.com/lite/global").expect("valid url")), + backend: ParticipantBackendKind::Local, + audio_enabled: true, + ..Default::default() + }; + + let configs = build_participant_configs( + global_config, + &[ + r#"{"url":"https://example.com/lite/participant","backend":"cloudflare","audio_enabled":false}"# + .to_string(), + ], + ) + .expect("participant configs"); + + assert_eq!(configs.len(), 1); + assert_eq!( + configs[0].url.as_ref().map(Url::as_str), + Some("https://example.com/lite/participant") + ); + assert_eq!(configs[0].backend, ParticipantBackendKind::Cloudflare); + assert!(!configs[0].audio_enabled); + } + + #[test] + fn participant_json_overrides_video_constraints_and_treats_null_tracks_as_absent() { + let global_config = Config { + video_constraint_publish_webcam: VideoConstraint::P720, + video_constraint_subscribe: VideoConstraint::P720, + video_max_concurrent_tracks: Some(2), + ..Default::default() + }; + + let configs = build_participant_configs( + global_config, + &[r#"{"video_constraint_publish_webcam":"360p","video_max_concurrent_tracks":null}"#.to_string()], + ) + .expect("participant configs"); + + // publish overridden; subscribe omitted -> inherits global; + // max tracks null is treated as absent -> inherits global Some(2). + assert_eq!(configs[0].video_constraint_publish_webcam, VideoConstraint::P360); + assert_eq!(configs[0].video_constraint_subscribe, VideoConstraint::P720); + assert_eq!(configs[0].video_max_concurrent_tracks, Some(2)); + } + + #[test] + fn participant_json_numeric_tracks_override_global() { + let global_config = Config { + video_max_concurrent_tracks: None, + ..Default::default() + }; + + let configs = build_participant_configs(global_config, &[r#"{"video_max_concurrent_tracks":3}"#.to_string()]) + .expect("participant configs"); + + assert_eq!(configs[0].video_max_concurrent_tracks, Some(3)); + } + + #[test] + fn unknown_participant_json_field_returns_error() { + let error = build_participant_configs(Config::default(), &[r#"{"audio_enable":false}"#.to_string()]) + .expect_err("unknown field should fail"); + + assert!(error.to_string().contains("--participant")); + } + + #[test] + fn empty_participant_list_returns_one_effective_config() { + let global_config = Config { + backend: ParticipantBackendKind::RemoteStub, + ..Default::default() + }; + + let configs = build_participant_configs(global_config, &[]).expect("participant configs"); + + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].backend, ParticipantBackendKind::RemoteStub); + } + + #[tokio::test] + async fn wait_for_all_stopped_waits_until_running_participant_stops() { + let data_dir = unique_test_data_dir(); + fs::create_dir_all(&data_dir).expect("create temp data dir"); + let store = client_simulator_browser::participant::ParticipantStore::new(&data_dir); + let config = Config { + url: Some(Url::parse("https://example.com/lite/demo").expect("valid url")), + ..Default::default() + }; + + store.spawn_remote_stub(&config).expect("spawn remote stub"); + + let wait_task = tokio::spawn({ + let store = store.clone(); + async move { wait_for_all_stopped(&store).await } + }); + + tokio::task::yield_now().await; + tokio::task::yield_now().await; + let finished_before_shutdown = wait_task.is_finished(); + + store.shutdown_all().await; + if !wait_task.is_finished() { + wait_task.await.expect("wait task should finish after shutdown"); + } + + assert!( + !finished_before_shutdown, + "wait_for_all_stopped returned before shutdown" + ); + } + + #[tokio::test] + async fn wait_for_states_stopped_ignores_initial_not_running_state() { + let (state_sender, state_receiver) = tokio::sync::watch::channel(ParticipantState::default()); + let wait_task = tokio::spawn(async move { + wait_for_states_stopped(vec![state_receiver]).await; + }); + + tokio::task::yield_now().await; + assert!( + !wait_task.is_finished(), + "initial not-running state should not finish the wait" + ); + + state_sender.send_modify(|state| state.running = true); + tokio::task::yield_now().await; + assert!(state_sender.borrow().running); + assert!(!wait_task.is_finished(), "running state should not finish the wait"); + + state_sender.send_modify(|state| state.running = false); + wait_task.await.expect("wait task should finish after stopped state"); + } + + #[tokio::test] + async fn wait_for_exit_returns_zero_when_shutdown_completes_after_signal() { + let code = wait_for_exit_with(pending(), ready(()), || ready(()), pending).await; + + assert_eq!(code, 0); + } + + #[tokio::test] + async fn wait_for_exit_returns_130_when_second_signal_arrives_before_shutdown() { + let code = wait_for_exit_with(pending(), ready(()), pending, || ready(())).await; + + assert_eq!(code, 130); + } + + #[tokio::test] + async fn spawn_participants_shuts_down_already_spawned_on_later_spawn_error() { + let data_dir = unique_test_data_dir(); + fs::create_dir_all(&data_dir).expect("create temp data dir"); + let store = client_simulator_browser::participant::ParticipantStore::new(&data_dir); + let configs = vec![ + Config { + url: Some(Url::parse("https://example.com/lite/first").expect("valid url")), + backend: ParticipantBackendKind::RemoteStub, + ..Default::default() + }, + Config { + url: None, + backend: ParticipantBackendKind::RemoteStub, + ..Default::default() + }, + ]; + + let error = spawn_participants_or_shutdown(&store, &configs) + .await + .expect_err("second participant should fail to spawn"); + + assert!(error.to_string().contains("Failed to spawn participant")); + assert!(store.is_empty()); + } + + fn unique_test_data_dir() -> PathBuf { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("current time") + .as_nanos(); + std::env::temp_dir().join(format!("hyper-client-simulator-headless-test-{timestamp}")) + } +} diff --git a/src/main.rs b/src/main.rs index 1fc5e3a..71d3676 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ +mod aws; mod errors; +mod headless; use clap::{ Parser, @@ -11,28 +13,270 @@ use eyre::{ OptionExt as _, }; use tracing_subscriber::{ + filter::LevelFilter, fmt, prelude::*, registry, + EnvFilter, }; -#[derive(Parser)] +const DEFAULT_LOGGING_DIRECTIVE: &str = "info"; +const RUST_LOG_ENV: &str = "RUST_LOG"; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, clap::ValueEnum)] +pub enum Logging { + Error, + Warn, + Info, + Debug, + Trace, +} + +impl Logging { + fn as_filter(self) -> &'static str { + match self { + Self::Error => "error", + Self::Warn => "warn", + Self::Info => "info", + Self::Debug => "debug", + Self::Trace => "trace", + } + } +} + +fn logging_filter(logging: Option, rust_log: Option<&str>) -> EnvFilter { + let directive = logging + .map(Logging::as_filter) + .or(rust_log) + .unwrap_or(DEFAULT_LOGGING_DIRECTIVE); + + EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .parse_lossy(directive) +} + +fn logging_filter_from_env(logging: Option) -> EnvFilter { + let rust_log = std::env::var(RUST_LOG_ENV).ok(); + logging_filter(logging, rust_log.as_deref()) +} + +#[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub struct CliArgs { - /// Increase verbosity level (can be used multiple times). 1x = info, 2x = debug, 3x = trace. - #[clap(long = "debug", action = clap::ArgAction::Count)] - pub debug: u8, + /// Tracing filter level. If absent, RUST_LOG is used. + #[clap(long, value_enum, value_name = "LEVEL")] + pub logging: Option, #[command(subcommand)] command: Option, } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] enum Command { /// Start the TUI application Tui(TuiArgs), + /// Start simulator participants without the TUI + Headless(headless::HeadlessArgs), /// Connect to the hyper server to get a hyper session cookie Cookie(CookieArgs), + /// Manage AWS Device Farm Test Grid sessions + Aws(aws::AwsArgs), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_headless_command_with_repeated_participants() { + let args = CliArgs::parse_from([ + "hyper-client-simulator", + "--logging", + "debug", + "headless", + "--url", + "https://latest.dev.hyper.video/F27-T5F-DXY", + "--participant", + r#"{"audio_enabled": false, "backend": "local"}"#, + "--participant", + r#"{"audio_enabled": true, "backend": "cloudflare"}"#, + ]); + + match args.command { + Some(Command::Headless(headless)) => { + assert_eq!(args.logging, Some(Logging::Debug)); + assert_eq!( + headless.url.as_ref().map(url::Url::as_str), + Some("https://latest.dev.hyper.video/F27-T5F-DXY") + ); + assert_eq!(headless.participants.len(), 2); + } + other => panic!("expected headless command, got {other:?}"), + } + } + + #[test] + fn parses_logging_levels() { + for (value, logging) in [ + ("error", Logging::Error), + ("warn", Logging::Warn), + ("info", Logging::Info), + ("debug", Logging::Debug), + ("trace", Logging::Trace), + ] { + let args = CliArgs::parse_from(["hyper-client-simulator", "--logging", value, "headless"]); + + assert_eq!(args.logging, Some(logging)); + } + } + + #[test] + fn leaves_logging_empty_when_not_provided() { + let args = CliArgs::parse_from(["hyper-client-simulator", "headless"]); + + assert_eq!(args.logging, None); + } + + #[test] + fn rejects_unknown_logging_level() { + let err = CliArgs::try_parse_from(["hyper-client-simulator", "--logging", "verbose", "headless"]) + .expect_err("unknown logging level should fail"); + + assert_eq!(err.kind(), clap::error::ErrorKind::InvalidValue); + } + + #[test] + fn uses_cli_logging_for_filter_when_present() { + let filter = logging_filter(Some(Logging::Trace), Some("warn")); + + assert_eq!(filter.to_string(), "trace"); + } + + #[test] + fn uses_rust_log_for_filter_when_cli_logging_is_absent() { + let filter = logging_filter(None, Some("warn,client_simulator_browser=debug")); + + let filter = filter.to_string(); + assert!(filter.contains("warn")); + assert!(filter.contains("client_simulator_browser=debug")); + } + + #[test] + fn parses_aws_list_sessions_json() { + let args = CliArgs::parse_from(["hyper-client-simulator", "aws", "list-sessions", "--json"]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::ListSessions(args), + })) => { + assert!(args.json); + assert_eq!(args.status, aws::ListSessionsStatus::Active); + } + other => panic!("expected aws list-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_list_sessions_closed_status() { + let args = CliArgs::parse_from(["hyper-client-simulator", "aws", "list-sessions", "--status", "closed"]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::ListSessions(args), + })) => { + assert_eq!(args.status, aws::ListSessionsStatus::Closed); + } + other => panic!("expected aws list-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_list_sessions_all_status() { + let args = CliArgs::parse_from(["hyper-client-simulator", "aws", "list-sessions", "--status", "all"]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::ListSessions(args), + })) => { + assert_eq!(args.status, aws::ListSessionsStatus::All); + } + other => panic!("expected aws list-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_list_sessions_since_date() { + let args = CliArgs::parse_from([ + "hyper-client-simulator", + "aws", + "list-sessions", + "--since", + "2026-06-04", + ]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::ListSessions(args), + })) => { + assert!(args.since.is_some()); + } + other => panic!("expected aws list-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_list_sessions_since_date_time() { + let args = CliArgs::parse_from([ + "hyper-client-simulator", + "aws", + "list-sessions", + "--since", + "2026-06-04 10:11", + ]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::ListSessions(args), + })) => { + assert!(args.since.is_some()); + } + other => panic!("expected aws list-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_list_sessions_since_human_duration() { + let args = CliArgs::parse_from(["hyper-client-simulator", "aws", "list-sessions", "--since", "1 day"]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::ListSessions(args), + })) => { + assert!(args.since.is_some()); + } + other => panic!("expected aws list-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_close_sessions_with_comma_separated_ids() { + let args = CliArgs::parse_from(["hyper-client-simulator", "aws", "close-sessions", "a,b,c"]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::CloseSessions(args), + })) => { + assert_eq!(args.session_ids, vec!["a", "b", "c"]); + } + other => panic!("expected aws close-sessions, got {other:?}"), + } + } + + #[test] + fn parses_aws_close_sessions_without_ids() { + let args = CliArgs::parse_from(["hyper-client-simulator", "aws", "close-sessions"]); + match args.command { + Some(Command::Aws(aws::AwsArgs { + command: aws::AwsCommand::CloseSessions(args), + })) => { + assert!(args.session_ids.is_empty()); + } + other => panic!("expected aws close-sessions, got {other:?}"), + } + } } #[derive(clap::Args, Debug, Clone)] @@ -50,41 +294,33 @@ pub struct CookieArgs { async fn main() -> eyre::Result<()> { errors::init()?; - let CliArgs { command, debug } = CliArgs::parse(); + let CliArgs { command, logging } = CliArgs::parse(); match command { None => { - let args = TuiArgs { - debug, - ..Default::default() - }; - start_tui(args).await + let args = TuiArgs::default(); + start_tui(args, logging_filter_from_env(logging)).await } - Some(Command::Tui(mut args)) => { - args.debug = debug; - start_tui(args).await + Some(Command::Tui(args)) => start_tui(args, logging_filter_from_env(logging)).await, + Some(Command::Headless(args)) => { + let code = headless::run(args, logging_filter_from_env(logging)).await?; + std::process::exit(code); } - Some(Command::Cookie(args)) => run_cookie(args, debug).await, + Some(Command::Cookie(args)) => run_cookie(args, logging_filter_from_env(logging)).await, + Some(Command::Aws(args)) => aws::run(args, logging_filter_from_env(logging)).await, } } -async fn run_cookie(CookieArgs { base_url, user }: CookieArgs, debug: u8) -> eyre::Result<()> { - if debug > 0 { - let filter = match debug { - 1 => "info", - 2 => "debug", - _ => "trace", - }; - - registry() - .with( - fmt::layer() - .with_span_events(fmt::format::FmtSpan::CLOSE) - .with_filter(tracing_subscriber::EnvFilter::builder().parse_lossy(filter)), - ) - .with(tracing_error::ErrorLayer::default()) - .init(); - } +async fn run_cookie(CookieArgs { base_url, user }: CookieArgs, filter: EnvFilter) -> eyre::Result<()> { + registry() + .with( + fmt::layer() + .with_writer(std::io::stderr) + .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_filter(filter), + ) + .with(tracing_error::ErrorLayer::default()) + .init(); let domain = base_url .host_str() diff --git a/tui/src/lib.rs b/tui/src/lib.rs index 4a05a3b..817e555 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -1,4 +1,5 @@ use client_simulator_config::TuiArgs; +use tracing_subscriber::EnvFilter; #[macro_use] extern crate tracing; @@ -8,8 +9,8 @@ mod tui; pub use tui::Tui; -pub async fn start_tui(args: TuiArgs) -> eyre::Result<()> { - logging::log_init(args.debug)?; +pub async fn start_tui(args: TuiArgs, filter: EnvFilter) -> eyre::Result<()> { + logging::log_init(filter)?; tui::App::new(args)?.run().await } diff --git a/tui/src/logging.rs b/tui/src/logging.rs index 1a70d19..0d1c560 100644 --- a/tui/src/logging.rs +++ b/tui/src/logging.rs @@ -3,14 +3,21 @@ use eyre::{ Context as _, Result, }; -use tracing_subscriber::prelude::*; +use tracing::{ + level_filters::LevelFilter, + Level, +}; +use tracing_subscriber::{ + prelude::*, + EnvFilter, +}; use tui_logger::TuiLoggerFile; lazy_static::lazy_static! { static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME")); } -pub fn log_init(debug_level: u8) -> Result<()> { +pub fn log_init(filter: EnvFilter) -> Result<()> { let directory = get_data_dir(); std::fs::create_dir_all(directory.clone()).context("Failed to create directory")?; let log_path = directory.join(LOG_FILE.clone()); @@ -18,11 +25,7 @@ pub fn log_init(debug_level: u8) -> Result<()> { std::fs::remove_file(&log_path).context("Failed to remove existing log file")?; } - let level_filter = match debug_level { - 0 => tui_logger::LevelFilter::Info, - 1 => tui_logger::LevelFilter::Debug, - _ => tui_logger::LevelFilter::Trace, - }; + let level_filter = tui_level_filter(filter.max_level_hint().unwrap_or(LevelFilter::INFO)); tui_logger::init_logger(level_filter).context("Failed to initialize tui logger")?; tui_logger::set_level_for_target("log", level_filter); @@ -30,7 +33,19 @@ pub fn log_init(debug_level: u8) -> Result<()> { tracing_subscriber::registry() .with(tracing_error::ErrorLayer::default()) + .with(filter) .with(tui_logger::TuiTracingSubscriberLayer) .try_init() .context("Failed to initialize tracing subscriber") } + +fn tui_level_filter(level_filter: LevelFilter) -> tui_logger::LevelFilter { + match level_filter.into_level() { + Some(Level::ERROR) => tui_logger::LevelFilter::Error, + Some(Level::WARN) => tui_logger::LevelFilter::Warn, + Some(Level::INFO) => tui_logger::LevelFilter::Info, + Some(Level::DEBUG) => tui_logger::LevelFilter::Debug, + Some(Level::TRACE) => tui_logger::LevelFilter::Trace, + None => tui_logger::LevelFilter::Off, + } +} diff --git a/tui/src/tui/app.rs b/tui/src/tui/app.rs index 700bb0f..9deea89 100644 --- a/tui/src/tui/app.rs +++ b/tui/src/tui/app.rs @@ -188,6 +188,11 @@ impl App { Action::ForceQuit => { if self.shutdown_in_progress { warn!("Force quitting while participant shutdown is still in progress"); + // Follow-up hardening: force quit currently lets Tokio + // tear down tasks that may still own browser driver + // handles. A safer force path would track participant + // task handles and run a bounded driver cleanup, or + // explicitly suppress driver Drop cleanup, before exit. self.should_quit = true; } else { self.begin_shutdown(action_tx.clone()); diff --git a/tui/src/tui/components/browser_start.rs b/tui/src/tui/components/browser_start.rs index 82cb2a7..a2d262f 100644 --- a/tui/src/tui/components/browser_start.rs +++ b/tui/src/tui/components/browser_start.rs @@ -19,7 +19,8 @@ use client_simulator_config::{ NoiseSuppression, ParticipantBackendKind, TransportMode, - WebcamResolution, + VideoConstraint, + VideoMaxConcurrentTracksPreset, }; use color_eyre::Result; use crossterm::event::KeyCode; @@ -47,7 +48,9 @@ enum SelectedField { AutoGainControl, NoiseSuppression, Transport, - Resolution, + VideoConstraintPublishWebcam, + VideoConstraintSubscribe, + VideoMaxConcurrentTracks, BackgroundBlur, Headless, Backend, @@ -67,7 +70,13 @@ impl SelectedField { SelectedField::AutoGainControl => " Automatically adjust volume? to toggle. ", SelectedField::NoiseSuppression => " Enable noise suppression? to select noise suppression model. ", SelectedField::Transport => " Select transport protocol. to select. ", - SelectedField::Resolution => " Select resolution for video (camera). to select. ", + SelectedField::VideoConstraintPublishWebcam => { + " Select outgoing webcam video constraint. to select. " + } + SelectedField::VideoConstraintSubscribe => " Select incoming video constraint. to select. ", + SelectedField::VideoMaxConcurrentTracks => { + " Select max concurrent webcam tracks. to select. " + } SelectedField::BackgroundBlur => " Enable background blur? to toggle. ", SelectedField::Headless => " Run the browser in headless mode? When disabled, will show a browser window with which you can interact. to toggle. ", SelectedField::Backend => " Select the participant backend. to select, to reset. ", @@ -84,7 +93,9 @@ pub(crate) enum BrowserStartAction { StartSelectFakeMedia, StartSelectNoiseSuppression, StartSelectTransport, - StartSelectResolution, + StartSelectVideoConstraintPublishWebcam, + StartSelectVideoConstraintSubscribe, + StartSelectVideoMaxConcurrentTracks, StartSelectBackend, StartBrowser, Toggle, @@ -109,7 +120,9 @@ pub struct BrowserStart { fake_media_builtin_list: Option>, noise_suppression_list: Option>, transport_list: Option>, - resolution_list: Option>, + video_constraint_publish_webcam_list: Option>, + video_constraint_subscribe_list: Option>, + video_max_concurrent_tracks_list: Option>, backend_list: Option>, participant_store: ParticipantStore, } @@ -125,7 +138,9 @@ impl BrowserStart { selected: SelectedField::Url, fake_media_builtin_list: None, noise_suppression_list: None, - resolution_list: None, + video_constraint_publish_webcam_list: None, + video_constraint_subscribe_list: None, + video_max_concurrent_tracks_list: None, transport_list: None, backend_list: None, editing: None, @@ -171,7 +186,9 @@ impl Component for BrowserStart { | SelectedField::AutoGainControl | SelectedField::NoiseSuppression | SelectedField::Transport - | SelectedField::Resolution + | SelectedField::VideoConstraintPublishWebcam + | SelectedField::VideoConstraintSubscribe + | SelectedField::VideoMaxConcurrentTracks | SelectedField::BackgroundBlur | SelectedField::Headless | SelectedField::Backend @@ -266,12 +283,68 @@ impl Component for BrowserStart { } } - if let Some(mut list) = self.resolution_list.take() { + if let Some(mut list) = self.video_constraint_publish_webcam_list.take() { + match key.code { + KeyCode::Enter => { + match list.finish() { + Ok(value) => { + self.config.video_constraint_publish_webcam = value; + } + Err(err) => { + error!(?err, "Failed to parse"); + } + } + if let Err(e) = self.config.save() { + error!(?e, "Failed to save config after edit"); + } + return Ok(Some(Action::Activate(ActivateAction::BrowserStart))); + } + KeyCode::Esc => { + return Ok(Some(Action::Activate(ActivateAction::BrowserStart))); + } + _ => {} + } + let handled = list.handle_key_event(key); + self.video_constraint_publish_webcam_list = Some(list); + if handled { + return Ok(None); + } + } + + if let Some(mut list) = self.video_constraint_subscribe_list.take() { + match key.code { + KeyCode::Enter => { + match list.finish() { + Ok(value) => { + self.config.video_constraint_subscribe = value; + } + Err(err) => { + error!(?err, "Failed to parse"); + } + } + if let Err(e) = self.config.save() { + error!(?e, "Failed to save config after edit"); + } + return Ok(Some(Action::Activate(ActivateAction::BrowserStart))); + } + KeyCode::Esc => { + return Ok(Some(Action::Activate(ActivateAction::BrowserStart))); + } + _ => {} + } + let handled = list.handle_key_event(key); + self.video_constraint_subscribe_list = Some(list); + if handled { + return Ok(None); + } + } + + if let Some(mut list) = self.video_max_concurrent_tracks_list.take() { match key.code { KeyCode::Enter => { match list.finish() { Ok(value) => { - self.config.resolution = value; + self.config.video_max_concurrent_tracks = value.to_option(); } Err(err) => { error!(?err, "Failed to parse"); @@ -288,7 +361,7 @@ impl Component for BrowserStart { _ => {} } let handled = list.handle_key_event(key); - self.resolution_list = Some(list); + self.video_max_concurrent_tracks_list = Some(list); if handled { return Ok(None); } @@ -370,8 +443,14 @@ impl Component for BrowserStart { KeyCode::Enter if self.selected == SelectedField::Transport => { Some(BrowserStartAction::StartSelectTransport) } - KeyCode::Enter if self.selected == SelectedField::Resolution => { - Some(BrowserStartAction::StartSelectResolution) + KeyCode::Enter if self.selected == SelectedField::VideoConstraintPublishWebcam => { + Some(BrowserStartAction::StartSelectVideoConstraintPublishWebcam) + } + KeyCode::Enter if self.selected == SelectedField::VideoConstraintSubscribe => { + Some(BrowserStartAction::StartSelectVideoConstraintSubscribe) + } + KeyCode::Enter if self.selected == SelectedField::VideoMaxConcurrentTracks => { + Some(BrowserStartAction::StartSelectVideoMaxConcurrentTracks) } KeyCode::Enter if self.selected == SelectedField::BackgroundBlur => Some(BrowserStartAction::Toggle), KeyCode::Enter if self.selected == SelectedField::Backend => Some(BrowserStartAction::StartSelectBackend), @@ -389,8 +468,16 @@ impl Component for BrowserStart { self.noise_suppression_list = None; None } - KeyCode::Esc if self.resolution_list.is_some() => { - self.resolution_list = None; + KeyCode::Esc if self.video_constraint_publish_webcam_list.is_some() => { + self.video_constraint_publish_webcam_list = None; + None + } + KeyCode::Esc if self.video_constraint_subscribe_list.is_some() => { + self.video_constraint_subscribe_list = None; + None + } + KeyCode::Esc if self.video_max_concurrent_tracks_list.is_some() => { + self.video_max_concurrent_tracks_list = None; None } KeyCode::Esc if self.transport_list.is_some() => { @@ -448,8 +535,10 @@ impl Component for BrowserStart { SelectedField::AutoGainControl => SelectedField::ScreenshareDisable, SelectedField::NoiseSuppression => SelectedField::AutoGainControl, SelectedField::Transport => SelectedField::NoiseSuppression, - SelectedField::Resolution => SelectedField::Transport, - SelectedField::BackgroundBlur => SelectedField::Resolution, + SelectedField::VideoConstraintPublishWebcam => SelectedField::Transport, + SelectedField::VideoConstraintSubscribe => SelectedField::VideoConstraintPublishWebcam, + SelectedField::VideoMaxConcurrentTracks => SelectedField::VideoConstraintSubscribe, + SelectedField::BackgroundBlur => SelectedField::VideoMaxConcurrentTracks, SelectedField::Headless => SelectedField::BackgroundBlur, SelectedField::Backend => SelectedField::Headless, SelectedField::StartBrowser => SelectedField::Backend, @@ -465,8 +554,10 @@ impl Component for BrowserStart { SelectedField::ScreenshareDisable => SelectedField::AutoGainControl, SelectedField::AutoGainControl => SelectedField::NoiseSuppression, SelectedField::NoiseSuppression => SelectedField::Transport, - SelectedField::Transport => SelectedField::Resolution, - SelectedField::Resolution => SelectedField::BackgroundBlur, + SelectedField::Transport => SelectedField::VideoConstraintPublishWebcam, + SelectedField::VideoConstraintPublishWebcam => SelectedField::VideoConstraintSubscribe, + SelectedField::VideoConstraintSubscribe => SelectedField::VideoMaxConcurrentTracks, + SelectedField::VideoMaxConcurrentTracks => SelectedField::BackgroundBlur, SelectedField::BackgroundBlur => SelectedField::Headless, SelectedField::Headless => SelectedField::Backend, SelectedField::Backend => SelectedField::StartBrowser, @@ -544,11 +635,29 @@ impl Component for BrowserStart { return Ok(None); } - BrowserStartAction::StartSelectResolution => { - self.resolution_list = Some(EnumListInput::new( - "Camera resolution", - WebcamResolution::iter(), - self.config.resolution, + BrowserStartAction::StartSelectVideoConstraintPublishWebcam => { + self.video_constraint_publish_webcam_list = Some(EnumListInput::new( + "Outgoing webcam video constraint", + VideoConstraint::iter(), + self.config.video_constraint_publish_webcam, + )); + return Ok(None); + } + + BrowserStartAction::StartSelectVideoConstraintSubscribe => { + self.video_constraint_subscribe_list = Some(EnumListInput::new( + "Incoming video constraint", + VideoConstraint::iter(), + self.config.video_constraint_subscribe, + )); + return Ok(None); + } + + BrowserStartAction::StartSelectVideoMaxConcurrentTracks => { + self.video_max_concurrent_tracks_list = Some(EnumListInput::new( + "Max concurrent webcam tracks", + VideoMaxConcurrentTracksPreset::iter(), + VideoMaxConcurrentTracksPreset::from_option(self.config.video_max_concurrent_tracks), )); return Ok(None); } @@ -650,7 +759,9 @@ impl Component for BrowserStart { Constraint::Length(1), // Auto gain control checkbox Constraint::Length(1), // Noise suppression checkbox Constraint::Length(1), // Transport - Constraint::Length(1), // Resolution + Constraint::Length(1), // Outgoing webcam video constraint + Constraint::Length(1), // Incoming video constraint + Constraint::Length(1), // Max concurrent video tracks Constraint::Length(1), // Background blur checkbox Constraint::Length(1), // Headless checkbox Constraint::Length(1), // Backend @@ -671,7 +782,9 @@ impl Component for BrowserStart { "Auto gain control:", "Noise suppression:", "Transport:", - "Resolution:", + "Outgoing constraint:", + "Incoming constraint:", + "Track limit:", "Background blur", "Headless:", "Backend:", @@ -773,13 +886,41 @@ impl Component for BrowserStart { frame.render_widget(widget, rows[current_row_index]); current_row_index += 1; - // --- Resolution --- - let resolution = self.config.resolution.to_string(); + // --- Outgoing webcam video constraint --- + let publish = self.config.video_constraint_publish_webcam.to_string(); let widget = widgets::label_and_text( form_labels[current_row_index], - &resolution, + &publish, max_length, - self.focused && self.selected == SelectedField::Resolution, + self.focused && self.selected == SelectedField::VideoConstraintPublishWebcam, + &theme, + ); + frame.render_widget(widget, rows[current_row_index]); + current_row_index += 1; + + // --- Incoming video constraint --- + let subscribe = self.config.video_constraint_subscribe.to_string(); + let widget = widgets::label_and_text( + form_labels[current_row_index], + &subscribe, + max_length, + self.focused && self.selected == SelectedField::VideoConstraintSubscribe, + &theme, + ); + frame.render_widget(widget, rows[current_row_index]); + current_row_index += 1; + + // --- Max concurrent video tracks --- + let max_tracks = self + .config + .video_max_concurrent_tracks + .map(|value| value.to_string()) + .unwrap_or_else(|| "unlimited".to_string()); + let widget = widgets::label_and_text( + form_labels[current_row_index], + &max_tracks, + max_length, + self.focused && self.selected == SelectedField::VideoMaxConcurrentTracks, &theme, ); frame.render_widget(widget, rows[current_row_index]); @@ -841,7 +982,13 @@ impl Component for BrowserStart { if let Some(list) = &mut self.noise_suppression_list { list.draw(frame, area)?; } - if let Some(list) = &mut self.resolution_list { + if let Some(list) = &mut self.video_constraint_publish_webcam_list { + list.draw(frame, area)?; + } + if let Some(list) = &mut self.video_constraint_subscribe_list { + list.draw(frame, area)?; + } + if let Some(list) = &mut self.video_max_concurrent_tracks_list { list.draw(frame, area)?; } if let Some(list) = &mut self.transport_list { diff --git a/tui/src/tui/components/participants.rs b/tui/src/tui/components/participants.rs index c157a74..49e1615 100644 --- a/tui/src/tui/components/participants.rs +++ b/tui/src/tui/components/participants.rs @@ -16,7 +16,8 @@ use client_simulator_browser::participant::ParticipantStore; use client_simulator_config::{ Config, NoiseSuppression, - WebcamResolution, + VideoConstraint, + VideoMaxConcurrentTracksPreset, }; use color_eyre::Result; use crossterm::event::KeyCode; @@ -49,7 +50,17 @@ pub(crate) enum ParticipantsAction { MoveUp, MoveDown, StartSelectNoiseSuppression, - StartSelectResolution, + StartSelectVideoSetting, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Display, strum::EnumIter, strum::EnumString)] +enum VideoSetting { + #[strum(to_string = "Outgoing constraint")] + PublishWebcam, + #[strum(to_string = "Incoming constraint")] + Subscribe, + #[strum(to_string = "Track limit")] + MaxTracks, } #[derive(Debug)] @@ -61,7 +72,10 @@ pub struct Participants { table_state: TableState, keymap: Keymap, noise_suppression_list: Option>, - resolution_list: Option>, + video_setting_menu: Option>, + video_constraint_publish_webcam_list: Option>, + video_constraint_subscribe_list: Option>, + video_max_concurrent_tracks_list: Option>, } impl Participants { @@ -74,7 +88,10 @@ impl Participants { table_state: TableState::default(), keymap: Keymap::default(), noise_suppression_list: None, - resolution_list: None, + video_setting_menu: None, + video_constraint_publish_webcam_list: None, + video_constraint_subscribe_list: None, + video_max_concurrent_tracks_list: None, } } @@ -198,14 +215,12 @@ impl Component for Participants { } return Ok(None); } - ParticipantsAction::StartSelectResolution => { - if let Some(selected) = self.selected.as_ref().and_then(|s| self.participants.get(s)) { - self.resolution_list = Some(EnumListInput::new( - "Camera resolution", - WebcamResolution::iter(), - selected.state.borrow().webcam_resolution, - )); - } + ParticipantsAction::StartSelectVideoSetting => { + self.video_setting_menu = Some(EnumListInput::new( + "Video constraints", + VideoSetting::iter(), + VideoSetting::PublishWebcam, + )); return Ok(None); } }, @@ -237,23 +252,112 @@ impl Component for Participants { } } - if let Some(mut list) = self.resolution_list.take() { + if let Some(mut list) = self.video_setting_menu.take() { + match key.code { + KeyCode::Enter => { + if let Ok(value) = list.finish() { + if let Some(selected) = self.selected.as_ref().and_then(|s| self.participants.get(s)) { + let state = selected.state.borrow(); + match value { + VideoSetting::PublishWebcam => { + self.video_constraint_publish_webcam_list = Some(EnumListInput::new( + "Outgoing webcam video constraint", + VideoConstraint::iter(), + state.video_constraint_publish_webcam, + )); + } + VideoSetting::Subscribe => { + self.video_constraint_subscribe_list = Some(EnumListInput::new( + "Incoming video constraint", + VideoConstraint::iter(), + state.video_constraint_subscribe, + )); + } + VideoSetting::MaxTracks => { + self.video_max_concurrent_tracks_list = Some(EnumListInput::new( + "Max concurrent webcam tracks", + VideoMaxConcurrentTracksPreset::iter(), + VideoMaxConcurrentTracksPreset::from_option(state.video_max_concurrent_tracks), + )); + } + } + } + } + return Ok(None); + } + KeyCode::Esc => { + return Ok(Some(Action::Activate(ActivateAction::Participants))); + } + _ => {} + } + let handled = list.handle_key_event(key); + self.video_setting_menu = Some(list); + if handled { + return Ok(None); + } + } + + if let Some(mut list) = self.video_constraint_publish_webcam_list.take() { + match key.code { + KeyCode::Enter => { + if let Ok(value) = list.finish() { + if let Some(participant) = self.selected.as_ref().and_then(|s| self.participants.get(s)) { + participant.set_video_constraint_publish_webcam(value); + } + } + return Ok(Some(Action::Activate(ActivateAction::Participants))); + } + KeyCode::Esc => { + return Ok(Some(Action::Activate(ActivateAction::Participants))); + } + _ => {} + } + let handled = list.handle_key_event(key); + self.video_constraint_publish_webcam_list = Some(list); + if handled { + return Ok(None); + } + } + + if let Some(mut list) = self.video_constraint_subscribe_list.take() { + match key.code { + KeyCode::Enter => { + if let Ok(value) = list.finish() { + if let Some(participant) = self.selected.as_ref().and_then(|s| self.participants.get(s)) { + participant.set_video_constraint_subscribe(value); + } + } + return Ok(Some(Action::Activate(ActivateAction::Participants))); + } + KeyCode::Esc => { + return Ok(Some(Action::Activate(ActivateAction::Participants))); + } + _ => {} + } + let handled = list.handle_key_event(key); + self.video_constraint_subscribe_list = Some(list); + if handled { + return Ok(None); + } + } + + if let Some(mut list) = self.video_max_concurrent_tracks_list.take() { match key.code { KeyCode::Enter => { if let Ok(value) = list.finish() { if let Some(participant) = self.selected.as_ref().and_then(|s| self.participants.get(s)) { - participant.set_webcam_resolutions(value); + participant.set_video_max_concurrent_tracks(value.to_option()); } } return Ok(Some(Action::Activate(ActivateAction::Participants))); } KeyCode::Esc => { - return Ok(Some(Action::Activate(ActivateAction::BrowserStart))); + return Ok(Some(Action::Activate(ActivateAction::Participants))); } _ => {} } let handled = list.handle_key_event(key); - self.resolution_list = Some(list); + self.video_max_concurrent_tracks_list = Some(list); if handled { return Ok(None); } @@ -326,7 +430,7 @@ impl Component for Participants { )), (KeyCode::Char('r'), Some(_)) => { - Some(Action::ParticipantsAction(ParticipantsAction::StartSelectResolution)) + Some(Action::ParticipantsAction(ParticipantsAction::StartSelectVideoSetting)) } (KeyCode::Char('b'), Some(selected)) => { @@ -351,7 +455,7 @@ impl Component for Participants { let [_, _, area] = header_and_two_main_areas(area)?; let help = if self.selected.is_some() { - " to shutdown, oin, eave, ute, ideo, creenshare, auto ain, oise suppression, esolutions, lur " + " to shutdown, oin, eave, ute, ideo, creenshare, auto ain, oise suppression, video constraints, lur " } else { "" }; @@ -382,7 +486,7 @@ impl Component for Participants { "Autogain", "Noise Suppression", "Transport", - "Resolution", + "Video constraints", "Blur", ]; @@ -411,7 +515,13 @@ impl Component for Participants { let auto_gain_control = format_bool(state.auto_gain_control); let noise_suppression = state.noise_suppression.to_string(); let transport_mode = state.transport_mode.to_string(); - let resolution = state.webcam_resolution.to_string(); + let publish = state.video_constraint_publish_webcam.to_string(); + let subscribe = state.video_constraint_subscribe.to_string(); + let tracks = state + .video_max_concurrent_tracks + .map(|value| value.to_string()) + .unwrap_or_else(|| "∞".to_string()); + let video_constraints = format!("out:{publish} in:{subscribe} t:{tracks}"); let background_blur = format_bool(state.background_blur); let cells = vec![ Cell::from(name), @@ -424,7 +534,7 @@ impl Component for Participants { Cell::from(auto_gain_control), Cell::from(noise_suppression), Cell::from(transport_mode), - Cell::from(resolution), + Cell::from(video_constraints), Cell::from(background_blur), ]; let style = if Some(&participant.name) == self.selected.as_ref() { @@ -447,18 +557,18 @@ impl Component for Participants { .title_bottom(Line::from(help).centered()), ) .widths([ - Constraint::Percentage(10), // Name - Constraint::Percentage(8), // Created - Constraint::Percentage(7), // Running - Constraint::Percentage(7), // Joined - Constraint::Percentage(7), // Muted + Constraint::Percentage(9), // Name + Constraint::Percentage(7), // Created + Constraint::Percentage(6), // Running + Constraint::Percentage(6), // Joined + Constraint::Percentage(6), // Muted Constraint::Percentage(7), // Video active - Constraint::Percentage(9), // Screenshare active - Constraint::Percentage(8), // Auto gain - Constraint::Percentage(12), // Noise suppression - Constraint::Percentage(8), // Transport mode - Constraint::Percentage(8), // Resolution - Constraint::Percentage(9), // Blur + Constraint::Percentage(8), // Screenshare active + Constraint::Percentage(7), // Auto gain + Constraint::Percentage(11), // Noise suppression + Constraint::Percentage(7), // Transport mode + Constraint::Percentage(18), // Video constraints + Constraint::Percentage(8), // Blur ]) .column_spacing(1); @@ -468,7 +578,16 @@ impl Component for Participants { if let Some(list) = &mut self.noise_suppression_list { list.draw(frame, area)?; } - if let Some(list) = &mut self.resolution_list { + if let Some(list) = &mut self.video_setting_menu { + list.draw(frame, area)?; + } + if let Some(list) = &mut self.video_constraint_publish_webcam_list { + list.draw(frame, area)?; + } + if let Some(list) = &mut self.video_constraint_subscribe_list { + list.draw(frame, area)?; + } + if let Some(list) = &mut self.video_max_concurrent_tracks_list { list.draw(frame, area)?; } diff --git a/tui/src/tui/layout.rs b/tui/src/tui/layout.rs index cf42bdb..86ef5e3 100644 --- a/tui/src/tui/layout.rs +++ b/tui/src/tui/layout.rs @@ -35,8 +35,8 @@ pub(crate) fn header_and_two_main_areas(area: Rect) -> Result<[Rect; 3]> { let [header, area] = header_and_main_area(area)?; let [a, b] = *Layout::default() .direction(Direction::Vertical) - // The browser controls pane currently needs 17 rows including borders. - .constraints([Constraint::Max(17), Constraint::Min(0)]) + // The browser controls pane currently needs 19 rows including borders. + .constraints([Constraint::Max(19), Constraint::Min(0)]) .split(area) else { bail!("Failed to split the area"); @@ -48,7 +48,7 @@ pub(crate) fn header_and_two_main_areas(area: Rect) -> Result<[Rect; 3]> { /// /// # Examples /// -/// ```rust +/// ```ignore /// use ratatui::layout::{Constraint, Rect}; /// /// let area = Rect::new(0, 0, 100, 100); diff --git a/vendor/chromiumoxide_cdp/Cargo.toml b/vendor/chromiumoxide_cdp/Cargo.toml new file mode 100644 index 0000000..3a4530c --- /dev/null +++ b/vendor/chromiumoxide_cdp/Cargo.toml @@ -0,0 +1,70 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.85" +name = "chromiumoxide_cdp" +version = "0.9.1" +authors = ["Matthias Seitz "] +build = false +include = [ + "src/**/*", + "*.pdl", + "LICENSE-*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Contains all the generated types for chromiumoxide" +homepage = "https://github.com/mattsse/chromiumoxide" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/mattsse/chromiumoxide" + +[lib] +name = "chromiumoxide_cdp" +path = "src/lib.rs" + +[dependencies.chromiumoxide_pdl] +version = "0.9" + +[dependencies.chromiumoxide_types] +version = "0.9" + +[dependencies.serde] +version = "1" +features = ["derive"] + +[dependencies.serde_json] +version = "1" + +[dev-dependencies.chromiumoxide_pdl] +version = "0.9" + +[dev-dependencies.reqwest] +version = "0.13" +features = ["rustls"] +default-features = false + +[dev-dependencies.tempfile] +version = "3.10" + +[dev-dependencies.tokio] +version = "1" +features = [ + "rt", + "fs", + "process", + "macros", +] diff --git a/vendor/chromiumoxide_cdp/LICENSE-APACHE b/vendor/chromiumoxide_cdp/LICENSE-APACHE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/vendor/chromiumoxide_cdp/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/chromiumoxide_cdp/LICENSE-MIT b/vendor/chromiumoxide_cdp/LICENSE-MIT new file mode 100644 index 0000000..8050947 --- /dev/null +++ b/vendor/chromiumoxide_cdp/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Matthias Seitz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/chromiumoxide_cdp/PATCHES.md b/vendor/chromiumoxide_cdp/PATCHES.md new file mode 100644 index 0000000..e3fc164 --- /dev/null +++ b/vendor/chromiumoxide_cdp/PATCHES.md @@ -0,0 +1,5 @@ +# Local patches + +This is `chromiumoxide_cdp` 0.9.1 with one compatibility patch for Chrome 146. + +- `src/cdp.rs`: `Network.ClientSecurityState.privateNetworkRequestPolicy` accepts `localNetworkAccessRequestPolicy` as a serde alias, matching the key emitted by current Chrome for `Network.requestWillBeSentExtraInfo`. diff --git a/vendor/chromiumoxide_cdp/README.md b/vendor/chromiumoxide_cdp/README.md new file mode 100644 index 0000000..9c11088 --- /dev/null +++ b/vendor/chromiumoxide_cdp/README.md @@ -0,0 +1,136 @@ +# chromiumoxide + +![Build](https://github.com/mattsse/chromiumoxide/workflows/Continuous%20integration/badge.svg) +[![Crates.io](https://img.shields.io/crates/v/chromiumoxide.svg)](https://crates.io/crates/chromiumoxide) +[![Documentation](https://docs.rs/chromiumoxide/badge.svg)](https://docs.rs/chromiumoxide) + +chromiumoxide provides a high-level and async API to control Chrome or Chromium over the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). It comes with support for all types of the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) and can launch a [headless](https://developers.google.com/web/updates/2017/04/headless-chrome) or full (non-headless) Chrome or Chromium instance or connect to an already running instance. + +## Usage + +```rust +use futures::StreamExt; + +use chromiumoxide::browser::{Browser, BrowserConfig}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // create a `Browser` that spawns a `chromium` process running with UI (`with_head()`, headless is default) + // and the handler that drives the websocket etc. + let (mut browser, mut handler) = + Browser::launch(BrowserConfig::builder().with_head().build()?).await?; + + // spawn a new task that continuously polls the handler + let handle = tokio::spawn(async move { + while let Some(h) = handler.next().await { + if h.is_err() { + break; + } + } + }); + + // create a new browser page and navigate to the url + let page = browser.new_page("https://en.wikipedia.org").await?; + + // find and click the search toggle button to reveal the search bar + page.find_element(".search-toggle").await?.click().await?; + + // find the search bar type into the search field and hit `Enter`, + // this triggers a new navigation to the search result page + page.find_element("input[name='search']") + .await? + .click() + .await? + .type_str("Rust programming language") + .await? + .press_key("Enter") + .await?; + + let html = page.wait_for_navigation().await?.content().await?; + + browser.close().await?; + handle.await?; + Ok(()) +} +``` + +The current API still lacks some functionality, but the [`Page::execute`](src/page.rs) function allows sending all `chromiumoxide_types::Command` types (see [Generated Code](README.md#generated-code)). Most `Element` and `Page` functions are basically just simplified command constructions and combinations, like `Page::pdf`: + +```rust +pub async fn pdf(&self, params: PrintToPdfParams) -> Result> { + let res = self.execute(params).await?; + Ok(base64::decode(&res.data)?) +} +``` + +If you need something else, the `Page::execute` function allows for writing your own command wrappers. PRs are very welcome if you think a meaningful command is missing a designated function. + +### Add chromiumoxide to your project + +`chromiumoxide` only supports the [`tokio`](https://github.com/tokio-rs/tokio) runtime. + +## Generated Code + +The [`chromiumoxide_pdl`](chromiumoxide_pdl) crate contains a [PDL parser](chromiumoxide_pdl/src/pdl/parser.rs), which is a rust rewrite of a [python script in the chromium source tree](https://chromium.googlesource.com/deps/inspector_protocol/+/refs/heads/master/pdl.py) and a [`Generator`](chromiumoxide_pdl/src/build/generator.rs) that turns the parsed PDL files into rust code. The [`chromiumoxide_cdp`](chromiumoxide_cdp) crate only purpose is to invoke the generator during its [build process](chromiumoxide_cdp/build.rs) and [include the generated output](chromiumoxide_cdp/src/lib.rs) before compiling the crate itself. This separation is done merely because the generated output is ~60K lines of rust code (not including all the proc macro expansions). So expect the compiling to take some time. +The generator can be configured and used independently, see [chromiumoxide_cdp/build.rs](chromiumoxide_cdp/build.rs). + +Every chrome pdl domain is put in its own rust module, the types for the page domain of the browser_protocol are in `chromiumoxide_cdp::cdp::browser_protocol::page`, the runtime domain of the js_protocol in `chromiumoxide_cdp::cdp::js_protocol::runtime` and so on. + +[vanilla.aslushnikov.com](https://vanilla.aslushnikov.com/) is a great resource to browse all the types defined in the pdl files. This site displays `Command` types as defined in the pdl files as `Method`. `chromiumoxid` sticks to the `Command` nomenclature. So for everything that is defined as a command type in the pdl (=marked as `Method` on [vanilla.aslushnikov.com](https://vanilla.aslushnikov.com/)) `chromiumoxide` contains a type for command and a designated type for the return type. For every command there is a `Params` type with builder support (`Params::builder()`) and its corresponding return type: `Returns`. All commands share an implementation of the `chromiumoxide_types::Command` trait. +All Events are bundled in single enum (`CdpEvent`) + +## Fetcher + +By default `chromiumoxide` will try to find an installed version of chromium on the computer it runs on. +It is possible to download and install one automatically for some platforms using the `fetcher` feature. + +You need to enable either the `rustls` or the `native-tls` feature and the `zip0` or `zip8` feature to allow the fetcher to download binaries. + +```rust +use std::path::Path; + +use futures::StreamExt; +use chromiumoxide::browser::{BrowserConfig}; +use chromiumoxide::fetcher::{BrowserFetcher, BrowserFetcherOptions}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let download_path = Path::new("./download"); + tokio::fs::create_dir_all(&download_path).await?; + let fetcher = BrowserFetcher::new( + BrowserFetcherOptions::builder() + .with_path(&download_path) + .build()?, + ); + let info = fetcher.fetch().await?; + + let config = BrowserConfig::builder() + .chrome_executable(info.executable_path) + .build()?, +} +``` + +## Known Issues + +- The rust files generated for the PDL files in [chromiumoxide_cdp](./chromiumoxide_cdp) don't compile when support for experimental types is manually turned off (`export CDP_NO_EXPERIMENTAL=true`). This is because the use of some experimental pdl types in the `*.pdl` files themselves are not marked as experimental. + +## Troubleshooting + +Q: A new chromium instance is being launched but then times out. + +A: Check that your chromium language settings are set to English. `chromiumoxide` tries to parse the debugging port from the chromium process output and that is limited to english. + +## License + +Licensed under either of these: + +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or + ) + +## References + +- [chromedp](https://github.com/chromedp/chromedp) +- [rust-headless-chrome](https://github.com/atroche/rust-headless-chrome) which the launch config, `KeyDefinition` and typing support among others is taken from. +- [puppeteer](https://github.com/puppeteer/puppeteer) diff --git a/vendor/chromiumoxide_cdp/pdl/browser_protocol.pdl b/vendor/chromiumoxide_cdp/pdl/browser_protocol.pdl new file mode 100644 index 0000000..795d9cd --- /dev/null +++ b/vendor/chromiumoxide_cdp/pdl/browser_protocol.pdl @@ -0,0 +1,57 @@ +# Copyright 2017 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Contributing to Chrome DevTools Protocol: https://goo.gle/devtools-contribution-guide-cdp + +version + major 1 + minor 3 + +include domains/Accessibility.pdl +include domains/Animation.pdl +include domains/Audits.pdl +include domains/Autofill.pdl +include domains/BackgroundService.pdl +include domains/BluetoothEmulation.pdl +include domains/Browser.pdl +include domains/CSS.pdl +include domains/CacheStorage.pdl +include domains/Cast.pdl +include domains/DOM.pdl +include domains/DOMDebugger.pdl +include domains/DOMSnapshot.pdl +include domains/DOMStorage.pdl +include domains/DeviceAccess.pdl +include domains/DeviceOrientation.pdl +include domains/Emulation.pdl +include domains/EventBreakpoints.pdl +include domains/Extensions.pdl +include domains/FedCm.pdl +include domains/Fetch.pdl +include domains/FileSystem.pdl +include domains/HeadlessExperimental.pdl +include domains/IO.pdl +include domains/IndexedDB.pdl +include domains/Input.pdl +include domains/Inspector.pdl +include domains/LayerTree.pdl +include domains/Log.pdl +include domains/Media.pdl +include domains/Memory.pdl +include domains/Network.pdl +include domains/Overlay.pdl +include domains/PWA.pdl +include domains/Page.pdl +include domains/Performance.pdl +include domains/PerformanceTimeline.pdl +include domains/Preload.pdl +include domains/Security.pdl +include domains/ServiceWorker.pdl +include domains/Storage.pdl +include domains/SystemInfo.pdl +include domains/Target.pdl +include domains/Tethering.pdl +include domains/Tracing.pdl +include domains/WebAudio.pdl +include domains/WebAuthn.pdl diff --git a/vendor/chromiumoxide_cdp/pdl/js_protocol.pdl b/vendor/chromiumoxide_cdp/pdl/js_protocol.pdl new file mode 100644 index 0000000..bc86332 --- /dev/null +++ b/vendor/chromiumoxide_cdp/pdl/js_protocol.pdl @@ -0,0 +1,1843 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +version + major 1 + minor 3 + +# This domain is deprecated - use Runtime or Log instead. +deprecated domain Console + depends on Runtime + + # Console message. + type ConsoleMessage extends object + properties + # Message source. + enum source + xml + javascript + network + console-api + storage + appcache + rendering + security + other + deprecation + worker + # Message severity. + enum level + log + warning + error + debug + info + # Message text. + string text + # URL of the message origin. + optional string url + # Line number in the resource that generated this message (1-based). + optional integer line + # Column number in the resource that generated this message (1-based). + optional integer column + + # Does nothing. + command clearMessages + + # Disables console domain, prevents further console messages from being reported to the client. + command disable + + # Enables console domain, sends the messages collected so far to the client by means of the + # `messageAdded` notification. + command enable + + # Issued when new console message is added. + event messageAdded + parameters + # Console message that has been added. + ConsoleMessage message + +# Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing +# breakpoints, stepping through execution, exploring stack traces, etc. +domain Debugger + depends on Runtime + + # Breakpoint identifier. + type BreakpointId extends string + + # Call frame identifier. + type CallFrameId extends string + + # Location in the source code. + type Location extends object + properties + # Script identifier as reported in the `Debugger.scriptParsed`. + Runtime.ScriptId scriptId + # Line number in the script (0-based). + integer lineNumber + # Column number in the script (0-based). + optional integer columnNumber + + # Location in the source code. + experimental type ScriptPosition extends object + properties + integer lineNumber + integer columnNumber + + # Location range within one script. + experimental type LocationRange extends object + properties + Runtime.ScriptId scriptId + ScriptPosition start + ScriptPosition end + + # JavaScript call frame. Array of call frames form the call stack. + type CallFrame extends object + properties + # Call frame identifier. This identifier is only valid while the virtual machine is paused. + CallFrameId callFrameId + # Name of the JavaScript function called on this call frame. + string functionName + # Location in the source code. + optional Location functionLocation + # Location in the source code. + Location location + # JavaScript script name or url. + # Deprecated in favor of using the `location.scriptId` to resolve the URL via a previously + # sent `Debugger.scriptParsed` event. + deprecated string url + # Scope chain for this call frame. + array of Scope scopeChain + # `this` object for this call frame. + Runtime.RemoteObject this + # The value being returned, if the function is at return point. + optional Runtime.RemoteObject returnValue + # Valid only while the VM is paused and indicates whether this frame + # can be restarted or not. Note that a `true` value here does not + # guarantee that Debugger#restartFrame with this CallFrameId will be + # successful, but it is very likely. + experimental optional boolean canBeRestarted + + # Scope description. + type Scope extends object + properties + # Scope type. + enum type + global + local + with + closure + catch + block + script + eval + module + wasm-expression-stack + # Object representing the scope. For `global` and `with` scopes it represents the actual + # object; for the rest of the scopes, it is artificial transient object enumerating scope + # variables as its properties. + Runtime.RemoteObject object + optional string name + # Location in the source code where scope starts + optional Location startLocation + # Location in the source code where scope ends + optional Location endLocation + + # Search match for resource. + type SearchMatch extends object + properties + # Line number in resource content. + number lineNumber + # Line with match content. + string lineContent + + type BreakLocation extends object + properties + # Script identifier as reported in the `Debugger.scriptParsed`. + Runtime.ScriptId scriptId + # Line number in the script (0-based). + integer lineNumber + # Column number in the script (0-based). + optional integer columnNumber + optional enum type + debuggerStatement + call + return + + # Continues execution until specific location is reached. + command continueToLocation + parameters + # Location to continue to. + Location location + optional enum targetCallFrames + any + current + + # Disables debugger for given page. + command disable + + # Enables debugger for the given page. Clients should not assume that the debugging has been + # enabled until the result for this command is received. + command enable + parameters + # The maximum size in bytes of collected scripts (not referenced by other heap objects) + # the debugger can hold. Puts no limit if parameter is omitted. + experimental optional number maxScriptsCacheSize + returns + # Unique identifier of the debugger. + experimental Runtime.UniqueDebuggerId debuggerId + + # Evaluates expression on a given call frame. + command evaluateOnCallFrame + parameters + # Call frame identifier to evaluate on. + CallFrameId callFrameId + # Expression to evaluate. + string expression + # String object group name to put result into (allows rapid releasing resulting object handles + # using `releaseObjectGroup`). + optional string objectGroup + # Specifies whether command line API should be available to the evaluated expression, defaults + # to false. + optional boolean includeCommandLineAPI + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Whether the result is expected to be a JSON object that should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + experimental optional boolean generatePreview + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + optional boolean throwOnSideEffect + # Terminate execution after timing out (number of milliseconds). + experimental optional Runtime.TimeDelta timeout + returns + # Object wrapper for the evaluation result. + Runtime.RemoteObject result + # Exception details. + optional Runtime.ExceptionDetails exceptionDetails + + # Returns possible locations for breakpoint. scriptId in start and end range locations should be + # the same. + command getPossibleBreakpoints + parameters + # Start of range to search possible breakpoint locations in. + Location start + # End of range to search possible breakpoint locations in (excluding). When not specified, end + # of scripts is used as end of range. + optional Location end + # Only consider locations which are in the same (non-nested) function as start. + optional boolean restrictToFunction + returns + # List of the possible breakpoint locations. + array of BreakLocation locations + + # Returns source for the script with given id. + command getScriptSource + parameters + # Id of the script to get source for. + Runtime.ScriptId scriptId + returns + # Script source (empty in case of Wasm bytecode). + string scriptSource + # Wasm bytecode. + optional binary bytecode + + experimental type WasmDisassemblyChunk extends object + properties + # The next chunk of disassembled lines. + array of string lines + # The bytecode offsets describing the start of each line. + array of integer bytecodeOffsets + + experimental command disassembleWasmModule + parameters + # Id of the script to disassemble + Runtime.ScriptId scriptId + returns + # For large modules, return a stream from which additional chunks of + # disassembly can be read successively. + optional string streamId + # The total number of lines in the disassembly text. + integer totalNumberOfLines + # The offsets of all function bodies, in the format [start1, end1, + # start2, end2, ...] where all ends are exclusive. + array of integer functionBodyOffsets + # The first chunk of disassembly. + WasmDisassemblyChunk chunk + + # Disassemble the next chunk of lines for the module corresponding to the + # stream. If disassembly is complete, this API will invalidate the streamId + # and return an empty chunk. Any subsequent calls for the now invalid stream + # will return errors. + experimental command nextWasmDisassemblyChunk + parameters + string streamId + returns + # The next chunk of disassembly. + WasmDisassemblyChunk chunk + + # This command is deprecated. Use getScriptSource instead. + deprecated command getWasmBytecode + parameters + # Id of the Wasm script to get source for. + Runtime.ScriptId scriptId + returns + # Script source. + binary bytecode + + # Returns stack trace with given `stackTraceId`. + experimental command getStackTrace + parameters + Runtime.StackTraceId stackTraceId + returns + Runtime.StackTrace stackTrace + + # Stops on the next JavaScript statement. + command pause + + experimental deprecated command pauseOnAsyncCall + parameters + # Debugger will pause when async call with given stack trace is started. + Runtime.StackTraceId parentStackTraceId + + # Removes JavaScript breakpoint. + command removeBreakpoint + parameters + BreakpointId breakpointId + + # Restarts particular call frame from the beginning. The old, deprecated + # behavior of `restartFrame` is to stay paused and allow further CDP commands + # after a restart was scheduled. This can cause problems with restarting, so + # we now continue execution immediatly after it has been scheduled until we + # reach the beginning of the restarted frame. + # + # To stay back-wards compatible, `restartFrame` now expects a `mode` + # parameter to be present. If the `mode` parameter is missing, `restartFrame` + # errors out. + # + # The various return values are deprecated and `callFrames` is always empty. + # Use the call frames from the `Debugger#paused` events instead, that fires + # once V8 pauses at the beginning of the restarted function. + command restartFrame + parameters + # Call frame identifier to evaluate on. + CallFrameId callFrameId + # The `mode` parameter must be present and set to 'StepInto', otherwise + # `restartFrame` will error out. + experimental optional enum mode + # Pause at the beginning of the restarted function + StepInto + returns + # New stack trace. + deprecated array of CallFrame callFrames + # Async stack trace, if any. + deprecated optional Runtime.StackTrace asyncStackTrace + # Async stack trace, if any. + deprecated optional Runtime.StackTraceId asyncStackTraceId + + # Resumes JavaScript execution. + command resume + parameters + # Set to true to terminate execution upon resuming execution. In contrast + # to Runtime.terminateExecution, this will allows to execute further + # JavaScript (i.e. via evaluation) until execution of the paused code + # is actually resumed, at which point termination is triggered. + # If execution is currently not paused, this parameter has no effect. + optional boolean terminateOnResume + + # Searches for given string in script content. + command searchInContent + parameters + # Id of the script to search in. + Runtime.ScriptId scriptId + # String to search for. + string query + # If true, search is case sensitive. + optional boolean caseSensitive + # If true, treats string parameter as regex. + optional boolean isRegex + returns + # List of search matches. + array of SearchMatch result + + # Enables or disables async call stacks tracking. + command setAsyncCallStackDepth + parameters + # Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async + # call stacks (default). + integer maxDepth + + # Replace previous blackbox execution contexts with passed ones. Forces backend to skip + # stepping/pausing in scripts in these execution contexts. VM will try to leave blackboxed script by + # performing 'step in' several times, finally resorting to 'step out' if unsuccessful. + experimental command setBlackboxExecutionContexts + parameters + # Array of execution context unique ids for the debugger to ignore. + array of string uniqueIds + + # Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in + # scripts with url matching one of the patterns. VM will try to leave blackboxed script by + # performing 'step in' several times, finally resorting to 'step out' if unsuccessful. + experimental command setBlackboxPatterns + parameters + # Array of regexps that will be used to check script url for blackbox state. + array of string patterns + # If true, also ignore scripts with no source url. + optional boolean skipAnonymous + + # Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted + # scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. + # Positions array contains positions where blackbox state is changed. First interval isn't + # blackboxed. Array should be sorted. + experimental command setBlackboxedRanges + parameters + # Id of the script. + Runtime.ScriptId scriptId + array of ScriptPosition positions + + # Sets JavaScript breakpoint at a given location. + command setBreakpoint + parameters + # Location to set breakpoint in. + Location location + # Expression to use as a breakpoint condition. When specified, debugger will only stop on the + # breakpoint if this expression evaluates to true. + optional string condition + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + # Location this breakpoint resolved into. + Location actualLocation + + # Sets instrumentation breakpoint. + command setInstrumentationBreakpoint + parameters + # Instrumentation name. + enum instrumentation + beforeScriptExecution + beforeScriptWithSourceMapExecution + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + + # Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this + # command is issued, all existing parsed scripts will have breakpoints resolved and returned in + # `locations` property. Further matching script parsing will result in subsequent + # `breakpointResolved` events issued. This logical breakpoint will survive page reloads. + command setBreakpointByUrl + parameters + # Line number to set breakpoint at. + integer lineNumber + # URL of the resources to set breakpoint on. + optional string url + # Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or + # `urlRegex` must be specified. + optional string urlRegex + # Script hash of the resources to set breakpoint on. + optional string scriptHash + # Offset in the line to set breakpoint at. + optional integer columnNumber + # Expression to use as a breakpoint condition. When specified, debugger will only stop on the + # breakpoint if this expression evaluates to true. + optional string condition + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + # List of the locations this breakpoint resolved into upon addition. + array of Location locations + + # Sets JavaScript breakpoint before each call to the given function. + # If another function was created from the same source as a given one, + # calling it will also trigger the breakpoint. + experimental command setBreakpointOnFunctionCall + parameters + # Function object id. + Runtime.RemoteObjectId objectId + # Expression to use as a breakpoint condition. When specified, debugger will + # stop on the breakpoint if this expression evaluates to true. + optional string condition + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + + # Activates / deactivates all breakpoints on the page. + command setBreakpointsActive + parameters + # New value for breakpoints active state. + boolean active + + # Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions, + # or caught exceptions, no exceptions. Initial pause on exceptions state is `none`. + command setPauseOnExceptions + parameters + # Pause on exceptions mode. + enum state + none + caught + uncaught + all + + # Changes return value in top frame. Available only at return break position. + experimental command setReturnValue + parameters + # New return value. + Runtime.CallArgument newValue + + # Edits JavaScript source live. + # + # In general, functions that are currently on the stack can not be edited with + # a single exception: If the edited function is the top-most stack frame and + # that is the only activation of that function on the stack. In this case + # the live edit will be successful and a `Debugger.restartFrame` for the + # top-most function is automatically triggered. + command setScriptSource + parameters + # Id of the script to edit. + Runtime.ScriptId scriptId + # New content of the script. + string scriptSource + # If true the change will not actually be applied. Dry run may be used to get result + # description without actually modifying the code. + optional boolean dryRun + # If true, then `scriptSource` is allowed to change the function on top of the stack + # as long as the top-most stack frame is the only activation of that function. + experimental optional boolean allowTopFrameEditing + returns + # New stack trace in case editing has happened while VM was stopped. + deprecated optional array of CallFrame callFrames + # Whether current call stack was modified after applying the changes. + deprecated optional boolean stackChanged + # Async stack trace, if any. + deprecated optional Runtime.StackTrace asyncStackTrace + # Async stack trace, if any. + deprecated optional Runtime.StackTraceId asyncStackTraceId + # Whether the operation was successful or not. Only `Ok` denotes a + # successful live edit while the other enum variants denote why + # the live edit failed. + experimental enum status + Ok + CompileError + BlockedByActiveGenerator + BlockedByActiveFunction + BlockedByTopLevelEsModuleChange + # Exception details if any. Only present when `status` is `CompileError`. + optional Runtime.ExceptionDetails exceptionDetails + + # Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc). + command setSkipAllPauses + parameters + # New value for skip pauses state. + boolean skip + + # Changes value of variable in a callframe. Object-based scopes are not supported and must be + # mutated manually. + command setVariableValue + parameters + # 0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' + # scope types are allowed. Other scopes could be manipulated manually. + integer scopeNumber + # Variable name. + string variableName + # New variable value. + Runtime.CallArgument newValue + # Id of callframe that holds variable. + CallFrameId callFrameId + + # Steps into the function call. + command stepInto + parameters + # Debugger will pause on the execution of the first async task which was scheduled + # before next pause. + experimental optional boolean breakOnAsyncCall + # The skipList specifies location ranges that should be skipped on step into. + experimental optional array of LocationRange skipList + + # Steps out of the function call. + command stepOut + + # Steps over the statement. + command stepOver + parameters + # The skipList specifies location ranges that should be skipped on step over. + experimental optional array of LocationRange skipList + + # Fired when breakpoint is resolved to an actual script and location. + # Deprecated in favor of `resolvedBreakpoints` in the `scriptParsed` event. + deprecated event breakpointResolved + parameters + # Breakpoint unique identifier. + BreakpointId breakpointId + # Actual breakpoint location. + Location location + + # Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria. + event paused + parameters + # Call stack the virtual machine stopped on. + array of CallFrame callFrames + # Pause reason. + enum reason + ambiguous + assert + CSPViolation + debugCommand + DOM + EventListener + exception + instrumentation + OOM + other + promiseRejection + XHR + step + # Object containing break-specific auxiliary properties. + optional object data + # Hit breakpoints IDs + optional array of string hitBreakpoints + # Async stack trace, if any. + optional Runtime.StackTrace asyncStackTrace + # Async stack trace, if any. + experimental optional Runtime.StackTraceId asyncStackTraceId + # Never present, will be removed. + experimental deprecated optional Runtime.StackTraceId asyncCallStackTraceId + + # Fired when the virtual machine resumed execution. + event resumed + + # Enum of possible script languages. + type ScriptLanguage extends string + enum + JavaScript + WebAssembly + + # Debug symbols available for a wasm script. + type DebugSymbols extends object + properties + # Type of the debug symbols. + enum type + SourceMap + EmbeddedDWARF + ExternalDWARF + # URL of the external symbol source. + optional string externalURL + + type ResolvedBreakpoint extends object + properties + # Breakpoint unique identifier. + BreakpointId breakpointId + # Actual breakpoint location. + Location location + + # Fired when virtual machine fails to parse the script. + event scriptFailedToParse + parameters + # Identifier of the script parsed. + Runtime.ScriptId scriptId + # URL or name of the script parsed (if any). + string url + # Line offset of the script within the resource with given URL (for script tags). + integer startLine + # Column offset of the script within the resource with given URL. + integer startColumn + # Last line of the script. + integer endLine + # Length of the last line of the script. + integer endColumn + # Specifies script creation context. + Runtime.ExecutionContextId executionContextId + # Content hash of the script, SHA-256. + string hash + # For Wasm modules, the content of the `build_id` custom section. For JavaScript the `debugId` magic comment. + string buildId + # Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} + optional object executionContextAuxData + # URL of source map associated with script (if any). + optional string sourceMapURL + # True, if this script has sourceURL. + optional boolean hasSourceURL + # True, if this script is ES6 module. + optional boolean isModule + # This script length. + optional integer length + # JavaScript top stack frame of where the script parsed event was triggered if available. + experimental optional Runtime.StackTrace stackTrace + # If the scriptLanguage is WebAssembly, the code section offset in the module. + experimental optional integer codeOffset + # The language of the script. + experimental optional Debugger.ScriptLanguage scriptLanguage + # The name the embedder supplied for this script. + experimental optional string embedderName + + # Fired when virtual machine parses script. This event is also fired for all known and uncollected + # scripts upon enabling debugger. + event scriptParsed + parameters + # Identifier of the script parsed. + Runtime.ScriptId scriptId + # URL or name of the script parsed (if any). + string url + # Line offset of the script within the resource with given URL (for script tags). + integer startLine + # Column offset of the script within the resource with given URL. + integer startColumn + # Last line of the script. + integer endLine + # Length of the last line of the script. + integer endColumn + # Specifies script creation context. + Runtime.ExecutionContextId executionContextId + # Content hash of the script, SHA-256. + string hash + # For Wasm modules, the content of the `build_id` custom section. For JavaScript the `debugId` magic comment. + string buildId + # Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} + optional object executionContextAuxData + # True, if this script is generated as a result of the live edit operation. + experimental optional boolean isLiveEdit + # URL of source map associated with script (if any). + optional string sourceMapURL + # True, if this script has sourceURL. + optional boolean hasSourceURL + # True, if this script is ES6 module. + optional boolean isModule + # This script length. + optional integer length + # JavaScript top stack frame of where the script parsed event was triggered if available. + experimental optional Runtime.StackTrace stackTrace + # If the scriptLanguage is WebAssembly, the code section offset in the module. + experimental optional integer codeOffset + # The language of the script. + experimental optional Debugger.ScriptLanguage scriptLanguage + # If the scriptLanguage is WebAssembly, the source of debug symbols for the module. + experimental optional array of Debugger.DebugSymbols debugSymbols + # The name the embedder supplied for this script. + experimental optional string embedderName + # The list of set breakpoints in this script if calls to `setBreakpointByUrl` + # matches this script's URL or hash. Clients that use this list can ignore the + # `breakpointResolved` event. They are equivalent. + experimental optional array of ResolvedBreakpoint resolvedBreakpoints + +experimental domain HeapProfiler + depends on Runtime + + # Heap snapshot object id. + type HeapSnapshotObjectId extends string + + # Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes. + type SamplingHeapProfileNode extends object + properties + # Function location. + Runtime.CallFrame callFrame + # Allocations size in bytes for the node excluding children. + number selfSize + # Node id. Ids are unique across all profiles collected between startSampling and stopSampling. + integer id + # Child nodes. + array of SamplingHeapProfileNode children + + # A single sample from a sampling profile. + type SamplingHeapProfileSample extends object + properties + # Allocation size in bytes attributed to the sample. + number size + # Id of the corresponding profile tree node. + integer nodeId + # Time-ordered sample ordinal number. It is unique across all profiles retrieved + # between startSampling and stopSampling. + number ordinal + + # Sampling profile. + type SamplingHeapProfile extends object + properties + SamplingHeapProfileNode head + array of SamplingHeapProfileSample samples + + # Enables console to refer to the node with given id via $x (see Command Line API for more details + # $x functions). + command addInspectedHeapObject + parameters + # Heap snapshot object id to be accessible by means of $x command line API. + HeapSnapshotObjectId heapObjectId + + command collectGarbage + + command disable + + command enable + + command getHeapObjectId + parameters + # Identifier of the object to get heap object id for. + Runtime.RemoteObjectId objectId + returns + # Id of the heap snapshot object corresponding to the passed remote object id. + HeapSnapshotObjectId heapSnapshotObjectId + + command getObjectByHeapObjectId + parameters + HeapSnapshotObjectId objectId + # Symbolic group name that can be used to release multiple objects. + optional string objectGroup + returns + # Evaluation result. + Runtime.RemoteObject result + + command getSamplingProfile + returns + # Return the sampling profile being collected. + SamplingHeapProfile profile + + command startSampling + parameters + # Average sample interval in bytes. Poisson distribution is used for the intervals. The + # default value is 32768 bytes. + optional number samplingInterval + # Maximum stack depth. The default value is 128. + optional number stackDepth + # By default, the sampling heap profiler reports only objects which are + # still alive when the profile is returned via getSamplingProfile or + # stopSampling, which is useful for determining what functions contribute + # the most to steady-state memory usage. This flag instructs the sampling + # heap profiler to also include information about objects discarded by + # major GC, which will show which functions cause large temporary memory + # usage or long GC pauses. + optional boolean includeObjectsCollectedByMajorGC + # By default, the sampling heap profiler reports only objects which are + # still alive when the profile is returned via getSamplingProfile or + # stopSampling, which is useful for determining what functions contribute + # the most to steady-state memory usage. This flag instructs the sampling + # heap profiler to also include information about objects discarded by + # minor GC, which is useful when tuning a latency-sensitive application + # for minimal GC activity. + optional boolean includeObjectsCollectedByMinorGC + + command startTrackingHeapObjects + parameters + optional boolean trackAllocations + + command stopSampling + returns + # Recorded sampling heap profile. + SamplingHeapProfile profile + + command stopTrackingHeapObjects + parameters + # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken + # when the tracking is stopped. + optional boolean reportProgress + # Deprecated in favor of `exposeInternals`. + deprecated optional boolean treatGlobalObjectsAsRoots + # If true, numerical values are included in the snapshot + optional boolean captureNumericValue + # If true, exposes internals of the snapshot. + experimental optional boolean exposeInternals + + command takeHeapSnapshot + parameters + # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken. + optional boolean reportProgress + # If true, a raw snapshot without artificial roots will be generated. + # Deprecated in favor of `exposeInternals`. + deprecated optional boolean treatGlobalObjectsAsRoots + # If true, numerical values are included in the snapshot + optional boolean captureNumericValue + # If true, exposes internals of the snapshot. + experimental optional boolean exposeInternals + + event addHeapSnapshotChunk + parameters + string chunk + + # If heap objects tracking has been started then backend may send update for one or more fragments + event heapStatsUpdate + parameters + # An array of triplets. Each triplet describes a fragment. The first integer is the fragment + # index, the second integer is a total count of objects for the fragment, the third integer is + # a total size of the objects for the fragment. + array of integer statsUpdate + + # If heap objects tracking has been started then backend regularly sends a current value for last + # seen object id and corresponding timestamp. If the were changes in the heap since last event + # then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event. + event lastSeenObjectId + parameters + integer lastSeenObjectId + number timestamp + + event reportHeapSnapshotProgress + parameters + integer done + integer total + optional boolean finished + + event resetProfiles + +domain Profiler + depends on Runtime + depends on Debugger + + # Profile node. Holds callsite information, execution statistics and child nodes. + type ProfileNode extends object + properties + # Unique id of the node. + integer id + # Function location. + Runtime.CallFrame callFrame + # Number of samples where this node was on top of the call stack. + optional integer hitCount + # Child node ids. + optional array of integer children + # The reason of being not optimized. The function may be deoptimized or marked as don't + # optimize. + optional string deoptReason + # An array of source position ticks. + optional array of PositionTickInfo positionTicks + + # Profile. + type Profile extends object + properties + # The list of profile nodes. First item is the root node. + array of ProfileNode nodes + # Profiling start timestamp in microseconds. + number startTime + # Profiling end timestamp in microseconds. + number endTime + # Ids of samples top nodes. + optional array of integer samples + # Time intervals between adjacent samples in microseconds. The first delta is relative to the + # profile startTime. + optional array of integer timeDeltas + + # Specifies a number of samples attributed to a certain source position. + type PositionTickInfo extends object + properties + # Source line number (1-based). + integer line + # Number of samples attributed to the source line. + integer ticks + + # Coverage data for a source range. + type CoverageRange extends object + properties + # JavaScript script source offset for the range start. + integer startOffset + # JavaScript script source offset for the range end. + integer endOffset + # Collected execution count of the source range. + integer count + + # Coverage data for a JavaScript function. + type FunctionCoverage extends object + properties + # JavaScript function name. + string functionName + # Source ranges inside the function with coverage data. + array of CoverageRange ranges + # Whether coverage data for this function has block granularity. + boolean isBlockCoverage + + # Coverage data for a JavaScript script. + type ScriptCoverage extends object + properties + # JavaScript script id. + Runtime.ScriptId scriptId + # JavaScript script name or url. + string url + # Functions contained in the script that has coverage data. + array of FunctionCoverage functions + + command disable + + command enable + + # Collect coverage data for the current isolate. The coverage data may be incomplete due to + # garbage collection. + command getBestEffortCoverage + returns + # Coverage data for the current isolate. + array of ScriptCoverage result + + # Changes CPU profiler sampling interval. Must be called before CPU profiles recording started. + command setSamplingInterval + parameters + # New sampling interval in microseconds. + integer interval + + command start + + # Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code + # coverage may be incomplete. Enabling prevents running optimized code and resets execution + # counters. + command startPreciseCoverage + parameters + # Collect accurate call counts beyond simple 'covered' or 'not covered'. + optional boolean callCount + # Collect block-based coverage. + optional boolean detailed + # Allow the backend to send updates on its own initiative + optional boolean allowTriggeredUpdates + returns + # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. + number timestamp + + command stop + returns + # Recorded profile. + Profile profile + + # Disable precise code coverage. Disabling releases unnecessary execution count records and allows + # executing optimized code. + command stopPreciseCoverage + + # Collect coverage data for the current isolate, and resets execution counters. Precise code + # coverage needs to have started. + command takePreciseCoverage + returns + # Coverage data for the current isolate. + array of ScriptCoverage result + # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. + number timestamp + + event consoleProfileFinished + parameters + string id + # Location of console.profileEnd(). + Debugger.Location location + Profile profile + # Profile title passed as an argument to console.profile(). + optional string title + + # Sent when new profile recording is started using console.profile() call. + event consoleProfileStarted + parameters + string id + # Location of console.profile(). + Debugger.Location location + # Profile title passed as an argument to console.profile(). + optional string title + + # Reports coverage delta since the last poll (either from an event like this, or from + # `takePreciseCoverage` for the current isolate. May only be sent if precise code + # coverage has been started. This event can be trigged by the embedder to, for example, + # trigger collection of coverage data immediately at a certain point in time. + experimental event preciseCoverageDeltaUpdate + parameters + # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. + number timestamp + # Identifier for distinguishing coverage events. + string occasion + # Coverage data for the current isolate. + array of ScriptCoverage result + +# Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. +# Evaluation results are returned as mirror object that expose object type, string representation +# and unique identifier that can be used for further object reference. Original objects are +# maintained in memory unless they are either explicitly released or are released along with the +# other objects in their object group. +domain Runtime + + # Unique script identifier. + type ScriptId extends string + + # Represents options for serialization. Overrides `generatePreview` and `returnByValue`. + type SerializationOptions extends object + properties + enum serialization + # Whether the result should be deep-serialized. The result is put into + # `deepSerializedValue` and `ObjectId` is provided. + deep + # Whether the result is expected to be a JSON object which should be sent by value. + # The result is put either into `value` or into `unserializableValue`. Synonym of + # `returnByValue: true`. Overrides `returnByValue`. + json + # Only remote object id is put in the result. Same bahaviour as if no + # `serializationOptions`, `generatePreview` nor `returnByValue` are provided. + idOnly + + # Deep serialization depth. Default is full depth. Respected only in `deep` serialization mode. + optional integer maxDepth + + # Embedder-specific parameters. For example if connected to V8 in Chrome these control DOM + # serialization via `maxNodeDepth: integer` and `includeShadowTree: "none" | "open" | "all"`. + # Values can be only of type string or integer. + optional object additionalParameters + + # Represents deep serialized value. + type DeepSerializedValue extends object + properties + enum type + undefined + null + string + number + boolean + bigint + regexp + date + symbol + array + object + function + map + set + weakmap + weakset + error + proxy + promise + typedarray + arraybuffer + node + window + generator + optional any value + optional string objectId + # Set if value reference met more then once during serialization. In such + # case, value is provided only to one of the serialized values. Unique + # per value in the scope of one CDP call. + optional integer weakLocalObjectReference + + # Unique object identifier. + type RemoteObjectId extends string + + # Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`, + # `-Infinity`, and bigint literals. + type UnserializableValue extends string + + # Mirror object referencing original JavaScript object. + type RemoteObject extends object + properties + # Object type. + enum type + object + function + undefined + string + number + boolean + symbol + bigint + # Object subtype hint. Specified for `object` type values only. + # NOTE: If you change anything here, make sure to also update + # `subtype` in `ObjectPreview` and `PropertyPreview` below. + optional enum subtype + array + null + node + regexp + date + map + set + weakmap + weakset + iterator + generator + error + proxy + promise + typedarray + arraybuffer + dataview + webassemblymemory + wasmvalue + # blink's subtypes. + trustedtype + # Object class (constructor) name. Specified for `object` type values only. + optional string className + # Remote object value in case of primitive values or JSON values (if it was requested). + optional any value + # Primitive value which can not be JSON-stringified does not have `value`, but gets this + # property. + optional UnserializableValue unserializableValue + # String representation of the object. + optional string description + # Deep serialized value. + experimental optional DeepSerializedValue deepSerializedValue + # Unique object identifier (for non-primitive values). + optional RemoteObjectId objectId + # Preview containing abbreviated property values. Specified for `object` type values only. + experimental optional ObjectPreview preview + experimental optional CustomPreview customPreview + + experimental type CustomPreview extends object + properties + # The JSON-stringified result of formatter.header(object, config) call. + # It contains json ML array that represents RemoteObject. + string header + # If formatter returns true as a result of formatter.hasBody call then bodyGetterId will + # contain RemoteObjectId for the function that returns result of formatter.body(object, config) call. + # The result value is json ML array. + optional RemoteObjectId bodyGetterId + + # Object containing abbreviated remote object value. + experimental type ObjectPreview extends object + properties + # Object type. + enum type + object + function + undefined + string + number + boolean + symbol + bigint + # Object subtype hint. Specified for `object` type values only. + optional enum subtype + array + null + node + regexp + date + map + set + weakmap + weakset + iterator + generator + error + proxy + promise + typedarray + arraybuffer + dataview + webassemblymemory + wasmvalue + # blink's subtypes. + trustedtype + # String representation of the object. + optional string description + # True iff some of the properties or entries of the original object did not fit. + boolean overflow + # List of the properties. + array of PropertyPreview properties + # List of the entries. Specified for `map` and `set` subtype values only. + optional array of EntryPreview entries + + experimental type PropertyPreview extends object + properties + # Property name. + string name + # Object type. Accessor means that the property itself is an accessor property. + enum type + object + function + undefined + string + number + boolean + symbol + accessor + bigint + # User-friendly property value string. + optional string value + # Nested value preview. + optional ObjectPreview valuePreview + # Object subtype hint. Specified for `object` type values only. + optional enum subtype + array + null + node + regexp + date + map + set + weakmap + weakset + iterator + generator + error + proxy + promise + typedarray + arraybuffer + dataview + webassemblymemory + wasmvalue + # blink's subtypes. + trustedtype + + experimental type EntryPreview extends object + properties + # Preview of the key. Specified for map-like collection entries. + optional ObjectPreview key + # Preview of the value. + ObjectPreview value + + # Object property descriptor. + type PropertyDescriptor extends object + properties + # Property name or symbol description. + string name + # The value associated with the property. + optional RemoteObject value + # True if the value associated with the property may be changed (data descriptors only). + optional boolean writable + # A function which serves as a getter for the property, or `undefined` if there is no getter + # (accessor descriptors only). + optional RemoteObject get + # A function which serves as a setter for the property, or `undefined` if there is no setter + # (accessor descriptors only). + optional RemoteObject set + # True if the type of this property descriptor may be changed and if the property may be + # deleted from the corresponding object. + boolean configurable + # True if this property shows up during enumeration of the properties on the corresponding + # object. + boolean enumerable + # True if the result was thrown during the evaluation. + optional boolean wasThrown + # True if the property is owned for the object. + optional boolean isOwn + # Property symbol object, if the property is of the `symbol` type. + optional RemoteObject symbol + + # Object internal property descriptor. This property isn't normally visible in JavaScript code. + type InternalPropertyDescriptor extends object + properties + # Conventional property name. + string name + # The value associated with the property. + optional RemoteObject value + + # Object private field descriptor. + experimental type PrivatePropertyDescriptor extends object + properties + # Private property name. + string name + # The value associated with the private property. + optional RemoteObject value + # A function which serves as a getter for the private property, + # or `undefined` if there is no getter (accessor descriptors only). + optional RemoteObject get + # A function which serves as a setter for the private property, + # or `undefined` if there is no setter (accessor descriptors only). + optional RemoteObject set + + # Represents function call argument. Either remote object id `objectId`, primitive `value`, + # unserializable primitive value or neither of (for undefined) them should be specified. + type CallArgument extends object + properties + # Primitive value or serializable javascript object. + optional any value + # Primitive value which can not be JSON-stringified. + optional UnserializableValue unserializableValue + # Remote object handle. + optional RemoteObjectId objectId + + # Id of an execution context. + type ExecutionContextId extends integer + + # Description of an isolated world. + type ExecutionContextDescription extends object + properties + # Unique id of the execution context. It can be used to specify in which execution context + # script evaluation should be performed. + ExecutionContextId id + # Execution context origin. + string origin + # Human readable name describing given context. + string name + # A system-unique execution context identifier. Unlike the id, this is unique across + # multiple processes, so can be reliably used to identify specific context while backend + # performs a cross-process navigation. + experimental string uniqueId + # Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string} + optional object auxData + + # Detailed information about exception (or error) that was thrown during script compilation or + # execution. + type ExceptionDetails extends object + properties + # Exception id. + integer exceptionId + # Exception text, which should be used together with exception object when available. + string text + # Line number of the exception location (0-based). + integer lineNumber + # Column number of the exception location (0-based). + integer columnNumber + # Script ID of the exception location. + optional ScriptId scriptId + # URL of the exception location, to be used when the script was not reported. + optional string url + # JavaScript stack trace if available. + optional StackTrace stackTrace + # Exception object if available. + optional RemoteObject exception + # Identifier of the context where exception happened. + optional ExecutionContextId executionContextId + # Dictionary with entries of meta data that the client associated + # with this exception, such as information about associated network + # requests, etc. + experimental optional object exceptionMetaData + + # Number of milliseconds since epoch. + type Timestamp extends number + + # Number of milliseconds. + type TimeDelta extends number + + # Stack entry for runtime errors and assertions. + type CallFrame extends object + properties + # JavaScript function name. + string functionName + # JavaScript script id. + ScriptId scriptId + # JavaScript script name or url. + string url + # JavaScript script line number (0-based). + integer lineNumber + # JavaScript script column number (0-based). + integer columnNumber + + # Call frames for assertions or error messages. + type StackTrace extends object + properties + # String label of this stack trace. For async traces this may be a name of the function that + # initiated the async call. + optional string description + # JavaScript function name. + array of CallFrame callFrames + # Asynchronous JavaScript stack trace that preceded this stack, if available. + optional StackTrace parent + # Asynchronous JavaScript stack trace that preceded this stack, if available. + experimental optional StackTraceId parentId + + # Unique identifier of current debugger. + experimental type UniqueDebuggerId extends string + + # If `debuggerId` is set stack trace comes from another debugger and can be resolved there. This + # allows to track cross-debugger calls. See `Runtime.StackTrace` and `Debugger.paused` for usages. + experimental type StackTraceId extends object + properties + string id + optional UniqueDebuggerId debuggerId + + # Add handler to promise with given promise object id. + command awaitPromise + parameters + # Identifier of the promise. + RemoteObjectId promiseObjectId + # Whether the result is expected to be a JSON object that should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + optional boolean generatePreview + returns + # Promise result. Will contain rejected value if promise was rejected. + RemoteObject result + # Exception details if stack strace is available. + optional ExceptionDetails exceptionDetails + + # Calls function with given declaration on the given object. Object group of the result is + # inherited from the target object. + command callFunctionOn + parameters + # Declaration of the function to call. + string functionDeclaration + # Identifier of the object to call function on. Either objectId or executionContextId should + # be specified. + optional RemoteObjectId objectId + # Call arguments. All call arguments must belong to the same JavaScript world as the target + # object. + optional array of CallArgument arguments + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Whether the result is expected to be a JSON object which should be sent by value. + # Can be overriden by `serializationOptions`. + optional boolean returnByValue + # Whether preview should be generated for the result. + experimental optional boolean generatePreview + # Whether execution should be treated as initiated by user in the UI. + optional boolean userGesture + # Whether execution should `await` for resulting value and return once awaited promise is + # resolved. + optional boolean awaitPromise + # Specifies execution context which global object will be used to call function on. Either + # executionContextId or objectId should be specified. + optional ExecutionContextId executionContextId + # Symbolic group name that can be used to release multiple objects. If objectGroup is not + # specified and objectId is, objectGroup will be inherited from object. + optional string objectGroup + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + experimental optional boolean throwOnSideEffect + # An alternative way to specify the execution context to call function on. + # Compared to contextId that may be reused across processes, this is guaranteed to be + # system-unique, so it can be used to prevent accidental function call + # in context different than intended (e.g. as a result of navigation across process + # boundaries). + # This is mutually exclusive with `executionContextId`. + experimental optional string uniqueContextId + # Specifies the result serialization. If provided, overrides + # `generatePreview` and `returnByValue`. + experimental optional SerializationOptions serializationOptions + + returns + # Call result. + RemoteObject result + # Exception details. + optional ExceptionDetails exceptionDetails + + # Compiles expression. + command compileScript + parameters + # Expression to compile. + string expression + # Source url to be set for the script. + string sourceURL + # Specifies whether the compiled script should be persisted. + boolean persistScript + # Specifies in which execution context to perform script run. If the parameter is omitted the + # evaluation will be performed in the context of the inspected page. + optional ExecutionContextId executionContextId + returns + # Id of the script. + optional ScriptId scriptId + # Exception details. + optional ExceptionDetails exceptionDetails + + # Disables reporting of execution contexts creation. + command disable + + # Discards collected exceptions and console API calls. + command discardConsoleEntries + + # Enables reporting of execution contexts creation by means of `executionContextCreated` event. + # When the reporting gets enabled the event will be sent immediately for each existing execution + # context. + command enable + + # Evaluates expression on global object. + command evaluate + parameters + # Expression to evaluate. + string expression + # Symbolic group name that can be used to release multiple objects. + optional string objectGroup + # Determines whether Command Line API should be available during the evaluation. + optional boolean includeCommandLineAPI + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Specifies in which execution context to perform evaluation. If the parameter is omitted the + # evaluation will be performed in the context of the inspected page. + # This is mutually exclusive with `uniqueContextId`, which offers an + # alternative way to identify the execution context that is more reliable + # in a multi-process environment. + optional ExecutionContextId contextId + # Whether the result is expected to be a JSON object that should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + experimental optional boolean generatePreview + # Whether execution should be treated as initiated by user in the UI. + optional boolean userGesture + # Whether execution should `await` for resulting value and return once awaited promise is + # resolved. + optional boolean awaitPromise + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + # This implies `disableBreaks` below. + experimental optional boolean throwOnSideEffect + # Terminate execution after timing out (number of milliseconds). + experimental optional TimeDelta timeout + # Disable breakpoints during execution. + experimental optional boolean disableBreaks + # Setting this flag to true enables `let` re-declaration and top-level `await`. + # Note that `let` variables can only be re-declared if they originate from + # `replMode` themselves. + experimental optional boolean replMode + # The Content Security Policy (CSP) for the target might block 'unsafe-eval' + # which includes eval(), Function(), setTimeout() and setInterval() + # when called with non-callable arguments. This flag bypasses CSP for this + # evaluation and allows unsafe-eval. Defaults to true. + experimental optional boolean allowUnsafeEvalBlockedByCSP + # An alternative way to specify the execution context to evaluate in. + # Compared to contextId that may be reused across processes, this is guaranteed to be + # system-unique, so it can be used to prevent accidental evaluation of the expression + # in context different than intended (e.g. as a result of navigation across process + # boundaries). + # This is mutually exclusive with `contextId`. + experimental optional string uniqueContextId + # Specifies the result serialization. If provided, overrides + # `generatePreview` and `returnByValue`. + experimental optional SerializationOptions serializationOptions + returns + # Evaluation result. + RemoteObject result + # Exception details. + optional ExceptionDetails exceptionDetails + + # Returns the isolate id. + experimental command getIsolateId + returns + # The isolate id. + string id + + # Returns the JavaScript heap usage. + # It is the total usage of the corresponding isolate not scoped to a particular Runtime. + experimental command getHeapUsage + returns + # Used JavaScript heap size in bytes. + number usedSize + # Allocated JavaScript heap size in bytes. + number totalSize + # Used size in bytes in the embedder's garbage-collected heap. + number embedderHeapUsedSize + # Size in bytes of backing storage for array buffers and external strings. + number backingStorageSize + + # Returns properties of a given object. Object group of the result is inherited from the target + # object. + command getProperties + parameters + # Identifier of the object to return properties for. + RemoteObjectId objectId + # If true, returns properties belonging only to the element itself, not to its prototype + # chain. + optional boolean ownProperties + # If true, returns accessor properties (with getter/setter) only; internal properties are not + # returned either. + experimental optional boolean accessorPropertiesOnly + # Whether preview should be generated for the results. + experimental optional boolean generatePreview + # If true, returns non-indexed properties only. + experimental optional boolean nonIndexedPropertiesOnly + returns + # Object properties. + array of PropertyDescriptor result + # Internal object properties (only of the element itself). + optional array of InternalPropertyDescriptor internalProperties + # Object private properties. + experimental optional array of PrivatePropertyDescriptor privateProperties + # Exception details. + optional ExceptionDetails exceptionDetails + + # Returns all let, const and class variables from global scope. + command globalLexicalScopeNames + parameters + # Specifies in which execution context to lookup global scope variables. + optional ExecutionContextId executionContextId + returns + array of string names + + command queryObjects + parameters + # Identifier of the prototype to return objects for. + RemoteObjectId prototypeObjectId + # Symbolic group name that can be used to release the results. + optional string objectGroup + returns + # Array with objects. + RemoteObject objects + + # Releases remote object with given id. + command releaseObject + parameters + # Identifier of the object to release. + RemoteObjectId objectId + + # Releases all remote objects that belong to a given group. + command releaseObjectGroup + parameters + # Symbolic object group name. + string objectGroup + + # Tells inspected instance to run if it was waiting for debugger to attach. + command runIfWaitingForDebugger + + # Runs script with given id in a given context. + command runScript + parameters + # Id of the script to run. + ScriptId scriptId + # Specifies in which execution context to perform script run. If the parameter is omitted the + # evaluation will be performed in the context of the inspected page. + optional ExecutionContextId executionContextId + # Symbolic group name that can be used to release multiple objects. + optional string objectGroup + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Determines whether Command Line API should be available during the evaluation. + optional boolean includeCommandLineAPI + # Whether the result is expected to be a JSON object which should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + optional boolean generatePreview + # Whether execution should `await` for resulting value and return once awaited promise is + # resolved. + optional boolean awaitPromise + returns + # Run result. + RemoteObject result + # Exception details. + optional ExceptionDetails exceptionDetails + + # Enables or disables async call stacks tracking. + command setAsyncCallStackDepth + redirect Debugger + parameters + # Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async + # call stacks (default). + integer maxDepth + + experimental command setCustomObjectFormatterEnabled + parameters + boolean enabled + + experimental command setMaxCallStackSizeToCapture + parameters + integer size + + # Terminate current or next JavaScript execution. + # Will cancel the termination when the outer-most script execution ends. + experimental command terminateExecution + + # If executionContextId is empty, adds binding with the given name on the + # global objects of all inspected contexts, including those created later, + # bindings survive reloads. + # Binding function takes exactly one argument, this argument should be string, + # in case of any other input, function throws an exception. + # Each binding function call produces Runtime.bindingCalled notification. + command addBinding + parameters + string name + # If specified, the binding would only be exposed to the specified + # execution context. If omitted and `executionContextName` is not set, + # the binding is exposed to all execution contexts of the target. + # This parameter is mutually exclusive with `executionContextName`. + # Deprecated in favor of `executionContextName` due to an unclear use case + # and bugs in implementation (crbug.com/1169639). `executionContextId` will be + # removed in the future. + experimental deprecated optional ExecutionContextId executionContextId + # If specified, the binding is exposed to the executionContext with + # matching name, even for contexts created after the binding is added. + # See also `ExecutionContext.name` and `worldName` parameter to + # `Page.addScriptToEvaluateOnNewDocument`. + # This parameter is mutually exclusive with `executionContextId`. + optional string executionContextName + + # This method does not remove binding function from global object but + # unsubscribes current runtime agent from Runtime.bindingCalled notifications. + command removeBinding + parameters + string name + + # This method tries to lookup and populate exception details for a + # JavaScript Error object. + # Note that the stackTrace portion of the resulting exceptionDetails will + # only be populated if the Runtime domain was enabled at the time when the + # Error was thrown. + experimental command getExceptionDetails + parameters + # The error object for which to resolve the exception details. + RemoteObjectId errorObjectId + returns + optional ExceptionDetails exceptionDetails + + # Notification is issued every time when binding is called. + experimental event bindingCalled + parameters + string name + string payload + # Identifier of the context where the call was made. + ExecutionContextId executionContextId + + # Issued when console API was called. + event consoleAPICalled + parameters + # Type of the call. + enum type + log + debug + info + error + warning + dir + dirxml + table + trace + clear + startGroup + startGroupCollapsed + endGroup + assert + profile + profileEnd + count + timeEnd + # Call arguments. + array of RemoteObject args + # Identifier of the context where the call was made. + ExecutionContextId executionContextId + # Call timestamp. + Timestamp timestamp + # Stack trace captured when the call was made. The async stack chain is automatically reported for + # the following call types: `assert`, `error`, `trace`, `warning`. For other types the async call + # chain can be retrieved using `Debugger.getStackTrace` and `stackTrace.parentId` field. + optional StackTrace stackTrace + # Console context descriptor for calls on non-default console context (not console.*): + # 'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call + # on named context. + experimental optional string context + + # Issued when unhandled exception was revoked. + event exceptionRevoked + parameters + # Reason describing why exception was revoked. + string reason + # The id of revoked exception, as reported in `exceptionThrown`. + integer exceptionId + + # Issued when exception was thrown and unhandled. + event exceptionThrown + parameters + # Timestamp of the exception. + Timestamp timestamp + ExceptionDetails exceptionDetails + + # Issued when new execution context is created. + event executionContextCreated + parameters + # A newly created execution context. + ExecutionContextDescription context + + # Issued when execution context is destroyed. + event executionContextDestroyed + parameters + # Id of the destroyed context + deprecated ExecutionContextId executionContextId + # Unique Id of the destroyed context + experimental string executionContextUniqueId + + # Issued when all executionContexts were cleared in browser + event executionContextsCleared + + # Issued when object should be inspected (for example, as a result of inspect() command line API + # call). + event inspectRequested + parameters + RemoteObject object + object hints + # Identifier of the context where the call was made. + experimental optional ExecutionContextId executionContextId + +# This domain is deprecated. +deprecated domain Schema + + # Description of the protocol domain. + type Domain extends object + properties + # Domain name. + string name + # Domain version. + string version + + # Returns supported domains. + command getDomains + returns + # List of supported domains. + array of Domain domains diff --git a/vendor/chromiumoxide_cdp/src/cdp.rs b/vendor/chromiumoxide_cdp/src/cdp.rs new file mode 100644 index 0000000..e70f111 --- /dev/null +++ b/vendor/chromiumoxide_cdp/src/cdp.rs @@ -0,0 +1,111038 @@ +#[doc = r" This file is generated and should not be edited directly."] +pub use events::*; +#[doc = r" This trait allows for implementing custom events that are not covered by the"] +#[doc = r" chrome protocol definitions."] +#[doc = r""] +#[doc = r" Every `CustomEvent` also requires an implementation of"] +#[doc = r" `chromiumoxide_types::MethodType` and it must be `DeserializeOwned`"] +#[doc = r" (`#[derive(serde::Deserialize)]`). This is necessary to identify match this"] +#[doc = r" type against the provided `method` identifier of a `CdpEventMessage`"] +#[doc = r" and to properly deserialize it from a `serde_json::Value`"] +pub trait CustomEvent: + ::std::any::Any + serde::de::DeserializeOwned + chromiumoxide_types::MethodType + Send + Sync +{ + #[doc = r" Used to convert the json event into in instance of this type"] + fn from_json(event: serde_json::Value) -> serde_json::Result + where + Self: Sized + 'static, + { + serde_json::from_value(event) + } +} +impl sealed::SealedEvent for T { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } +} +#[doc = r" This is trait that all Events share"] +#[doc = r""] +#[doc = r" This trait is sealed to prevent implementation. The only way to implement a new `Event` is by implementing `CustomEvent`"] +pub trait Event: sealed::SealedEvent {} +impl Event for T {} +impl sealed::SealedCustomEventConverter for T {} +#[doc = r" Function type to convert a json event into an instance of it self but as dyn Event"] +pub type EventConversion = Box< + dyn Fn(serde_json::Value) -> serde_json::Result<::std::sync::Arc> + Send + 'static, +>; +#[doc = r" An enum that does nothing for built in types but contains the conversion method for custom events"] +pub enum EventKind { + BuiltIn, + Custom(EventConversion), +} +impl EventKind { + #[doc = r" Whether this is a custom event"] + pub fn is_custom(&self) -> bool { + matches!(self, EventKind::Custom(_)) + } +} +impl ::std::fmt::Debug for EventKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + EventKind::BuiltIn => f.debug_tuple("BuiltIn").finish(), + EventKind::Custom(_) => f.debug_tuple("Custom").finish(), + } + } +} +#[doc = r" A trait on top of the `Event` trait"] +pub trait IntoEventKind: Event { + #[doc = r" What kind of event this type is"] + fn event_kind() -> EventKind + where + Self: Sized + 'static; +} +impl IntoEventKind for T { + fn event_kind() -> EventKind + where + Self: Sized + 'static, + { + EventKind::Custom(Box::new(Self::event_arc)) + } +} +pub(crate) mod sealed { + pub trait SealedCustomEventConverter: super::CustomEvent + super::Event { + fn event_arc( + event: serde_json::Value, + ) -> serde_json::Result<::std::sync::Arc> + where + Self: Sized + 'static, + { + Ok(::std::sync::Arc::new(Self::from_json(event)?)) + } + } + pub trait SealedEvent: ArcAny + chromiumoxide_types::MethodType { + #[doc = r" generate `&::std::any::Any`'s vtable from `&Trait`'s."] + fn as_any(&self) -> &dyn ::std::any::Any; + } + pub trait ArcAny: ::std::any::Any + Send + Sync { + fn into_any_arc( + self: ::std::sync::Arc, + ) -> ::std::sync::Arc; + } + impl ArcAny for T { + fn into_any_arc( + self: ::std::sync::Arc, + ) -> ::std::sync::Arc { + self + } + } + impl dyn SealedEvent { + #[doc = r" Returns true if the trait object wraps an object of type `T`."] + #[inline] + pub fn is(&self) -> bool { + self.as_any().is::() + } + #[inline] + pub fn downcast_arc( + self: ::std::sync::Arc, + ) -> Result<::std::sync::Arc, ::std::sync::Arc> + where + T: ::std::any::Any + Send + Sync, + { + if self.is::() { + Ok(ArcAny::into_any_arc(self).downcast::().unwrap()) + } else { + Err(self) + } + } + } +} +pub mod events { + use serde::Deserialize; + #[derive(Debug, PartialEq, Clone)] + pub struct CdpEventMessage { + #[doc = r" Name of the method"] + pub method: chromiumoxide_types::MethodId, + #[doc = r" The chromium session Id"] + pub session_id: Option, + #[doc = r" Json params"] + pub params: CdpEvent, + } + impl chromiumoxide_types::Method for CdpEventMessage { + fn identifier(&self) -> chromiumoxide_types::MethodId { + match &self.params { + CdpEvent::DebuggerPaused(inner) => inner.identifier(), + CdpEvent::DebuggerResumed(inner) => inner.identifier(), + CdpEvent::DebuggerScriptFailedToParse(inner) => inner.identifier(), + CdpEvent::DebuggerScriptParsed(inner) => inner.identifier(), + CdpEvent::HeapProfilerAddHeapSnapshotChunk(inner) => inner.identifier(), + CdpEvent::HeapProfilerHeapStatsUpdate(inner) => inner.identifier(), + CdpEvent::HeapProfilerLastSeenObjectId(inner) => inner.identifier(), + CdpEvent::HeapProfilerReportHeapSnapshotProgress(inner) => inner.identifier(), + CdpEvent::HeapProfilerResetProfiles(inner) => inner.identifier(), + CdpEvent::ProfilerConsoleProfileFinished(inner) => inner.identifier(), + CdpEvent::ProfilerConsoleProfileStarted(inner) => inner.identifier(), + CdpEvent::ProfilerPreciseCoverageDeltaUpdate(inner) => inner.identifier(), + CdpEvent::RuntimeBindingCalled(inner) => inner.identifier(), + CdpEvent::RuntimeConsoleApiCalled(inner) => inner.identifier(), + CdpEvent::RuntimeExceptionRevoked(inner) => inner.identifier(), + CdpEvent::RuntimeExceptionThrown(inner) => inner.identifier(), + CdpEvent::RuntimeExecutionContextCreated(inner) => inner.identifier(), + CdpEvent::RuntimeExecutionContextDestroyed(inner) => inner.identifier(), + CdpEvent::RuntimeExecutionContextsCleared(inner) => inner.identifier(), + CdpEvent::RuntimeInspectRequested(inner) => inner.identifier(), + CdpEvent::AccessibilityLoadComplete(inner) => inner.identifier(), + CdpEvent::AccessibilityNodesUpdated(inner) => inner.identifier(), + CdpEvent::AnimationAnimationCanceled(inner) => inner.identifier(), + CdpEvent::AnimationAnimationCreated(inner) => inner.identifier(), + CdpEvent::AnimationAnimationStarted(inner) => inner.identifier(), + CdpEvent::AnimationAnimationUpdated(inner) => inner.identifier(), + CdpEvent::AuditsIssueAdded(inner) => inner.identifier(), + CdpEvent::AutofillAddressFormFilled(inner) => inner.identifier(), + CdpEvent::BackgroundServiceRecordingStateChanged(inner) => inner.identifier(), + CdpEvent::BackgroundServiceBackgroundServiceEventReceived(inner) => { + inner.identifier() + } + CdpEvent::BluetoothEmulationGattOperationReceived(inner) => inner.identifier(), + CdpEvent::BluetoothEmulationCharacteristicOperationReceived(inner) => { + inner.identifier() + } + CdpEvent::BluetoothEmulationDescriptorOperationReceived(inner) => { + inner.identifier() + } + CdpEvent::BrowserDownloadWillBegin(inner) => inner.identifier(), + CdpEvent::BrowserDownloadProgress(inner) => inner.identifier(), + CdpEvent::CssFontsUpdated(inner) => inner.identifier(), + CdpEvent::CssMediaQueryResultChanged(inner) => inner.identifier(), + CdpEvent::CssStyleSheetAdded(inner) => inner.identifier(), + CdpEvent::CssStyleSheetChanged(inner) => inner.identifier(), + CdpEvent::CssStyleSheetRemoved(inner) => inner.identifier(), + CdpEvent::CssComputedStyleUpdated(inner) => inner.identifier(), + CdpEvent::CastSinksUpdated(inner) => inner.identifier(), + CdpEvent::CastIssueUpdated(inner) => inner.identifier(), + CdpEvent::DomAttributeModified(inner) => inner.identifier(), + CdpEvent::DomAdoptedStyleSheetsModified(inner) => inner.identifier(), + CdpEvent::DomAttributeRemoved(inner) => inner.identifier(), + CdpEvent::DomCharacterDataModified(inner) => inner.identifier(), + CdpEvent::DomChildNodeCountUpdated(inner) => inner.identifier(), + CdpEvent::DomChildNodeInserted(inner) => inner.identifier(), + CdpEvent::DomChildNodeRemoved(inner) => inner.identifier(), + CdpEvent::DomDistributedNodesUpdated(inner) => inner.identifier(), + CdpEvent::DomDocumentUpdated(inner) => inner.identifier(), + CdpEvent::DomInlineStyleInvalidated(inner) => inner.identifier(), + CdpEvent::DomPseudoElementAdded(inner) => inner.identifier(), + CdpEvent::DomTopLayerElementsUpdated(inner) => inner.identifier(), + CdpEvent::DomScrollableFlagUpdated(inner) => inner.identifier(), + CdpEvent::DomAffectedByStartingStylesFlagUpdated(inner) => inner.identifier(), + CdpEvent::DomPseudoElementRemoved(inner) => inner.identifier(), + CdpEvent::DomSetChildNodes(inner) => inner.identifier(), + CdpEvent::DomShadowRootPopped(inner) => inner.identifier(), + CdpEvent::DomShadowRootPushed(inner) => inner.identifier(), + CdpEvent::DomStorageDomStorageItemAdded(inner) => inner.identifier(), + CdpEvent::DomStorageDomStorageItemRemoved(inner) => inner.identifier(), + CdpEvent::DomStorageDomStorageItemUpdated(inner) => inner.identifier(), + CdpEvent::DomStorageDomStorageItemsCleared(inner) => inner.identifier(), + CdpEvent::DeviceAccessDeviceRequestPrompted(inner) => inner.identifier(), + CdpEvent::EmulationVirtualTimeBudgetExpired(inner) => inner.identifier(), + CdpEvent::FedCmDialogShown(inner) => inner.identifier(), + CdpEvent::FedCmDialogClosed(inner) => inner.identifier(), + CdpEvent::FetchRequestPaused(inner) => inner.identifier(), + CdpEvent::FetchAuthRequired(inner) => inner.identifier(), + CdpEvent::InputDragIntercepted(inner) => inner.identifier(), + CdpEvent::InspectorDetached(inner) => inner.identifier(), + CdpEvent::InspectorTargetCrashed(inner) => inner.identifier(), + CdpEvent::InspectorTargetReloadedAfterCrash(inner) => inner.identifier(), + CdpEvent::InspectorWorkerScriptLoaded(inner) => inner.identifier(), + CdpEvent::LayerTreeLayerPainted(inner) => inner.identifier(), + CdpEvent::LayerTreeLayerTreeDidChange(inner) => inner.identifier(), + CdpEvent::LogEntryAdded(inner) => inner.identifier(), + CdpEvent::MediaPlayerPropertiesChanged(inner) => inner.identifier(), + CdpEvent::MediaPlayerEventsAdded(inner) => inner.identifier(), + CdpEvent::MediaPlayerMessagesLogged(inner) => inner.identifier(), + CdpEvent::MediaPlayerErrorsRaised(inner) => inner.identifier(), + CdpEvent::MediaPlayerCreated(inner) => inner.identifier(), + CdpEvent::NetworkDataReceived(inner) => inner.identifier(), + CdpEvent::NetworkEventSourceMessageReceived(inner) => inner.identifier(), + CdpEvent::NetworkLoadingFailed(inner) => inner.identifier(), + CdpEvent::NetworkLoadingFinished(inner) => inner.identifier(), + CdpEvent::NetworkRequestServedFromCache(inner) => inner.identifier(), + CdpEvent::NetworkRequestWillBeSent(inner) => inner.identifier(), + CdpEvent::NetworkResourceChangedPriority(inner) => inner.identifier(), + CdpEvent::NetworkSignedExchangeReceived(inner) => inner.identifier(), + CdpEvent::NetworkResponseReceived(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketClosed(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketCreated(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketFrameError(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketFrameReceived(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketFrameSent(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketHandshakeResponseReceived(inner) => inner.identifier(), + CdpEvent::NetworkWebSocketWillSendHandshakeRequest(inner) => inner.identifier(), + CdpEvent::NetworkWebTransportCreated(inner) => inner.identifier(), + CdpEvent::NetworkWebTransportConnectionEstablished(inner) => inner.identifier(), + CdpEvent::NetworkWebTransportClosed(inner) => inner.identifier(), + CdpEvent::NetworkDirectTcpSocketCreated(inner) => inner.identifier(), + CdpEvent::NetworkDirectTcpSocketOpened(inner) => inner.identifier(), + CdpEvent::NetworkDirectTcpSocketAborted(inner) => inner.identifier(), + CdpEvent::NetworkDirectTcpSocketClosed(inner) => inner.identifier(), + CdpEvent::NetworkDirectTcpSocketChunkSent(inner) => inner.identifier(), + CdpEvent::NetworkDirectTcpSocketChunkReceived(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketJoinedMulticastGroup(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketLeftMulticastGroup(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketCreated(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketOpened(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketAborted(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketClosed(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketChunkSent(inner) => inner.identifier(), + CdpEvent::NetworkDirectUdpSocketChunkReceived(inner) => inner.identifier(), + CdpEvent::NetworkRequestWillBeSentExtraInfo(inner) => inner.identifier(), + CdpEvent::NetworkResponseReceivedExtraInfo(inner) => inner.identifier(), + CdpEvent::NetworkResponseReceivedEarlyHints(inner) => inner.identifier(), + CdpEvent::NetworkTrustTokenOperationDone(inner) => inner.identifier(), + CdpEvent::NetworkPolicyUpdated(inner) => inner.identifier(), + CdpEvent::NetworkReportingApiReportAdded(inner) => inner.identifier(), + CdpEvent::NetworkReportingApiReportUpdated(inner) => inner.identifier(), + CdpEvent::NetworkReportingApiEndpointsChangedForOrigin(inner) => inner.identifier(), + CdpEvent::NetworkDeviceBoundSessionsAdded(inner) => inner.identifier(), + CdpEvent::NetworkDeviceBoundSessionEventOccurred(inner) => inner.identifier(), + CdpEvent::OverlayInspectNodeRequested(inner) => inner.identifier(), + CdpEvent::OverlayNodeHighlightRequested(inner) => inner.identifier(), + CdpEvent::OverlayScreenshotRequested(inner) => inner.identifier(), + CdpEvent::OverlayInspectModeCanceled(inner) => inner.identifier(), + CdpEvent::PageDomContentEventFired(inner) => inner.identifier(), + CdpEvent::PageFileChooserOpened(inner) => inner.identifier(), + CdpEvent::PageFrameAttached(inner) => inner.identifier(), + CdpEvent::PageFrameDetached(inner) => inner.identifier(), + CdpEvent::PageFrameSubtreeWillBeDetached(inner) => inner.identifier(), + CdpEvent::PageFrameNavigated(inner) => inner.identifier(), + CdpEvent::PageDocumentOpened(inner) => inner.identifier(), + CdpEvent::PageFrameResized(inner) => inner.identifier(), + CdpEvent::PageFrameStartedNavigating(inner) => inner.identifier(), + CdpEvent::PageFrameRequestedNavigation(inner) => inner.identifier(), + CdpEvent::PageFrameStartedLoading(inner) => inner.identifier(), + CdpEvent::PageFrameStoppedLoading(inner) => inner.identifier(), + CdpEvent::PageInterstitialHidden(inner) => inner.identifier(), + CdpEvent::PageInterstitialShown(inner) => inner.identifier(), + CdpEvent::PageJavascriptDialogClosed(inner) => inner.identifier(), + CdpEvent::PageJavascriptDialogOpening(inner) => inner.identifier(), + CdpEvent::PageLifecycleEvent(inner) => inner.identifier(), + CdpEvent::PageBackForwardCacheNotUsed(inner) => inner.identifier(), + CdpEvent::PageLoadEventFired(inner) => inner.identifier(), + CdpEvent::PageNavigatedWithinDocument(inner) => inner.identifier(), + CdpEvent::PageScreencastFrame(inner) => inner.identifier(), + CdpEvent::PageScreencastVisibilityChanged(inner) => inner.identifier(), + CdpEvent::PageWindowOpen(inner) => inner.identifier(), + CdpEvent::PageCompilationCacheProduced(inner) => inner.identifier(), + CdpEvent::PerformanceMetrics(inner) => inner.identifier(), + CdpEvent::PerformanceTimelineTimelineEventAdded(inner) => inner.identifier(), + CdpEvent::PreloadRuleSetUpdated(inner) => inner.identifier(), + CdpEvent::PreloadRuleSetRemoved(inner) => inner.identifier(), + CdpEvent::PreloadPreloadEnabledStateUpdated(inner) => inner.identifier(), + CdpEvent::PreloadPrefetchStatusUpdated(inner) => inner.identifier(), + CdpEvent::PreloadPrerenderStatusUpdated(inner) => inner.identifier(), + CdpEvent::PreloadPreloadingAttemptSourcesUpdated(inner) => inner.identifier(), + CdpEvent::SecurityVisibleSecurityStateChanged(inner) => inner.identifier(), + CdpEvent::ServiceWorkerWorkerErrorReported(inner) => inner.identifier(), + CdpEvent::ServiceWorkerWorkerRegistrationUpdated(inner) => inner.identifier(), + CdpEvent::ServiceWorkerWorkerVersionUpdated(inner) => inner.identifier(), + CdpEvent::StorageCacheStorageContentUpdated(inner) => inner.identifier(), + CdpEvent::StorageCacheStorageListUpdated(inner) => inner.identifier(), + CdpEvent::StorageIndexedDbContentUpdated(inner) => inner.identifier(), + CdpEvent::StorageIndexedDbListUpdated(inner) => inner.identifier(), + CdpEvent::StorageInterestGroupAccessed(inner) => inner.identifier(), + CdpEvent::StorageInterestGroupAuctionEventOccurred(inner) => inner.identifier(), + CdpEvent::StorageInterestGroupAuctionNetworkRequestCreated(inner) => { + inner.identifier() + } + CdpEvent::StorageSharedStorageAccessed(inner) => inner.identifier(), + CdpEvent::StorageSharedStorageWorkletOperationExecutionFinished(inner) => { + inner.identifier() + } + CdpEvent::StorageStorageBucketCreatedOrUpdated(inner) => inner.identifier(), + CdpEvent::StorageStorageBucketDeleted(inner) => inner.identifier(), + CdpEvent::StorageAttributionReportingSourceRegistered(inner) => inner.identifier(), + CdpEvent::StorageAttributionReportingTriggerRegistered(inner) => inner.identifier(), + CdpEvent::StorageAttributionReportingReportSent(inner) => inner.identifier(), + CdpEvent::StorageAttributionReportingVerboseDebugReportSent(inner) => { + inner.identifier() + } + CdpEvent::TargetAttachedToTarget(inner) => inner.identifier(), + CdpEvent::TargetDetachedFromTarget(inner) => inner.identifier(), + CdpEvent::TargetReceivedMessageFromTarget(inner) => inner.identifier(), + CdpEvent::TargetTargetCreated(inner) => inner.identifier(), + CdpEvent::TargetTargetDestroyed(inner) => inner.identifier(), + CdpEvent::TargetTargetCrashed(inner) => inner.identifier(), + CdpEvent::TargetTargetInfoChanged(inner) => inner.identifier(), + CdpEvent::TetheringAccepted(inner) => inner.identifier(), + CdpEvent::TracingBufferUsage(inner) => inner.identifier(), + CdpEvent::TracingDataCollected(inner) => inner.identifier(), + CdpEvent::TracingTracingComplete(inner) => inner.identifier(), + CdpEvent::WebAudioContextCreated(inner) => inner.identifier(), + CdpEvent::WebAudioContextWillBeDestroyed(inner) => inner.identifier(), + CdpEvent::WebAudioContextChanged(inner) => inner.identifier(), + CdpEvent::WebAudioAudioListenerCreated(inner) => inner.identifier(), + CdpEvent::WebAudioAudioListenerWillBeDestroyed(inner) => inner.identifier(), + CdpEvent::WebAudioAudioNodeCreated(inner) => inner.identifier(), + CdpEvent::WebAudioAudioNodeWillBeDestroyed(inner) => inner.identifier(), + CdpEvent::WebAudioAudioParamCreated(inner) => inner.identifier(), + CdpEvent::WebAudioAudioParamWillBeDestroyed(inner) => inner.identifier(), + CdpEvent::WebAudioNodesConnected(inner) => inner.identifier(), + CdpEvent::WebAudioNodesDisconnected(inner) => inner.identifier(), + CdpEvent::WebAudioNodeParamConnected(inner) => inner.identifier(), + CdpEvent::WebAudioNodeParamDisconnected(inner) => inner.identifier(), + CdpEvent::WebAuthnCredentialAdded(inner) => inner.identifier(), + CdpEvent::WebAuthnCredentialDeleted(inner) => inner.identifier(), + CdpEvent::WebAuthnCredentialUpdated(inner) => inner.identifier(), + CdpEvent::WebAuthnCredentialAsserted(inner) => inner.identifier(), + _ => self.method.clone(), + } + } + } + impl chromiumoxide_types::EventMessage for CdpEventMessage { + fn session_id(&self) -> Option<&str> { + self.session_id.as_deref() + } + } + #[derive(Debug, Clone, PartialEq)] + pub enum CdpEvent { + DebuggerPaused(super::js_protocol::debugger::EventPaused), + DebuggerResumed(super::js_protocol::debugger::EventResumed), + DebuggerScriptFailedToParse(Box), + DebuggerScriptParsed(Box), + HeapProfilerAddHeapSnapshotChunk( + super::js_protocol::heap_profiler::EventAddHeapSnapshotChunk, + ), + HeapProfilerHeapStatsUpdate(super::js_protocol::heap_profiler::EventHeapStatsUpdate), + HeapProfilerLastSeenObjectId(super::js_protocol::heap_profiler::EventLastSeenObjectId), + HeapProfilerReportHeapSnapshotProgress( + super::js_protocol::heap_profiler::EventReportHeapSnapshotProgress, + ), + HeapProfilerResetProfiles(super::js_protocol::heap_profiler::EventResetProfiles), + ProfilerConsoleProfileFinished(super::js_protocol::profiler::EventConsoleProfileFinished), + ProfilerConsoleProfileStarted(super::js_protocol::profiler::EventConsoleProfileStarted), + ProfilerPreciseCoverageDeltaUpdate( + super::js_protocol::profiler::EventPreciseCoverageDeltaUpdate, + ), + RuntimeBindingCalled(super::js_protocol::runtime::EventBindingCalled), + RuntimeConsoleApiCalled(super::js_protocol::runtime::EventConsoleApiCalled), + RuntimeExceptionRevoked(super::js_protocol::runtime::EventExceptionRevoked), + RuntimeExceptionThrown(Box), + RuntimeExecutionContextCreated(super::js_protocol::runtime::EventExecutionContextCreated), + RuntimeExecutionContextDestroyed( + super::js_protocol::runtime::EventExecutionContextDestroyed, + ), + RuntimeExecutionContextsCleared(super::js_protocol::runtime::EventExecutionContextsCleared), + RuntimeInspectRequested(Box), + AccessibilityLoadComplete(Box), + AccessibilityNodesUpdated(super::browser_protocol::accessibility::EventNodesUpdated), + AnimationAnimationCanceled(super::browser_protocol::animation::EventAnimationCanceled), + AnimationAnimationCreated(super::browser_protocol::animation::EventAnimationCreated), + AnimationAnimationStarted(Box), + AnimationAnimationUpdated(Box), + AuditsIssueAdded(Box), + AutofillAddressFormFilled(super::browser_protocol::autofill::EventAddressFormFilled), + BackgroundServiceRecordingStateChanged( + super::browser_protocol::background_service::EventRecordingStateChanged, + ), + BackgroundServiceBackgroundServiceEventReceived( + super::browser_protocol::background_service::EventBackgroundServiceEventReceived, + ), + BluetoothEmulationGattOperationReceived( + super::browser_protocol::bluetooth_emulation::EventGattOperationReceived, + ), + BluetoothEmulationCharacteristicOperationReceived( + super::browser_protocol::bluetooth_emulation::EventCharacteristicOperationReceived, + ), + BluetoothEmulationDescriptorOperationReceived( + super::browser_protocol::bluetooth_emulation::EventDescriptorOperationReceived, + ), + BrowserDownloadWillBegin(super::browser_protocol::browser::EventDownloadWillBegin), + BrowserDownloadProgress(super::browser_protocol::browser::EventDownloadProgress), + CssFontsUpdated(Box), + CssMediaQueryResultChanged(super::browser_protocol::css::EventMediaQueryResultChanged), + CssStyleSheetAdded(super::browser_protocol::css::EventStyleSheetAdded), + CssStyleSheetChanged(super::browser_protocol::css::EventStyleSheetChanged), + CssStyleSheetRemoved(super::browser_protocol::css::EventStyleSheetRemoved), + CssComputedStyleUpdated(super::browser_protocol::css::EventComputedStyleUpdated), + CastSinksUpdated(super::browser_protocol::cast::EventSinksUpdated), + CastIssueUpdated(super::browser_protocol::cast::EventIssueUpdated), + DomAttributeModified(super::browser_protocol::dom::EventAttributeModified), + DomAdoptedStyleSheetsModified( + super::browser_protocol::dom::EventAdoptedStyleSheetsModified, + ), + DomAttributeRemoved(super::browser_protocol::dom::EventAttributeRemoved), + DomCharacterDataModified(super::browser_protocol::dom::EventCharacterDataModified), + DomChildNodeCountUpdated(super::browser_protocol::dom::EventChildNodeCountUpdated), + DomChildNodeInserted(Box), + DomChildNodeRemoved(super::browser_protocol::dom::EventChildNodeRemoved), + DomDistributedNodesUpdated(super::browser_protocol::dom::EventDistributedNodesUpdated), + DomDocumentUpdated(super::browser_protocol::dom::EventDocumentUpdated), + DomInlineStyleInvalidated(super::browser_protocol::dom::EventInlineStyleInvalidated), + DomPseudoElementAdded(Box), + DomTopLayerElementsUpdated(super::browser_protocol::dom::EventTopLayerElementsUpdated), + DomScrollableFlagUpdated(super::browser_protocol::dom::EventScrollableFlagUpdated), + DomAffectedByStartingStylesFlagUpdated( + super::browser_protocol::dom::EventAffectedByStartingStylesFlagUpdated, + ), + DomPseudoElementRemoved(super::browser_protocol::dom::EventPseudoElementRemoved), + DomSetChildNodes(super::browser_protocol::dom::EventSetChildNodes), + DomShadowRootPopped(super::browser_protocol::dom::EventShadowRootPopped), + DomShadowRootPushed(Box), + DomStorageDomStorageItemAdded( + super::browser_protocol::dom_storage::EventDomStorageItemAdded, + ), + DomStorageDomStorageItemRemoved( + super::browser_protocol::dom_storage::EventDomStorageItemRemoved, + ), + DomStorageDomStorageItemUpdated( + super::browser_protocol::dom_storage::EventDomStorageItemUpdated, + ), + DomStorageDomStorageItemsCleared( + super::browser_protocol::dom_storage::EventDomStorageItemsCleared, + ), + DeviceAccessDeviceRequestPrompted( + super::browser_protocol::device_access::EventDeviceRequestPrompted, + ), + EmulationVirtualTimeBudgetExpired( + super::browser_protocol::emulation::EventVirtualTimeBudgetExpired, + ), + FedCmDialogShown(super::browser_protocol::fed_cm::EventDialogShown), + FedCmDialogClosed(super::browser_protocol::fed_cm::EventDialogClosed), + FetchRequestPaused(Box), + FetchAuthRequired(Box), + InputDragIntercepted(super::browser_protocol::input::EventDragIntercepted), + InspectorDetached(super::browser_protocol::inspector::EventDetached), + InspectorTargetCrashed(super::browser_protocol::inspector::EventTargetCrashed), + InspectorTargetReloadedAfterCrash( + super::browser_protocol::inspector::EventTargetReloadedAfterCrash, + ), + InspectorWorkerScriptLoaded(super::browser_protocol::inspector::EventWorkerScriptLoaded), + LayerTreeLayerPainted(super::browser_protocol::layer_tree::EventLayerPainted), + LayerTreeLayerTreeDidChange(super::browser_protocol::layer_tree::EventLayerTreeDidChange), + LogEntryAdded(Box), + MediaPlayerPropertiesChanged(super::browser_protocol::media::EventPlayerPropertiesChanged), + MediaPlayerEventsAdded(super::browser_protocol::media::EventPlayerEventsAdded), + MediaPlayerMessagesLogged(super::browser_protocol::media::EventPlayerMessagesLogged), + MediaPlayerErrorsRaised(super::browser_protocol::media::EventPlayerErrorsRaised), + MediaPlayerCreated(super::browser_protocol::media::EventPlayerCreated), + NetworkDataReceived(super::browser_protocol::network::EventDataReceived), + NetworkEventSourceMessageReceived( + super::browser_protocol::network::EventEventSourceMessageReceived, + ), + NetworkLoadingFailed(super::browser_protocol::network::EventLoadingFailed), + NetworkLoadingFinished(super::browser_protocol::network::EventLoadingFinished), + NetworkRequestServedFromCache( + super::browser_protocol::network::EventRequestServedFromCache, + ), + NetworkRequestWillBeSent(Box), + NetworkResourceChangedPriority( + super::browser_protocol::network::EventResourceChangedPriority, + ), + NetworkSignedExchangeReceived( + Box, + ), + NetworkResponseReceived(Box), + NetworkWebSocketClosed(super::browser_protocol::network::EventWebSocketClosed), + NetworkWebSocketCreated(Box), + NetworkWebSocketFrameError(super::browser_protocol::network::EventWebSocketFrameError), + NetworkWebSocketFrameReceived( + super::browser_protocol::network::EventWebSocketFrameReceived, + ), + NetworkWebSocketFrameSent(super::browser_protocol::network::EventWebSocketFrameSent), + NetworkWebSocketHandshakeResponseReceived( + Box, + ), + NetworkWebSocketWillSendHandshakeRequest( + super::browser_protocol::network::EventWebSocketWillSendHandshakeRequest, + ), + NetworkWebTransportCreated(Box), + NetworkWebTransportConnectionEstablished( + super::browser_protocol::network::EventWebTransportConnectionEstablished, + ), + NetworkWebTransportClosed(super::browser_protocol::network::EventWebTransportClosed), + NetworkDirectTcpSocketCreated( + Box, + ), + NetworkDirectTcpSocketOpened(super::browser_protocol::network::EventDirectTcpSocketOpened), + NetworkDirectTcpSocketAborted( + super::browser_protocol::network::EventDirectTcpSocketAborted, + ), + NetworkDirectTcpSocketClosed(super::browser_protocol::network::EventDirectTcpSocketClosed), + NetworkDirectTcpSocketChunkSent( + super::browser_protocol::network::EventDirectTcpSocketChunkSent, + ), + NetworkDirectTcpSocketChunkReceived( + super::browser_protocol::network::EventDirectTcpSocketChunkReceived, + ), + NetworkDirectUdpSocketJoinedMulticastGroup( + super::browser_protocol::network::EventDirectUdpSocketJoinedMulticastGroup, + ), + NetworkDirectUdpSocketLeftMulticastGroup( + super::browser_protocol::network::EventDirectUdpSocketLeftMulticastGroup, + ), + NetworkDirectUdpSocketCreated( + Box, + ), + NetworkDirectUdpSocketOpened(super::browser_protocol::network::EventDirectUdpSocketOpened), + NetworkDirectUdpSocketAborted( + super::browser_protocol::network::EventDirectUdpSocketAborted, + ), + NetworkDirectUdpSocketClosed(super::browser_protocol::network::EventDirectUdpSocketClosed), + NetworkDirectUdpSocketChunkSent( + super::browser_protocol::network::EventDirectUdpSocketChunkSent, + ), + NetworkDirectUdpSocketChunkReceived( + super::browser_protocol::network::EventDirectUdpSocketChunkReceived, + ), + NetworkRequestWillBeSentExtraInfo( + super::browser_protocol::network::EventRequestWillBeSentExtraInfo, + ), + NetworkResponseReceivedExtraInfo( + Box, + ), + NetworkResponseReceivedEarlyHints( + super::browser_protocol::network::EventResponseReceivedEarlyHints, + ), + NetworkTrustTokenOperationDone( + super::browser_protocol::network::EventTrustTokenOperationDone, + ), + NetworkPolicyUpdated(super::browser_protocol::network::EventPolicyUpdated), + NetworkReportingApiReportAdded( + super::browser_protocol::network::EventReportingApiReportAdded, + ), + NetworkReportingApiReportUpdated( + super::browser_protocol::network::EventReportingApiReportUpdated, + ), + NetworkReportingApiEndpointsChangedForOrigin( + super::browser_protocol::network::EventReportingApiEndpointsChangedForOrigin, + ), + NetworkDeviceBoundSessionsAdded( + super::browser_protocol::network::EventDeviceBoundSessionsAdded, + ), + NetworkDeviceBoundSessionEventOccurred( + Box, + ), + OverlayInspectNodeRequested(super::browser_protocol::overlay::EventInspectNodeRequested), + OverlayNodeHighlightRequested( + super::browser_protocol::overlay::EventNodeHighlightRequested, + ), + OverlayScreenshotRequested(super::browser_protocol::overlay::EventScreenshotRequested), + OverlayInspectModeCanceled(super::browser_protocol::overlay::EventInspectModeCanceled), + PageDomContentEventFired(super::browser_protocol::page::EventDomContentEventFired), + PageFileChooserOpened(super::browser_protocol::page::EventFileChooserOpened), + PageFrameAttached(super::browser_protocol::page::EventFrameAttached), + PageFrameDetached(super::browser_protocol::page::EventFrameDetached), + PageFrameSubtreeWillBeDetached( + super::browser_protocol::page::EventFrameSubtreeWillBeDetached, + ), + PageFrameNavigated(Box), + PageDocumentOpened(Box), + PageFrameResized(super::browser_protocol::page::EventFrameResized), + PageFrameStartedNavigating(super::browser_protocol::page::EventFrameStartedNavigating), + PageFrameRequestedNavigation(super::browser_protocol::page::EventFrameRequestedNavigation), + PageFrameStartedLoading(super::browser_protocol::page::EventFrameStartedLoading), + PageFrameStoppedLoading(super::browser_protocol::page::EventFrameStoppedLoading), + PageInterstitialHidden(super::browser_protocol::page::EventInterstitialHidden), + PageInterstitialShown(super::browser_protocol::page::EventInterstitialShown), + PageJavascriptDialogClosed(super::browser_protocol::page::EventJavascriptDialogClosed), + PageJavascriptDialogOpening(super::browser_protocol::page::EventJavascriptDialogOpening), + PageLifecycleEvent(super::browser_protocol::page::EventLifecycleEvent), + PageBackForwardCacheNotUsed(super::browser_protocol::page::EventBackForwardCacheNotUsed), + PageLoadEventFired(super::browser_protocol::page::EventLoadEventFired), + PageNavigatedWithinDocument(super::browser_protocol::page::EventNavigatedWithinDocument), + PageScreencastFrame(super::browser_protocol::page::EventScreencastFrame), + PageScreencastVisibilityChanged( + super::browser_protocol::page::EventScreencastVisibilityChanged, + ), + PageWindowOpen(super::browser_protocol::page::EventWindowOpen), + PageCompilationCacheProduced(super::browser_protocol::page::EventCompilationCacheProduced), + PerformanceMetrics(super::browser_protocol::performance::EventMetrics), + PerformanceTimelineTimelineEventAdded( + Box, + ), + PreloadRuleSetUpdated(Box), + PreloadRuleSetRemoved(super::browser_protocol::preload::EventRuleSetRemoved), + PreloadPreloadEnabledStateUpdated( + super::browser_protocol::preload::EventPreloadEnabledStateUpdated, + ), + PreloadPrefetchStatusUpdated( + Box, + ), + PreloadPrerenderStatusUpdated( + super::browser_protocol::preload::EventPrerenderStatusUpdated, + ), + PreloadPreloadingAttemptSourcesUpdated( + super::browser_protocol::preload::EventPreloadingAttemptSourcesUpdated, + ), + SecurityVisibleSecurityStateChanged( + Box, + ), + ServiceWorkerWorkerErrorReported( + super::browser_protocol::service_worker::EventWorkerErrorReported, + ), + ServiceWorkerWorkerRegistrationUpdated( + super::browser_protocol::service_worker::EventWorkerRegistrationUpdated, + ), + ServiceWorkerWorkerVersionUpdated( + super::browser_protocol::service_worker::EventWorkerVersionUpdated, + ), + StorageCacheStorageContentUpdated( + super::browser_protocol::storage::EventCacheStorageContentUpdated, + ), + StorageCacheStorageListUpdated( + super::browser_protocol::storage::EventCacheStorageListUpdated, + ), + StorageIndexedDbContentUpdated( + super::browser_protocol::storage::EventIndexedDbContentUpdated, + ), + StorageIndexedDbListUpdated(super::browser_protocol::storage::EventIndexedDbListUpdated), + StorageInterestGroupAccessed(super::browser_protocol::storage::EventInterestGroupAccessed), + StorageInterestGroupAuctionEventOccurred( + super::browser_protocol::storage::EventInterestGroupAuctionEventOccurred, + ), + StorageInterestGroupAuctionNetworkRequestCreated( + super::browser_protocol::storage::EventInterestGroupAuctionNetworkRequestCreated, + ), + StorageSharedStorageAccessed( + Box, + ), + StorageSharedStorageWorkletOperationExecutionFinished( + super::browser_protocol::storage::EventSharedStorageWorkletOperationExecutionFinished, + ), + StorageStorageBucketCreatedOrUpdated( + super::browser_protocol::storage::EventStorageBucketCreatedOrUpdated, + ), + StorageStorageBucketDeleted(super::browser_protocol::storage::EventStorageBucketDeleted), + StorageAttributionReportingSourceRegistered( + Box, + ), + StorageAttributionReportingTriggerRegistered( + Box, + ), + StorageAttributionReportingReportSent( + super::browser_protocol::storage::EventAttributionReportingReportSent, + ), + StorageAttributionReportingVerboseDebugReportSent( + super::browser_protocol::storage::EventAttributionReportingVerboseDebugReportSent, + ), + TargetAttachedToTarget(Box), + TargetDetachedFromTarget(super::browser_protocol::target::EventDetachedFromTarget), + TargetReceivedMessageFromTarget( + super::browser_protocol::target::EventReceivedMessageFromTarget, + ), + TargetTargetCreated(Box), + TargetTargetDestroyed(super::browser_protocol::target::EventTargetDestroyed), + TargetTargetCrashed(super::browser_protocol::target::EventTargetCrashed), + TargetTargetInfoChanged(Box), + TetheringAccepted(super::browser_protocol::tethering::EventAccepted), + TracingBufferUsage(super::browser_protocol::tracing::EventBufferUsage), + TracingDataCollected(super::browser_protocol::tracing::EventDataCollected), + TracingTracingComplete(super::browser_protocol::tracing::EventTracingComplete), + WebAudioContextCreated(super::browser_protocol::web_audio::EventContextCreated), + WebAudioContextWillBeDestroyed( + super::browser_protocol::web_audio::EventContextWillBeDestroyed, + ), + WebAudioContextChanged(super::browser_protocol::web_audio::EventContextChanged), + WebAudioAudioListenerCreated(super::browser_protocol::web_audio::EventAudioListenerCreated), + WebAudioAudioListenerWillBeDestroyed( + super::browser_protocol::web_audio::EventAudioListenerWillBeDestroyed, + ), + WebAudioAudioNodeCreated(super::browser_protocol::web_audio::EventAudioNodeCreated), + WebAudioAudioNodeWillBeDestroyed( + super::browser_protocol::web_audio::EventAudioNodeWillBeDestroyed, + ), + WebAudioAudioParamCreated(super::browser_protocol::web_audio::EventAudioParamCreated), + WebAudioAudioParamWillBeDestroyed( + super::browser_protocol::web_audio::EventAudioParamWillBeDestroyed, + ), + WebAudioNodesConnected(super::browser_protocol::web_audio::EventNodesConnected), + WebAudioNodesDisconnected(super::browser_protocol::web_audio::EventNodesDisconnected), + WebAudioNodeParamConnected(super::browser_protocol::web_audio::EventNodeParamConnected), + WebAudioNodeParamDisconnected( + super::browser_protocol::web_audio::EventNodeParamDisconnected, + ), + WebAuthnCredentialAdded(Box), + WebAuthnCredentialDeleted(super::browser_protocol::web_authn::EventCredentialDeleted), + WebAuthnCredentialUpdated(Box), + WebAuthnCredentialAsserted( + Box, + ), + Other(serde_json::Value), + } + impl CdpEvent { + pub fn other(other: serde_json::Value) -> Self { + CdpEvent::Other(other) + } + #[doc = r" Serializes the event as Json"] + pub fn into_json(self) -> serde_json::Result { + match self { + CdpEvent::DebuggerPaused(inner) => serde_json::to_value(inner), + CdpEvent::DebuggerResumed(inner) => serde_json::to_value(inner), + CdpEvent::DebuggerScriptFailedToParse(inner) => serde_json::to_value(inner), + CdpEvent::DebuggerScriptParsed(inner) => serde_json::to_value(inner), + CdpEvent::HeapProfilerAddHeapSnapshotChunk(inner) => serde_json::to_value(inner), + CdpEvent::HeapProfilerHeapStatsUpdate(inner) => serde_json::to_value(inner), + CdpEvent::HeapProfilerLastSeenObjectId(inner) => serde_json::to_value(inner), + CdpEvent::HeapProfilerReportHeapSnapshotProgress(inner) => { + serde_json::to_value(inner) + } + CdpEvent::HeapProfilerResetProfiles(inner) => serde_json::to_value(inner), + CdpEvent::ProfilerConsoleProfileFinished(inner) => serde_json::to_value(inner), + CdpEvent::ProfilerConsoleProfileStarted(inner) => serde_json::to_value(inner), + CdpEvent::ProfilerPreciseCoverageDeltaUpdate(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeBindingCalled(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeConsoleApiCalled(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeExceptionRevoked(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeExceptionThrown(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeExecutionContextCreated(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeExecutionContextDestroyed(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeExecutionContextsCleared(inner) => serde_json::to_value(inner), + CdpEvent::RuntimeInspectRequested(inner) => serde_json::to_value(inner), + CdpEvent::AccessibilityLoadComplete(inner) => serde_json::to_value(inner), + CdpEvent::AccessibilityNodesUpdated(inner) => serde_json::to_value(inner), + CdpEvent::AnimationAnimationCanceled(inner) => serde_json::to_value(inner), + CdpEvent::AnimationAnimationCreated(inner) => serde_json::to_value(inner), + CdpEvent::AnimationAnimationStarted(inner) => serde_json::to_value(inner), + CdpEvent::AnimationAnimationUpdated(inner) => serde_json::to_value(inner), + CdpEvent::AuditsIssueAdded(inner) => serde_json::to_value(inner), + CdpEvent::AutofillAddressFormFilled(inner) => serde_json::to_value(inner), + CdpEvent::BackgroundServiceRecordingStateChanged(inner) => { + serde_json::to_value(inner) + } + CdpEvent::BackgroundServiceBackgroundServiceEventReceived(inner) => { + serde_json::to_value(inner) + } + CdpEvent::BluetoothEmulationGattOperationReceived(inner) => { + serde_json::to_value(inner) + } + CdpEvent::BluetoothEmulationCharacteristicOperationReceived(inner) => { + serde_json::to_value(inner) + } + CdpEvent::BluetoothEmulationDescriptorOperationReceived(inner) => { + serde_json::to_value(inner) + } + CdpEvent::BrowserDownloadWillBegin(inner) => serde_json::to_value(inner), + CdpEvent::BrowserDownloadProgress(inner) => serde_json::to_value(inner), + CdpEvent::CssFontsUpdated(inner) => serde_json::to_value(inner), + CdpEvent::CssMediaQueryResultChanged(inner) => serde_json::to_value(inner), + CdpEvent::CssStyleSheetAdded(inner) => serde_json::to_value(inner), + CdpEvent::CssStyleSheetChanged(inner) => serde_json::to_value(inner), + CdpEvent::CssStyleSheetRemoved(inner) => serde_json::to_value(inner), + CdpEvent::CssComputedStyleUpdated(inner) => serde_json::to_value(inner), + CdpEvent::CastSinksUpdated(inner) => serde_json::to_value(inner), + CdpEvent::CastIssueUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomAttributeModified(inner) => serde_json::to_value(inner), + CdpEvent::DomAdoptedStyleSheetsModified(inner) => serde_json::to_value(inner), + CdpEvent::DomAttributeRemoved(inner) => serde_json::to_value(inner), + CdpEvent::DomCharacterDataModified(inner) => serde_json::to_value(inner), + CdpEvent::DomChildNodeCountUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomChildNodeInserted(inner) => serde_json::to_value(inner), + CdpEvent::DomChildNodeRemoved(inner) => serde_json::to_value(inner), + CdpEvent::DomDistributedNodesUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomDocumentUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomInlineStyleInvalidated(inner) => serde_json::to_value(inner), + CdpEvent::DomPseudoElementAdded(inner) => serde_json::to_value(inner), + CdpEvent::DomTopLayerElementsUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomScrollableFlagUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomAffectedByStartingStylesFlagUpdated(inner) => { + serde_json::to_value(inner) + } + CdpEvent::DomPseudoElementRemoved(inner) => serde_json::to_value(inner), + CdpEvent::DomSetChildNodes(inner) => serde_json::to_value(inner), + CdpEvent::DomShadowRootPopped(inner) => serde_json::to_value(inner), + CdpEvent::DomShadowRootPushed(inner) => serde_json::to_value(inner), + CdpEvent::DomStorageDomStorageItemAdded(inner) => serde_json::to_value(inner), + CdpEvent::DomStorageDomStorageItemRemoved(inner) => serde_json::to_value(inner), + CdpEvent::DomStorageDomStorageItemUpdated(inner) => serde_json::to_value(inner), + CdpEvent::DomStorageDomStorageItemsCleared(inner) => serde_json::to_value(inner), + CdpEvent::DeviceAccessDeviceRequestPrompted(inner) => serde_json::to_value(inner), + CdpEvent::EmulationVirtualTimeBudgetExpired(inner) => serde_json::to_value(inner), + CdpEvent::FedCmDialogShown(inner) => serde_json::to_value(inner), + CdpEvent::FedCmDialogClosed(inner) => serde_json::to_value(inner), + CdpEvent::FetchRequestPaused(inner) => serde_json::to_value(inner), + CdpEvent::FetchAuthRequired(inner) => serde_json::to_value(inner), + CdpEvent::InputDragIntercepted(inner) => serde_json::to_value(inner), + CdpEvent::InspectorDetached(inner) => serde_json::to_value(inner), + CdpEvent::InspectorTargetCrashed(inner) => serde_json::to_value(inner), + CdpEvent::InspectorTargetReloadedAfterCrash(inner) => serde_json::to_value(inner), + CdpEvent::InspectorWorkerScriptLoaded(inner) => serde_json::to_value(inner), + CdpEvent::LayerTreeLayerPainted(inner) => serde_json::to_value(inner), + CdpEvent::LayerTreeLayerTreeDidChange(inner) => serde_json::to_value(inner), + CdpEvent::LogEntryAdded(inner) => serde_json::to_value(inner), + CdpEvent::MediaPlayerPropertiesChanged(inner) => serde_json::to_value(inner), + CdpEvent::MediaPlayerEventsAdded(inner) => serde_json::to_value(inner), + CdpEvent::MediaPlayerMessagesLogged(inner) => serde_json::to_value(inner), + CdpEvent::MediaPlayerErrorsRaised(inner) => serde_json::to_value(inner), + CdpEvent::MediaPlayerCreated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDataReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkEventSourceMessageReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkLoadingFailed(inner) => serde_json::to_value(inner), + CdpEvent::NetworkLoadingFinished(inner) => serde_json::to_value(inner), + CdpEvent::NetworkRequestServedFromCache(inner) => serde_json::to_value(inner), + CdpEvent::NetworkRequestWillBeSent(inner) => serde_json::to_value(inner), + CdpEvent::NetworkResourceChangedPriority(inner) => serde_json::to_value(inner), + CdpEvent::NetworkSignedExchangeReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkResponseReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebSocketClosed(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebSocketCreated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebSocketFrameError(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebSocketFrameReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebSocketFrameSent(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebSocketHandshakeResponseReceived(inner) => { + serde_json::to_value(inner) + } + CdpEvent::NetworkWebSocketWillSendHandshakeRequest(inner) => { + serde_json::to_value(inner) + } + CdpEvent::NetworkWebTransportCreated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkWebTransportConnectionEstablished(inner) => { + serde_json::to_value(inner) + } + CdpEvent::NetworkWebTransportClosed(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectTcpSocketCreated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectTcpSocketOpened(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectTcpSocketAborted(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectTcpSocketClosed(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectTcpSocketChunkSent(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectTcpSocketChunkReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectUdpSocketJoinedMulticastGroup(inner) => { + serde_json::to_value(inner) + } + CdpEvent::NetworkDirectUdpSocketLeftMulticastGroup(inner) => { + serde_json::to_value(inner) + } + CdpEvent::NetworkDirectUdpSocketCreated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectUdpSocketOpened(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectUdpSocketAborted(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectUdpSocketClosed(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectUdpSocketChunkSent(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDirectUdpSocketChunkReceived(inner) => serde_json::to_value(inner), + CdpEvent::NetworkRequestWillBeSentExtraInfo(inner) => serde_json::to_value(inner), + CdpEvent::NetworkResponseReceivedExtraInfo(inner) => serde_json::to_value(inner), + CdpEvent::NetworkResponseReceivedEarlyHints(inner) => serde_json::to_value(inner), + CdpEvent::NetworkTrustTokenOperationDone(inner) => serde_json::to_value(inner), + CdpEvent::NetworkPolicyUpdated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkReportingApiReportAdded(inner) => serde_json::to_value(inner), + CdpEvent::NetworkReportingApiReportUpdated(inner) => serde_json::to_value(inner), + CdpEvent::NetworkReportingApiEndpointsChangedForOrigin(inner) => { + serde_json::to_value(inner) + } + CdpEvent::NetworkDeviceBoundSessionsAdded(inner) => serde_json::to_value(inner), + CdpEvent::NetworkDeviceBoundSessionEventOccurred(inner) => { + serde_json::to_value(inner) + } + CdpEvent::OverlayInspectNodeRequested(inner) => serde_json::to_value(inner), + CdpEvent::OverlayNodeHighlightRequested(inner) => serde_json::to_value(inner), + CdpEvent::OverlayScreenshotRequested(inner) => serde_json::to_value(inner), + CdpEvent::OverlayInspectModeCanceled(inner) => serde_json::to_value(inner), + CdpEvent::PageDomContentEventFired(inner) => serde_json::to_value(inner), + CdpEvent::PageFileChooserOpened(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameAttached(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameDetached(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameSubtreeWillBeDetached(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameNavigated(inner) => serde_json::to_value(inner), + CdpEvent::PageDocumentOpened(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameResized(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameStartedNavigating(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameRequestedNavigation(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameStartedLoading(inner) => serde_json::to_value(inner), + CdpEvent::PageFrameStoppedLoading(inner) => serde_json::to_value(inner), + CdpEvent::PageInterstitialHidden(inner) => serde_json::to_value(inner), + CdpEvent::PageInterstitialShown(inner) => serde_json::to_value(inner), + CdpEvent::PageJavascriptDialogClosed(inner) => serde_json::to_value(inner), + CdpEvent::PageJavascriptDialogOpening(inner) => serde_json::to_value(inner), + CdpEvent::PageLifecycleEvent(inner) => serde_json::to_value(inner), + CdpEvent::PageBackForwardCacheNotUsed(inner) => serde_json::to_value(inner), + CdpEvent::PageLoadEventFired(inner) => serde_json::to_value(inner), + CdpEvent::PageNavigatedWithinDocument(inner) => serde_json::to_value(inner), + CdpEvent::PageScreencastFrame(inner) => serde_json::to_value(inner), + CdpEvent::PageScreencastVisibilityChanged(inner) => serde_json::to_value(inner), + CdpEvent::PageWindowOpen(inner) => serde_json::to_value(inner), + CdpEvent::PageCompilationCacheProduced(inner) => serde_json::to_value(inner), + CdpEvent::PerformanceMetrics(inner) => serde_json::to_value(inner), + CdpEvent::PerformanceTimelineTimelineEventAdded(inner) => { + serde_json::to_value(inner) + } + CdpEvent::PreloadRuleSetUpdated(inner) => serde_json::to_value(inner), + CdpEvent::PreloadRuleSetRemoved(inner) => serde_json::to_value(inner), + CdpEvent::PreloadPreloadEnabledStateUpdated(inner) => serde_json::to_value(inner), + CdpEvent::PreloadPrefetchStatusUpdated(inner) => serde_json::to_value(inner), + CdpEvent::PreloadPrerenderStatusUpdated(inner) => serde_json::to_value(inner), + CdpEvent::PreloadPreloadingAttemptSourcesUpdated(inner) => { + serde_json::to_value(inner) + } + CdpEvent::SecurityVisibleSecurityStateChanged(inner) => serde_json::to_value(inner), + CdpEvent::ServiceWorkerWorkerErrorReported(inner) => serde_json::to_value(inner), + CdpEvent::ServiceWorkerWorkerRegistrationUpdated(inner) => { + serde_json::to_value(inner) + } + CdpEvent::ServiceWorkerWorkerVersionUpdated(inner) => serde_json::to_value(inner), + CdpEvent::StorageCacheStorageContentUpdated(inner) => serde_json::to_value(inner), + CdpEvent::StorageCacheStorageListUpdated(inner) => serde_json::to_value(inner), + CdpEvent::StorageIndexedDbContentUpdated(inner) => serde_json::to_value(inner), + CdpEvent::StorageIndexedDbListUpdated(inner) => serde_json::to_value(inner), + CdpEvent::StorageInterestGroupAccessed(inner) => serde_json::to_value(inner), + CdpEvent::StorageInterestGroupAuctionEventOccurred(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageInterestGroupAuctionNetworkRequestCreated(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageSharedStorageAccessed(inner) => serde_json::to_value(inner), + CdpEvent::StorageSharedStorageWorkletOperationExecutionFinished(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageStorageBucketCreatedOrUpdated(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageStorageBucketDeleted(inner) => serde_json::to_value(inner), + CdpEvent::StorageAttributionReportingSourceRegistered(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageAttributionReportingTriggerRegistered(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageAttributionReportingReportSent(inner) => { + serde_json::to_value(inner) + } + CdpEvent::StorageAttributionReportingVerboseDebugReportSent(inner) => { + serde_json::to_value(inner) + } + CdpEvent::TargetAttachedToTarget(inner) => serde_json::to_value(inner), + CdpEvent::TargetDetachedFromTarget(inner) => serde_json::to_value(inner), + CdpEvent::TargetReceivedMessageFromTarget(inner) => serde_json::to_value(inner), + CdpEvent::TargetTargetCreated(inner) => serde_json::to_value(inner), + CdpEvent::TargetTargetDestroyed(inner) => serde_json::to_value(inner), + CdpEvent::TargetTargetCrashed(inner) => serde_json::to_value(inner), + CdpEvent::TargetTargetInfoChanged(inner) => serde_json::to_value(inner), + CdpEvent::TetheringAccepted(inner) => serde_json::to_value(inner), + CdpEvent::TracingBufferUsage(inner) => serde_json::to_value(inner), + CdpEvent::TracingDataCollected(inner) => serde_json::to_value(inner), + CdpEvent::TracingTracingComplete(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioContextCreated(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioContextWillBeDestroyed(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioContextChanged(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioAudioListenerCreated(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioAudioListenerWillBeDestroyed(inner) => { + serde_json::to_value(inner) + } + CdpEvent::WebAudioAudioNodeCreated(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioAudioNodeWillBeDestroyed(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioAudioParamCreated(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioAudioParamWillBeDestroyed(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioNodesConnected(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioNodesDisconnected(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioNodeParamConnected(inner) => serde_json::to_value(inner), + CdpEvent::WebAudioNodeParamDisconnected(inner) => serde_json::to_value(inner), + CdpEvent::WebAuthnCredentialAdded(inner) => serde_json::to_value(inner), + CdpEvent::WebAuthnCredentialDeleted(inner) => serde_json::to_value(inner), + CdpEvent::WebAuthnCredentialUpdated(inner) => serde_json::to_value(inner), + CdpEvent::WebAuthnCredentialAsserted(inner) => serde_json::to_value(inner), + CdpEvent::Other(val) => Ok(val), + } + } + pub fn into_event(self) -> ::std::result::Result, serde_json::Value> { + match self { + CdpEvent::DebuggerPaused(event) => Ok(Box::new(event)), + CdpEvent::DebuggerResumed(event) => Ok(Box::new(event)), + CdpEvent::DebuggerScriptFailedToParse(event) => Ok(Box::new(*event)), + CdpEvent::DebuggerScriptParsed(event) => Ok(Box::new(*event)), + CdpEvent::HeapProfilerAddHeapSnapshotChunk(event) => Ok(Box::new(event)), + CdpEvent::HeapProfilerHeapStatsUpdate(event) => Ok(Box::new(event)), + CdpEvent::HeapProfilerLastSeenObjectId(event) => Ok(Box::new(event)), + CdpEvent::HeapProfilerReportHeapSnapshotProgress(event) => Ok(Box::new(event)), + CdpEvent::HeapProfilerResetProfiles(event) => Ok(Box::new(event)), + CdpEvent::ProfilerConsoleProfileFinished(event) => Ok(Box::new(event)), + CdpEvent::ProfilerConsoleProfileStarted(event) => Ok(Box::new(event)), + CdpEvent::ProfilerPreciseCoverageDeltaUpdate(event) => Ok(Box::new(event)), + CdpEvent::RuntimeBindingCalled(event) => Ok(Box::new(event)), + CdpEvent::RuntimeConsoleApiCalled(event) => Ok(Box::new(event)), + CdpEvent::RuntimeExceptionRevoked(event) => Ok(Box::new(event)), + CdpEvent::RuntimeExceptionThrown(event) => Ok(Box::new(*event)), + CdpEvent::RuntimeExecutionContextCreated(event) => Ok(Box::new(event)), + CdpEvent::RuntimeExecutionContextDestroyed(event) => Ok(Box::new(event)), + CdpEvent::RuntimeExecutionContextsCleared(event) => Ok(Box::new(event)), + CdpEvent::RuntimeInspectRequested(event) => Ok(Box::new(*event)), + CdpEvent::AccessibilityLoadComplete(event) => Ok(Box::new(*event)), + CdpEvent::AccessibilityNodesUpdated(event) => Ok(Box::new(event)), + CdpEvent::AnimationAnimationCanceled(event) => Ok(Box::new(event)), + CdpEvent::AnimationAnimationCreated(event) => Ok(Box::new(event)), + CdpEvent::AnimationAnimationStarted(event) => Ok(Box::new(*event)), + CdpEvent::AnimationAnimationUpdated(event) => Ok(Box::new(*event)), + CdpEvent::AuditsIssueAdded(event) => Ok(Box::new(*event)), + CdpEvent::AutofillAddressFormFilled(event) => Ok(Box::new(event)), + CdpEvent::BackgroundServiceRecordingStateChanged(event) => Ok(Box::new(event)), + CdpEvent::BackgroundServiceBackgroundServiceEventReceived(event) => { + Ok(Box::new(event)) + } + CdpEvent::BluetoothEmulationGattOperationReceived(event) => Ok(Box::new(event)), + CdpEvent::BluetoothEmulationCharacteristicOperationReceived(event) => { + Ok(Box::new(event)) + } + CdpEvent::BluetoothEmulationDescriptorOperationReceived(event) => { + Ok(Box::new(event)) + } + CdpEvent::BrowserDownloadWillBegin(event) => Ok(Box::new(event)), + CdpEvent::BrowserDownloadProgress(event) => Ok(Box::new(event)), + CdpEvent::CssFontsUpdated(event) => Ok(Box::new(*event)), + CdpEvent::CssMediaQueryResultChanged(event) => Ok(Box::new(event)), + CdpEvent::CssStyleSheetAdded(event) => Ok(Box::new(event)), + CdpEvent::CssStyleSheetChanged(event) => Ok(Box::new(event)), + CdpEvent::CssStyleSheetRemoved(event) => Ok(Box::new(event)), + CdpEvent::CssComputedStyleUpdated(event) => Ok(Box::new(event)), + CdpEvent::CastSinksUpdated(event) => Ok(Box::new(event)), + CdpEvent::CastIssueUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomAttributeModified(event) => Ok(Box::new(event)), + CdpEvent::DomAdoptedStyleSheetsModified(event) => Ok(Box::new(event)), + CdpEvent::DomAttributeRemoved(event) => Ok(Box::new(event)), + CdpEvent::DomCharacterDataModified(event) => Ok(Box::new(event)), + CdpEvent::DomChildNodeCountUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomChildNodeInserted(event) => Ok(Box::new(*event)), + CdpEvent::DomChildNodeRemoved(event) => Ok(Box::new(event)), + CdpEvent::DomDistributedNodesUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomDocumentUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomInlineStyleInvalidated(event) => Ok(Box::new(event)), + CdpEvent::DomPseudoElementAdded(event) => Ok(Box::new(*event)), + CdpEvent::DomTopLayerElementsUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomScrollableFlagUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomAffectedByStartingStylesFlagUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomPseudoElementRemoved(event) => Ok(Box::new(event)), + CdpEvent::DomSetChildNodes(event) => Ok(Box::new(event)), + CdpEvent::DomShadowRootPopped(event) => Ok(Box::new(event)), + CdpEvent::DomShadowRootPushed(event) => Ok(Box::new(*event)), + CdpEvent::DomStorageDomStorageItemAdded(event) => Ok(Box::new(event)), + CdpEvent::DomStorageDomStorageItemRemoved(event) => Ok(Box::new(event)), + CdpEvent::DomStorageDomStorageItemUpdated(event) => Ok(Box::new(event)), + CdpEvent::DomStorageDomStorageItemsCleared(event) => Ok(Box::new(event)), + CdpEvent::DeviceAccessDeviceRequestPrompted(event) => Ok(Box::new(event)), + CdpEvent::EmulationVirtualTimeBudgetExpired(event) => Ok(Box::new(event)), + CdpEvent::FedCmDialogShown(event) => Ok(Box::new(event)), + CdpEvent::FedCmDialogClosed(event) => Ok(Box::new(event)), + CdpEvent::FetchRequestPaused(event) => Ok(Box::new(*event)), + CdpEvent::FetchAuthRequired(event) => Ok(Box::new(*event)), + CdpEvent::InputDragIntercepted(event) => Ok(Box::new(event)), + CdpEvent::InspectorDetached(event) => Ok(Box::new(event)), + CdpEvent::InspectorTargetCrashed(event) => Ok(Box::new(event)), + CdpEvent::InspectorTargetReloadedAfterCrash(event) => Ok(Box::new(event)), + CdpEvent::InspectorWorkerScriptLoaded(event) => Ok(Box::new(event)), + CdpEvent::LayerTreeLayerPainted(event) => Ok(Box::new(event)), + CdpEvent::LayerTreeLayerTreeDidChange(event) => Ok(Box::new(event)), + CdpEvent::LogEntryAdded(event) => Ok(Box::new(*event)), + CdpEvent::MediaPlayerPropertiesChanged(event) => Ok(Box::new(event)), + CdpEvent::MediaPlayerEventsAdded(event) => Ok(Box::new(event)), + CdpEvent::MediaPlayerMessagesLogged(event) => Ok(Box::new(event)), + CdpEvent::MediaPlayerErrorsRaised(event) => Ok(Box::new(event)), + CdpEvent::MediaPlayerCreated(event) => Ok(Box::new(event)), + CdpEvent::NetworkDataReceived(event) => Ok(Box::new(event)), + CdpEvent::NetworkEventSourceMessageReceived(event) => Ok(Box::new(event)), + CdpEvent::NetworkLoadingFailed(event) => Ok(Box::new(event)), + CdpEvent::NetworkLoadingFinished(event) => Ok(Box::new(event)), + CdpEvent::NetworkRequestServedFromCache(event) => Ok(Box::new(event)), + CdpEvent::NetworkRequestWillBeSent(event) => Ok(Box::new(*event)), + CdpEvent::NetworkResourceChangedPriority(event) => Ok(Box::new(event)), + CdpEvent::NetworkSignedExchangeReceived(event) => Ok(Box::new(*event)), + CdpEvent::NetworkResponseReceived(event) => Ok(Box::new(*event)), + CdpEvent::NetworkWebSocketClosed(event) => Ok(Box::new(event)), + CdpEvent::NetworkWebSocketCreated(event) => Ok(Box::new(*event)), + CdpEvent::NetworkWebSocketFrameError(event) => Ok(Box::new(event)), + CdpEvent::NetworkWebSocketFrameReceived(event) => Ok(Box::new(event)), + CdpEvent::NetworkWebSocketFrameSent(event) => Ok(Box::new(event)), + CdpEvent::NetworkWebSocketHandshakeResponseReceived(event) => Ok(Box::new(*event)), + CdpEvent::NetworkWebSocketWillSendHandshakeRequest(event) => Ok(Box::new(event)), + CdpEvent::NetworkWebTransportCreated(event) => Ok(Box::new(*event)), + CdpEvent::NetworkWebTransportConnectionEstablished(event) => Ok(Box::new(event)), + CdpEvent::NetworkWebTransportClosed(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectTcpSocketCreated(event) => Ok(Box::new(*event)), + CdpEvent::NetworkDirectTcpSocketOpened(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectTcpSocketAborted(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectTcpSocketClosed(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectTcpSocketChunkSent(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectTcpSocketChunkReceived(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketJoinedMulticastGroup(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketLeftMulticastGroup(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketCreated(event) => Ok(Box::new(*event)), + CdpEvent::NetworkDirectUdpSocketOpened(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketAborted(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketClosed(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketChunkSent(event) => Ok(Box::new(event)), + CdpEvent::NetworkDirectUdpSocketChunkReceived(event) => Ok(Box::new(event)), + CdpEvent::NetworkRequestWillBeSentExtraInfo(event) => Ok(Box::new(event)), + CdpEvent::NetworkResponseReceivedExtraInfo(event) => Ok(Box::new(*event)), + CdpEvent::NetworkResponseReceivedEarlyHints(event) => Ok(Box::new(event)), + CdpEvent::NetworkTrustTokenOperationDone(event) => Ok(Box::new(event)), + CdpEvent::NetworkPolicyUpdated(event) => Ok(Box::new(event)), + CdpEvent::NetworkReportingApiReportAdded(event) => Ok(Box::new(event)), + CdpEvent::NetworkReportingApiReportUpdated(event) => Ok(Box::new(event)), + CdpEvent::NetworkReportingApiEndpointsChangedForOrigin(event) => { + Ok(Box::new(event)) + } + CdpEvent::NetworkDeviceBoundSessionsAdded(event) => Ok(Box::new(event)), + CdpEvent::NetworkDeviceBoundSessionEventOccurred(event) => Ok(Box::new(*event)), + CdpEvent::OverlayInspectNodeRequested(event) => Ok(Box::new(event)), + CdpEvent::OverlayNodeHighlightRequested(event) => Ok(Box::new(event)), + CdpEvent::OverlayScreenshotRequested(event) => Ok(Box::new(event)), + CdpEvent::OverlayInspectModeCanceled(event) => Ok(Box::new(event)), + CdpEvent::PageDomContentEventFired(event) => Ok(Box::new(event)), + CdpEvent::PageFileChooserOpened(event) => Ok(Box::new(event)), + CdpEvent::PageFrameAttached(event) => Ok(Box::new(event)), + CdpEvent::PageFrameDetached(event) => Ok(Box::new(event)), + CdpEvent::PageFrameSubtreeWillBeDetached(event) => Ok(Box::new(event)), + CdpEvent::PageFrameNavigated(event) => Ok(Box::new(*event)), + CdpEvent::PageDocumentOpened(event) => Ok(Box::new(*event)), + CdpEvent::PageFrameResized(event) => Ok(Box::new(event)), + CdpEvent::PageFrameStartedNavigating(event) => Ok(Box::new(event)), + CdpEvent::PageFrameRequestedNavigation(event) => Ok(Box::new(event)), + CdpEvent::PageFrameStartedLoading(event) => Ok(Box::new(event)), + CdpEvent::PageFrameStoppedLoading(event) => Ok(Box::new(event)), + CdpEvent::PageInterstitialHidden(event) => Ok(Box::new(event)), + CdpEvent::PageInterstitialShown(event) => Ok(Box::new(event)), + CdpEvent::PageJavascriptDialogClosed(event) => Ok(Box::new(event)), + CdpEvent::PageJavascriptDialogOpening(event) => Ok(Box::new(event)), + CdpEvent::PageLifecycleEvent(event) => Ok(Box::new(event)), + CdpEvent::PageBackForwardCacheNotUsed(event) => Ok(Box::new(event)), + CdpEvent::PageLoadEventFired(event) => Ok(Box::new(event)), + CdpEvent::PageNavigatedWithinDocument(event) => Ok(Box::new(event)), + CdpEvent::PageScreencastFrame(event) => Ok(Box::new(event)), + CdpEvent::PageScreencastVisibilityChanged(event) => Ok(Box::new(event)), + CdpEvent::PageWindowOpen(event) => Ok(Box::new(event)), + CdpEvent::PageCompilationCacheProduced(event) => Ok(Box::new(event)), + CdpEvent::PerformanceMetrics(event) => Ok(Box::new(event)), + CdpEvent::PerformanceTimelineTimelineEventAdded(event) => Ok(Box::new(*event)), + CdpEvent::PreloadRuleSetUpdated(event) => Ok(Box::new(*event)), + CdpEvent::PreloadRuleSetRemoved(event) => Ok(Box::new(event)), + CdpEvent::PreloadPreloadEnabledStateUpdated(event) => Ok(Box::new(event)), + CdpEvent::PreloadPrefetchStatusUpdated(event) => Ok(Box::new(*event)), + CdpEvent::PreloadPrerenderStatusUpdated(event) => Ok(Box::new(event)), + CdpEvent::PreloadPreloadingAttemptSourcesUpdated(event) => Ok(Box::new(event)), + CdpEvent::SecurityVisibleSecurityStateChanged(event) => Ok(Box::new(*event)), + CdpEvent::ServiceWorkerWorkerErrorReported(event) => Ok(Box::new(event)), + CdpEvent::ServiceWorkerWorkerRegistrationUpdated(event) => Ok(Box::new(event)), + CdpEvent::ServiceWorkerWorkerVersionUpdated(event) => Ok(Box::new(event)), + CdpEvent::StorageCacheStorageContentUpdated(event) => Ok(Box::new(event)), + CdpEvent::StorageCacheStorageListUpdated(event) => Ok(Box::new(event)), + CdpEvent::StorageIndexedDbContentUpdated(event) => Ok(Box::new(event)), + CdpEvent::StorageIndexedDbListUpdated(event) => Ok(Box::new(event)), + CdpEvent::StorageInterestGroupAccessed(event) => Ok(Box::new(event)), + CdpEvent::StorageInterestGroupAuctionEventOccurred(event) => Ok(Box::new(event)), + CdpEvent::StorageInterestGroupAuctionNetworkRequestCreated(event) => { + Ok(Box::new(event)) + } + CdpEvent::StorageSharedStorageAccessed(event) => Ok(Box::new(*event)), + CdpEvent::StorageSharedStorageWorkletOperationExecutionFinished(event) => { + Ok(Box::new(event)) + } + CdpEvent::StorageStorageBucketCreatedOrUpdated(event) => Ok(Box::new(event)), + CdpEvent::StorageStorageBucketDeleted(event) => Ok(Box::new(event)), + CdpEvent::StorageAttributionReportingSourceRegistered(event) => { + Ok(Box::new(*event)) + } + CdpEvent::StorageAttributionReportingTriggerRegistered(event) => { + Ok(Box::new(*event)) + } + CdpEvent::StorageAttributionReportingReportSent(event) => Ok(Box::new(event)), + CdpEvent::StorageAttributionReportingVerboseDebugReportSent(event) => { + Ok(Box::new(event)) + } + CdpEvent::TargetAttachedToTarget(event) => Ok(Box::new(*event)), + CdpEvent::TargetDetachedFromTarget(event) => Ok(Box::new(event)), + CdpEvent::TargetReceivedMessageFromTarget(event) => Ok(Box::new(event)), + CdpEvent::TargetTargetCreated(event) => Ok(Box::new(*event)), + CdpEvent::TargetTargetDestroyed(event) => Ok(Box::new(event)), + CdpEvent::TargetTargetCrashed(event) => Ok(Box::new(event)), + CdpEvent::TargetTargetInfoChanged(event) => Ok(Box::new(*event)), + CdpEvent::TetheringAccepted(event) => Ok(Box::new(event)), + CdpEvent::TracingBufferUsage(event) => Ok(Box::new(event)), + CdpEvent::TracingDataCollected(event) => Ok(Box::new(event)), + CdpEvent::TracingTracingComplete(event) => Ok(Box::new(event)), + CdpEvent::WebAudioContextCreated(event) => Ok(Box::new(event)), + CdpEvent::WebAudioContextWillBeDestroyed(event) => Ok(Box::new(event)), + CdpEvent::WebAudioContextChanged(event) => Ok(Box::new(event)), + CdpEvent::WebAudioAudioListenerCreated(event) => Ok(Box::new(event)), + CdpEvent::WebAudioAudioListenerWillBeDestroyed(event) => Ok(Box::new(event)), + CdpEvent::WebAudioAudioNodeCreated(event) => Ok(Box::new(event)), + CdpEvent::WebAudioAudioNodeWillBeDestroyed(event) => Ok(Box::new(event)), + CdpEvent::WebAudioAudioParamCreated(event) => Ok(Box::new(event)), + CdpEvent::WebAudioAudioParamWillBeDestroyed(event) => Ok(Box::new(event)), + CdpEvent::WebAudioNodesConnected(event) => Ok(Box::new(event)), + CdpEvent::WebAudioNodesDisconnected(event) => Ok(Box::new(event)), + CdpEvent::WebAudioNodeParamConnected(event) => Ok(Box::new(event)), + CdpEvent::WebAudioNodeParamDisconnected(event) => Ok(Box::new(event)), + CdpEvent::WebAuthnCredentialAdded(event) => Ok(Box::new(*event)), + CdpEvent::WebAuthnCredentialDeleted(event) => Ok(Box::new(event)), + CdpEvent::WebAuthnCredentialUpdated(event) => Ok(Box::new(*event)), + CdpEvent::WebAuthnCredentialAsserted(event) => Ok(Box::new(*event)), + CdpEvent::Other(other) => Err(other), + } + } + } + use serde::de::{self, Deserializer, MapAccess, Visitor}; + use std::fmt; + impl<'de> Deserialize<'de> for CdpEventMessage { + fn deserialize(deserializer: D) -> Result>::Error> + where + D: Deserializer<'de>, + { + enum Field { + Method, + Session, + Params, + } + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FieldVisitor; + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`method` or `sessionId` or `params`") + } + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "method" => Ok(Field::Method), + "sessionId" => Ok(Field::Session), + "params" => Ok(Field::Params), + _ => Err(de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(FieldVisitor) + } + } + struct MessageVisitor; + impl<'de> Visitor<'de> for MessageVisitor { + type Value = CdpEventMessage; + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct CdpEventMessage") + } + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut method = None; + let mut session_id = None; + let mut params = None; + while let Some(key) = map.next_key()? { + match key { + Field::Method => { + if method.is_some() { + return Err(de::Error::duplicate_field("method")); + } + method = Some(map.next_value::()?); + } + Field::Session => { + if session_id.is_some() { + return Err(de::Error::duplicate_field("sessionId")); + } + session_id = Some(map.next_value::()?); + } + Field::Params => { + if params.is_some() { + return Err(de::Error::duplicate_field("params")); + } + params = Some (match method . as_ref () . ok_or_else (|| de :: Error :: missing_field ("params")) ? . as_str () { super :: js_protocol :: debugger :: EventPaused :: IDENTIFIER => CdpEvent :: DebuggerPaused (map . next_value :: < super :: js_protocol :: debugger :: EventPaused > () ?) , super :: js_protocol :: debugger :: EventResumed :: IDENTIFIER => CdpEvent :: DebuggerResumed (map . next_value :: < super :: js_protocol :: debugger :: EventResumed > () ?) , super :: js_protocol :: debugger :: EventScriptFailedToParse :: IDENTIFIER => CdpEvent :: DebuggerScriptFailedToParse (Box :: new (map . next_value :: < super :: js_protocol :: debugger :: EventScriptFailedToParse > () ?)) , super :: js_protocol :: debugger :: EventScriptParsed :: IDENTIFIER => CdpEvent :: DebuggerScriptParsed (Box :: new (map . next_value :: < super :: js_protocol :: debugger :: EventScriptParsed > () ?)) , super :: js_protocol :: heap_profiler :: EventAddHeapSnapshotChunk :: IDENTIFIER => CdpEvent :: HeapProfilerAddHeapSnapshotChunk (map . next_value :: < super :: js_protocol :: heap_profiler :: EventAddHeapSnapshotChunk > () ?) , super :: js_protocol :: heap_profiler :: EventHeapStatsUpdate :: IDENTIFIER => CdpEvent :: HeapProfilerHeapStatsUpdate (map . next_value :: < super :: js_protocol :: heap_profiler :: EventHeapStatsUpdate > () ?) , super :: js_protocol :: heap_profiler :: EventLastSeenObjectId :: IDENTIFIER => CdpEvent :: HeapProfilerLastSeenObjectId (map . next_value :: < super :: js_protocol :: heap_profiler :: EventLastSeenObjectId > () ?) , super :: js_protocol :: heap_profiler :: EventReportHeapSnapshotProgress :: IDENTIFIER => CdpEvent :: HeapProfilerReportHeapSnapshotProgress (map . next_value :: < super :: js_protocol :: heap_profiler :: EventReportHeapSnapshotProgress > () ?) , super :: js_protocol :: heap_profiler :: EventResetProfiles :: IDENTIFIER => CdpEvent :: HeapProfilerResetProfiles (map . next_value :: < super :: js_protocol :: heap_profiler :: EventResetProfiles > () ?) , super :: js_protocol :: profiler :: EventConsoleProfileFinished :: IDENTIFIER => CdpEvent :: ProfilerConsoleProfileFinished (map . next_value :: < super :: js_protocol :: profiler :: EventConsoleProfileFinished > () ?) , super :: js_protocol :: profiler :: EventConsoleProfileStarted :: IDENTIFIER => CdpEvent :: ProfilerConsoleProfileStarted (map . next_value :: < super :: js_protocol :: profiler :: EventConsoleProfileStarted > () ?) , super :: js_protocol :: profiler :: EventPreciseCoverageDeltaUpdate :: IDENTIFIER => CdpEvent :: ProfilerPreciseCoverageDeltaUpdate (map . next_value :: < super :: js_protocol :: profiler :: EventPreciseCoverageDeltaUpdate > () ?) , super :: js_protocol :: runtime :: EventBindingCalled :: IDENTIFIER => CdpEvent :: RuntimeBindingCalled (map . next_value :: < super :: js_protocol :: runtime :: EventBindingCalled > () ?) , super :: js_protocol :: runtime :: EventConsoleApiCalled :: IDENTIFIER => CdpEvent :: RuntimeConsoleApiCalled (map . next_value :: < super :: js_protocol :: runtime :: EventConsoleApiCalled > () ?) , super :: js_protocol :: runtime :: EventExceptionRevoked :: IDENTIFIER => CdpEvent :: RuntimeExceptionRevoked (map . next_value :: < super :: js_protocol :: runtime :: EventExceptionRevoked > () ?) , super :: js_protocol :: runtime :: EventExceptionThrown :: IDENTIFIER => CdpEvent :: RuntimeExceptionThrown (Box :: new (map . next_value :: < super :: js_protocol :: runtime :: EventExceptionThrown > () ?)) , super :: js_protocol :: runtime :: EventExecutionContextCreated :: IDENTIFIER => CdpEvent :: RuntimeExecutionContextCreated (map . next_value :: < super :: js_protocol :: runtime :: EventExecutionContextCreated > () ?) , super :: js_protocol :: runtime :: EventExecutionContextDestroyed :: IDENTIFIER => CdpEvent :: RuntimeExecutionContextDestroyed (map . next_value :: < super :: js_protocol :: runtime :: EventExecutionContextDestroyed > () ?) , super :: js_protocol :: runtime :: EventExecutionContextsCleared :: IDENTIFIER => CdpEvent :: RuntimeExecutionContextsCleared (map . next_value :: < super :: js_protocol :: runtime :: EventExecutionContextsCleared > () ?) , super :: js_protocol :: runtime :: EventInspectRequested :: IDENTIFIER => CdpEvent :: RuntimeInspectRequested (Box :: new (map . next_value :: < super :: js_protocol :: runtime :: EventInspectRequested > () ?)) , super :: browser_protocol :: accessibility :: EventLoadComplete :: IDENTIFIER => CdpEvent :: AccessibilityLoadComplete (Box :: new (map . next_value :: < super :: browser_protocol :: accessibility :: EventLoadComplete > () ?)) , super :: browser_protocol :: accessibility :: EventNodesUpdated :: IDENTIFIER => CdpEvent :: AccessibilityNodesUpdated (map . next_value :: < super :: browser_protocol :: accessibility :: EventNodesUpdated > () ?) , super :: browser_protocol :: animation :: EventAnimationCanceled :: IDENTIFIER => CdpEvent :: AnimationAnimationCanceled (map . next_value :: < super :: browser_protocol :: animation :: EventAnimationCanceled > () ?) , super :: browser_protocol :: animation :: EventAnimationCreated :: IDENTIFIER => CdpEvent :: AnimationAnimationCreated (map . next_value :: < super :: browser_protocol :: animation :: EventAnimationCreated > () ?) , super :: browser_protocol :: animation :: EventAnimationStarted :: IDENTIFIER => CdpEvent :: AnimationAnimationStarted (Box :: new (map . next_value :: < super :: browser_protocol :: animation :: EventAnimationStarted > () ?)) , super :: browser_protocol :: animation :: EventAnimationUpdated :: IDENTIFIER => CdpEvent :: AnimationAnimationUpdated (Box :: new (map . next_value :: < super :: browser_protocol :: animation :: EventAnimationUpdated > () ?)) , super :: browser_protocol :: audits :: EventIssueAdded :: IDENTIFIER => CdpEvent :: AuditsIssueAdded (Box :: new (map . next_value :: < super :: browser_protocol :: audits :: EventIssueAdded > () ?)) , super :: browser_protocol :: autofill :: EventAddressFormFilled :: IDENTIFIER => CdpEvent :: AutofillAddressFormFilled (map . next_value :: < super :: browser_protocol :: autofill :: EventAddressFormFilled > () ?) , super :: browser_protocol :: background_service :: EventRecordingStateChanged :: IDENTIFIER => CdpEvent :: BackgroundServiceRecordingStateChanged (map . next_value :: < super :: browser_protocol :: background_service :: EventRecordingStateChanged > () ?) , super :: browser_protocol :: background_service :: EventBackgroundServiceEventReceived :: IDENTIFIER => CdpEvent :: BackgroundServiceBackgroundServiceEventReceived (map . next_value :: < super :: browser_protocol :: background_service :: EventBackgroundServiceEventReceived > () ?) , super :: browser_protocol :: bluetooth_emulation :: EventGattOperationReceived :: IDENTIFIER => CdpEvent :: BluetoothEmulationGattOperationReceived (map . next_value :: < super :: browser_protocol :: bluetooth_emulation :: EventGattOperationReceived > () ?) , super :: browser_protocol :: bluetooth_emulation :: EventCharacteristicOperationReceived :: IDENTIFIER => CdpEvent :: BluetoothEmulationCharacteristicOperationReceived (map . next_value :: < super :: browser_protocol :: bluetooth_emulation :: EventCharacteristicOperationReceived > () ?) , super :: browser_protocol :: bluetooth_emulation :: EventDescriptorOperationReceived :: IDENTIFIER => CdpEvent :: BluetoothEmulationDescriptorOperationReceived (map . next_value :: < super :: browser_protocol :: bluetooth_emulation :: EventDescriptorOperationReceived > () ?) , super :: browser_protocol :: browser :: EventDownloadWillBegin :: IDENTIFIER => CdpEvent :: BrowserDownloadWillBegin (map . next_value :: < super :: browser_protocol :: browser :: EventDownloadWillBegin > () ?) , super :: browser_protocol :: browser :: EventDownloadProgress :: IDENTIFIER => CdpEvent :: BrowserDownloadProgress (map . next_value :: < super :: browser_protocol :: browser :: EventDownloadProgress > () ?) , super :: browser_protocol :: css :: EventFontsUpdated :: IDENTIFIER => CdpEvent :: CssFontsUpdated (Box :: new (map . next_value :: < super :: browser_protocol :: css :: EventFontsUpdated > () ?)) , super :: browser_protocol :: css :: EventMediaQueryResultChanged :: IDENTIFIER => CdpEvent :: CssMediaQueryResultChanged (map . next_value :: < super :: browser_protocol :: css :: EventMediaQueryResultChanged > () ?) , super :: browser_protocol :: css :: EventStyleSheetAdded :: IDENTIFIER => CdpEvent :: CssStyleSheetAdded (map . next_value :: < super :: browser_protocol :: css :: EventStyleSheetAdded > () ?) , super :: browser_protocol :: css :: EventStyleSheetChanged :: IDENTIFIER => CdpEvent :: CssStyleSheetChanged (map . next_value :: < super :: browser_protocol :: css :: EventStyleSheetChanged > () ?) , super :: browser_protocol :: css :: EventStyleSheetRemoved :: IDENTIFIER => CdpEvent :: CssStyleSheetRemoved (map . next_value :: < super :: browser_protocol :: css :: EventStyleSheetRemoved > () ?) , super :: browser_protocol :: css :: EventComputedStyleUpdated :: IDENTIFIER => CdpEvent :: CssComputedStyleUpdated (map . next_value :: < super :: browser_protocol :: css :: EventComputedStyleUpdated > () ?) , super :: browser_protocol :: cast :: EventSinksUpdated :: IDENTIFIER => CdpEvent :: CastSinksUpdated (map . next_value :: < super :: browser_protocol :: cast :: EventSinksUpdated > () ?) , super :: browser_protocol :: cast :: EventIssueUpdated :: IDENTIFIER => CdpEvent :: CastIssueUpdated (map . next_value :: < super :: browser_protocol :: cast :: EventIssueUpdated > () ?) , super :: browser_protocol :: dom :: EventAttributeModified :: IDENTIFIER => CdpEvent :: DomAttributeModified (map . next_value :: < super :: browser_protocol :: dom :: EventAttributeModified > () ?) , super :: browser_protocol :: dom :: EventAdoptedStyleSheetsModified :: IDENTIFIER => CdpEvent :: DomAdoptedStyleSheetsModified (map . next_value :: < super :: browser_protocol :: dom :: EventAdoptedStyleSheetsModified > () ?) , super :: browser_protocol :: dom :: EventAttributeRemoved :: IDENTIFIER => CdpEvent :: DomAttributeRemoved (map . next_value :: < super :: browser_protocol :: dom :: EventAttributeRemoved > () ?) , super :: browser_protocol :: dom :: EventCharacterDataModified :: IDENTIFIER => CdpEvent :: DomCharacterDataModified (map . next_value :: < super :: browser_protocol :: dom :: EventCharacterDataModified > () ?) , super :: browser_protocol :: dom :: EventChildNodeCountUpdated :: IDENTIFIER => CdpEvent :: DomChildNodeCountUpdated (map . next_value :: < super :: browser_protocol :: dom :: EventChildNodeCountUpdated > () ?) , super :: browser_protocol :: dom :: EventChildNodeInserted :: IDENTIFIER => CdpEvent :: DomChildNodeInserted (Box :: new (map . next_value :: < super :: browser_protocol :: dom :: EventChildNodeInserted > () ?)) , super :: browser_protocol :: dom :: EventChildNodeRemoved :: IDENTIFIER => CdpEvent :: DomChildNodeRemoved (map . next_value :: < super :: browser_protocol :: dom :: EventChildNodeRemoved > () ?) , super :: browser_protocol :: dom :: EventDistributedNodesUpdated :: IDENTIFIER => CdpEvent :: DomDistributedNodesUpdated (map . next_value :: < super :: browser_protocol :: dom :: EventDistributedNodesUpdated > () ?) , super :: browser_protocol :: dom :: EventDocumentUpdated :: IDENTIFIER => CdpEvent :: DomDocumentUpdated (map . next_value :: < super :: browser_protocol :: dom :: EventDocumentUpdated > () ?) , super :: browser_protocol :: dom :: EventInlineStyleInvalidated :: IDENTIFIER => CdpEvent :: DomInlineStyleInvalidated (map . next_value :: < super :: browser_protocol :: dom :: EventInlineStyleInvalidated > () ?) , super :: browser_protocol :: dom :: EventPseudoElementAdded :: IDENTIFIER => CdpEvent :: DomPseudoElementAdded (Box :: new (map . next_value :: < super :: browser_protocol :: dom :: EventPseudoElementAdded > () ?)) , super :: browser_protocol :: dom :: EventTopLayerElementsUpdated :: IDENTIFIER => CdpEvent :: DomTopLayerElementsUpdated (map . next_value :: < super :: browser_protocol :: dom :: EventTopLayerElementsUpdated > () ?) , super :: browser_protocol :: dom :: EventScrollableFlagUpdated :: IDENTIFIER => CdpEvent :: DomScrollableFlagUpdated (map . next_value :: < super :: browser_protocol :: dom :: EventScrollableFlagUpdated > () ?) , super :: browser_protocol :: dom :: EventAffectedByStartingStylesFlagUpdated :: IDENTIFIER => CdpEvent :: DomAffectedByStartingStylesFlagUpdated (map . next_value :: < super :: browser_protocol :: dom :: EventAffectedByStartingStylesFlagUpdated > () ?) , super :: browser_protocol :: dom :: EventPseudoElementRemoved :: IDENTIFIER => CdpEvent :: DomPseudoElementRemoved (map . next_value :: < super :: browser_protocol :: dom :: EventPseudoElementRemoved > () ?) , super :: browser_protocol :: dom :: EventSetChildNodes :: IDENTIFIER => CdpEvent :: DomSetChildNodes (map . next_value :: < super :: browser_protocol :: dom :: EventSetChildNodes > () ?) , super :: browser_protocol :: dom :: EventShadowRootPopped :: IDENTIFIER => CdpEvent :: DomShadowRootPopped (map . next_value :: < super :: browser_protocol :: dom :: EventShadowRootPopped > () ?) , super :: browser_protocol :: dom :: EventShadowRootPushed :: IDENTIFIER => CdpEvent :: DomShadowRootPushed (Box :: new (map . next_value :: < super :: browser_protocol :: dom :: EventShadowRootPushed > () ?)) , super :: browser_protocol :: dom_storage :: EventDomStorageItemAdded :: IDENTIFIER => CdpEvent :: DomStorageDomStorageItemAdded (map . next_value :: < super :: browser_protocol :: dom_storage :: EventDomStorageItemAdded > () ?) , super :: browser_protocol :: dom_storage :: EventDomStorageItemRemoved :: IDENTIFIER => CdpEvent :: DomStorageDomStorageItemRemoved (map . next_value :: < super :: browser_protocol :: dom_storage :: EventDomStorageItemRemoved > () ?) , super :: browser_protocol :: dom_storage :: EventDomStorageItemUpdated :: IDENTIFIER => CdpEvent :: DomStorageDomStorageItemUpdated (map . next_value :: < super :: browser_protocol :: dom_storage :: EventDomStorageItemUpdated > () ?) , super :: browser_protocol :: dom_storage :: EventDomStorageItemsCleared :: IDENTIFIER => CdpEvent :: DomStorageDomStorageItemsCleared (map . next_value :: < super :: browser_protocol :: dom_storage :: EventDomStorageItemsCleared > () ?) , super :: browser_protocol :: device_access :: EventDeviceRequestPrompted :: IDENTIFIER => CdpEvent :: DeviceAccessDeviceRequestPrompted (map . next_value :: < super :: browser_protocol :: device_access :: EventDeviceRequestPrompted > () ?) , super :: browser_protocol :: emulation :: EventVirtualTimeBudgetExpired :: IDENTIFIER => CdpEvent :: EmulationVirtualTimeBudgetExpired (map . next_value :: < super :: browser_protocol :: emulation :: EventVirtualTimeBudgetExpired > () ?) , super :: browser_protocol :: fed_cm :: EventDialogShown :: IDENTIFIER => CdpEvent :: FedCmDialogShown (map . next_value :: < super :: browser_protocol :: fed_cm :: EventDialogShown > () ?) , super :: browser_protocol :: fed_cm :: EventDialogClosed :: IDENTIFIER => CdpEvent :: FedCmDialogClosed (map . next_value :: < super :: browser_protocol :: fed_cm :: EventDialogClosed > () ?) , super :: browser_protocol :: fetch :: EventRequestPaused :: IDENTIFIER => CdpEvent :: FetchRequestPaused (Box :: new (map . next_value :: < super :: browser_protocol :: fetch :: EventRequestPaused > () ?)) , super :: browser_protocol :: fetch :: EventAuthRequired :: IDENTIFIER => CdpEvent :: FetchAuthRequired (Box :: new (map . next_value :: < super :: browser_protocol :: fetch :: EventAuthRequired > () ?)) , super :: browser_protocol :: input :: EventDragIntercepted :: IDENTIFIER => CdpEvent :: InputDragIntercepted (map . next_value :: < super :: browser_protocol :: input :: EventDragIntercepted > () ?) , super :: browser_protocol :: inspector :: EventDetached :: IDENTIFIER => CdpEvent :: InspectorDetached (map . next_value :: < super :: browser_protocol :: inspector :: EventDetached > () ?) , super :: browser_protocol :: inspector :: EventTargetCrashed :: IDENTIFIER => CdpEvent :: InspectorTargetCrashed (map . next_value :: < super :: browser_protocol :: inspector :: EventTargetCrashed > () ?) , super :: browser_protocol :: inspector :: EventTargetReloadedAfterCrash :: IDENTIFIER => CdpEvent :: InspectorTargetReloadedAfterCrash (map . next_value :: < super :: browser_protocol :: inspector :: EventTargetReloadedAfterCrash > () ?) , super :: browser_protocol :: inspector :: EventWorkerScriptLoaded :: IDENTIFIER => CdpEvent :: InspectorWorkerScriptLoaded (map . next_value :: < super :: browser_protocol :: inspector :: EventWorkerScriptLoaded > () ?) , super :: browser_protocol :: layer_tree :: EventLayerPainted :: IDENTIFIER => CdpEvent :: LayerTreeLayerPainted (map . next_value :: < super :: browser_protocol :: layer_tree :: EventLayerPainted > () ?) , super :: browser_protocol :: layer_tree :: EventLayerTreeDidChange :: IDENTIFIER => CdpEvent :: LayerTreeLayerTreeDidChange (map . next_value :: < super :: browser_protocol :: layer_tree :: EventLayerTreeDidChange > () ?) , super :: browser_protocol :: log :: EventEntryAdded :: IDENTIFIER => CdpEvent :: LogEntryAdded (Box :: new (map . next_value :: < super :: browser_protocol :: log :: EventEntryAdded > () ?)) , super :: browser_protocol :: media :: EventPlayerPropertiesChanged :: IDENTIFIER => CdpEvent :: MediaPlayerPropertiesChanged (map . next_value :: < super :: browser_protocol :: media :: EventPlayerPropertiesChanged > () ?) , super :: browser_protocol :: media :: EventPlayerEventsAdded :: IDENTIFIER => CdpEvent :: MediaPlayerEventsAdded (map . next_value :: < super :: browser_protocol :: media :: EventPlayerEventsAdded > () ?) , super :: browser_protocol :: media :: EventPlayerMessagesLogged :: IDENTIFIER => CdpEvent :: MediaPlayerMessagesLogged (map . next_value :: < super :: browser_protocol :: media :: EventPlayerMessagesLogged > () ?) , super :: browser_protocol :: media :: EventPlayerErrorsRaised :: IDENTIFIER => CdpEvent :: MediaPlayerErrorsRaised (map . next_value :: < super :: browser_protocol :: media :: EventPlayerErrorsRaised > () ?) , super :: browser_protocol :: media :: EventPlayerCreated :: IDENTIFIER => CdpEvent :: MediaPlayerCreated (map . next_value :: < super :: browser_protocol :: media :: EventPlayerCreated > () ?) , super :: browser_protocol :: network :: EventDataReceived :: IDENTIFIER => CdpEvent :: NetworkDataReceived (map . next_value :: < super :: browser_protocol :: network :: EventDataReceived > () ?) , super :: browser_protocol :: network :: EventEventSourceMessageReceived :: IDENTIFIER => CdpEvent :: NetworkEventSourceMessageReceived (map . next_value :: < super :: browser_protocol :: network :: EventEventSourceMessageReceived > () ?) , super :: browser_protocol :: network :: EventLoadingFailed :: IDENTIFIER => CdpEvent :: NetworkLoadingFailed (map . next_value :: < super :: browser_protocol :: network :: EventLoadingFailed > () ?) , super :: browser_protocol :: network :: EventLoadingFinished :: IDENTIFIER => CdpEvent :: NetworkLoadingFinished (map . next_value :: < super :: browser_protocol :: network :: EventLoadingFinished > () ?) , super :: browser_protocol :: network :: EventRequestServedFromCache :: IDENTIFIER => CdpEvent :: NetworkRequestServedFromCache (map . next_value :: < super :: browser_protocol :: network :: EventRequestServedFromCache > () ?) , super :: browser_protocol :: network :: EventRequestWillBeSent :: IDENTIFIER => CdpEvent :: NetworkRequestWillBeSent (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventRequestWillBeSent > () ?)) , super :: browser_protocol :: network :: EventResourceChangedPriority :: IDENTIFIER => CdpEvent :: NetworkResourceChangedPriority (map . next_value :: < super :: browser_protocol :: network :: EventResourceChangedPriority > () ?) , super :: browser_protocol :: network :: EventSignedExchangeReceived :: IDENTIFIER => CdpEvent :: NetworkSignedExchangeReceived (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventSignedExchangeReceived > () ?)) , super :: browser_protocol :: network :: EventResponseReceived :: IDENTIFIER => CdpEvent :: NetworkResponseReceived (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventResponseReceived > () ?)) , super :: browser_protocol :: network :: EventWebSocketClosed :: IDENTIFIER => CdpEvent :: NetworkWebSocketClosed (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketClosed > () ?) , super :: browser_protocol :: network :: EventWebSocketCreated :: IDENTIFIER => CdpEvent :: NetworkWebSocketCreated (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketCreated > () ?)) , super :: browser_protocol :: network :: EventWebSocketFrameError :: IDENTIFIER => CdpEvent :: NetworkWebSocketFrameError (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketFrameError > () ?) , super :: browser_protocol :: network :: EventWebSocketFrameReceived :: IDENTIFIER => CdpEvent :: NetworkWebSocketFrameReceived (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketFrameReceived > () ?) , super :: browser_protocol :: network :: EventWebSocketFrameSent :: IDENTIFIER => CdpEvent :: NetworkWebSocketFrameSent (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketFrameSent > () ?) , super :: browser_protocol :: network :: EventWebSocketHandshakeResponseReceived :: IDENTIFIER => CdpEvent :: NetworkWebSocketHandshakeResponseReceived (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketHandshakeResponseReceived > () ?)) , super :: browser_protocol :: network :: EventWebSocketWillSendHandshakeRequest :: IDENTIFIER => CdpEvent :: NetworkWebSocketWillSendHandshakeRequest (map . next_value :: < super :: browser_protocol :: network :: EventWebSocketWillSendHandshakeRequest > () ?) , super :: browser_protocol :: network :: EventWebTransportCreated :: IDENTIFIER => CdpEvent :: NetworkWebTransportCreated (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventWebTransportCreated > () ?)) , super :: browser_protocol :: network :: EventWebTransportConnectionEstablished :: IDENTIFIER => CdpEvent :: NetworkWebTransportConnectionEstablished (map . next_value :: < super :: browser_protocol :: network :: EventWebTransportConnectionEstablished > () ?) , super :: browser_protocol :: network :: EventWebTransportClosed :: IDENTIFIER => CdpEvent :: NetworkWebTransportClosed (map . next_value :: < super :: browser_protocol :: network :: EventWebTransportClosed > () ?) , super :: browser_protocol :: network :: EventDirectTcpSocketCreated :: IDENTIFIER => CdpEvent :: NetworkDirectTcpSocketCreated (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventDirectTcpSocketCreated > () ?)) , super :: browser_protocol :: network :: EventDirectTcpSocketOpened :: IDENTIFIER => CdpEvent :: NetworkDirectTcpSocketOpened (map . next_value :: < super :: browser_protocol :: network :: EventDirectTcpSocketOpened > () ?) , super :: browser_protocol :: network :: EventDirectTcpSocketAborted :: IDENTIFIER => CdpEvent :: NetworkDirectTcpSocketAborted (map . next_value :: < super :: browser_protocol :: network :: EventDirectTcpSocketAborted > () ?) , super :: browser_protocol :: network :: EventDirectTcpSocketClosed :: IDENTIFIER => CdpEvent :: NetworkDirectTcpSocketClosed (map . next_value :: < super :: browser_protocol :: network :: EventDirectTcpSocketClosed > () ?) , super :: browser_protocol :: network :: EventDirectTcpSocketChunkSent :: IDENTIFIER => CdpEvent :: NetworkDirectTcpSocketChunkSent (map . next_value :: < super :: browser_protocol :: network :: EventDirectTcpSocketChunkSent > () ?) , super :: browser_protocol :: network :: EventDirectTcpSocketChunkReceived :: IDENTIFIER => CdpEvent :: NetworkDirectTcpSocketChunkReceived (map . next_value :: < super :: browser_protocol :: network :: EventDirectTcpSocketChunkReceived > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketJoinedMulticastGroup :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketJoinedMulticastGroup (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketJoinedMulticastGroup > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketLeftMulticastGroup :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketLeftMulticastGroup (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketLeftMulticastGroup > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketCreated :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketCreated (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketCreated > () ?)) , super :: browser_protocol :: network :: EventDirectUdpSocketOpened :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketOpened (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketOpened > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketAborted :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketAborted (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketAborted > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketClosed :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketClosed (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketClosed > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketChunkSent :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketChunkSent (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketChunkSent > () ?) , super :: browser_protocol :: network :: EventDirectUdpSocketChunkReceived :: IDENTIFIER => CdpEvent :: NetworkDirectUdpSocketChunkReceived (map . next_value :: < super :: browser_protocol :: network :: EventDirectUdpSocketChunkReceived > () ?) , super :: browser_protocol :: network :: EventRequestWillBeSentExtraInfo :: IDENTIFIER => CdpEvent :: NetworkRequestWillBeSentExtraInfo (map . next_value :: < super :: browser_protocol :: network :: EventRequestWillBeSentExtraInfo > () ?) , super :: browser_protocol :: network :: EventResponseReceivedExtraInfo :: IDENTIFIER => CdpEvent :: NetworkResponseReceivedExtraInfo (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventResponseReceivedExtraInfo > () ?)) , super :: browser_protocol :: network :: EventResponseReceivedEarlyHints :: IDENTIFIER => CdpEvent :: NetworkResponseReceivedEarlyHints (map . next_value :: < super :: browser_protocol :: network :: EventResponseReceivedEarlyHints > () ?) , super :: browser_protocol :: network :: EventTrustTokenOperationDone :: IDENTIFIER => CdpEvent :: NetworkTrustTokenOperationDone (map . next_value :: < super :: browser_protocol :: network :: EventTrustTokenOperationDone > () ?) , super :: browser_protocol :: network :: EventPolicyUpdated :: IDENTIFIER => CdpEvent :: NetworkPolicyUpdated (map . next_value :: < super :: browser_protocol :: network :: EventPolicyUpdated > () ?) , super :: browser_protocol :: network :: EventReportingApiReportAdded :: IDENTIFIER => CdpEvent :: NetworkReportingApiReportAdded (map . next_value :: < super :: browser_protocol :: network :: EventReportingApiReportAdded > () ?) , super :: browser_protocol :: network :: EventReportingApiReportUpdated :: IDENTIFIER => CdpEvent :: NetworkReportingApiReportUpdated (map . next_value :: < super :: browser_protocol :: network :: EventReportingApiReportUpdated > () ?) , super :: browser_protocol :: network :: EventReportingApiEndpointsChangedForOrigin :: IDENTIFIER => CdpEvent :: NetworkReportingApiEndpointsChangedForOrigin (map . next_value :: < super :: browser_protocol :: network :: EventReportingApiEndpointsChangedForOrigin > () ?) , super :: browser_protocol :: network :: EventDeviceBoundSessionsAdded :: IDENTIFIER => CdpEvent :: NetworkDeviceBoundSessionsAdded (map . next_value :: < super :: browser_protocol :: network :: EventDeviceBoundSessionsAdded > () ?) , super :: browser_protocol :: network :: EventDeviceBoundSessionEventOccurred :: IDENTIFIER => CdpEvent :: NetworkDeviceBoundSessionEventOccurred (Box :: new (map . next_value :: < super :: browser_protocol :: network :: EventDeviceBoundSessionEventOccurred > () ?)) , super :: browser_protocol :: overlay :: EventInspectNodeRequested :: IDENTIFIER => CdpEvent :: OverlayInspectNodeRequested (map . next_value :: < super :: browser_protocol :: overlay :: EventInspectNodeRequested > () ?) , super :: browser_protocol :: overlay :: EventNodeHighlightRequested :: IDENTIFIER => CdpEvent :: OverlayNodeHighlightRequested (map . next_value :: < super :: browser_protocol :: overlay :: EventNodeHighlightRequested > () ?) , super :: browser_protocol :: overlay :: EventScreenshotRequested :: IDENTIFIER => CdpEvent :: OverlayScreenshotRequested (map . next_value :: < super :: browser_protocol :: overlay :: EventScreenshotRequested > () ?) , super :: browser_protocol :: overlay :: EventInspectModeCanceled :: IDENTIFIER => CdpEvent :: OverlayInspectModeCanceled (map . next_value :: < super :: browser_protocol :: overlay :: EventInspectModeCanceled > () ?) , super :: browser_protocol :: page :: EventDomContentEventFired :: IDENTIFIER => CdpEvent :: PageDomContentEventFired (map . next_value :: < super :: browser_protocol :: page :: EventDomContentEventFired > () ?) , super :: browser_protocol :: page :: EventFileChooserOpened :: IDENTIFIER => CdpEvent :: PageFileChooserOpened (map . next_value :: < super :: browser_protocol :: page :: EventFileChooserOpened > () ?) , super :: browser_protocol :: page :: EventFrameAttached :: IDENTIFIER => CdpEvent :: PageFrameAttached (map . next_value :: < super :: browser_protocol :: page :: EventFrameAttached > () ?) , super :: browser_protocol :: page :: EventFrameDetached :: IDENTIFIER => CdpEvent :: PageFrameDetached (map . next_value :: < super :: browser_protocol :: page :: EventFrameDetached > () ?) , super :: browser_protocol :: page :: EventFrameSubtreeWillBeDetached :: IDENTIFIER => CdpEvent :: PageFrameSubtreeWillBeDetached (map . next_value :: < super :: browser_protocol :: page :: EventFrameSubtreeWillBeDetached > () ?) , super :: browser_protocol :: page :: EventFrameNavigated :: IDENTIFIER => CdpEvent :: PageFrameNavigated (Box :: new (map . next_value :: < super :: browser_protocol :: page :: EventFrameNavigated > () ?)) , super :: browser_protocol :: page :: EventDocumentOpened :: IDENTIFIER => CdpEvent :: PageDocumentOpened (Box :: new (map . next_value :: < super :: browser_protocol :: page :: EventDocumentOpened > () ?)) , super :: browser_protocol :: page :: EventFrameResized :: IDENTIFIER => CdpEvent :: PageFrameResized (map . next_value :: < super :: browser_protocol :: page :: EventFrameResized > () ?) , super :: browser_protocol :: page :: EventFrameStartedNavigating :: IDENTIFIER => CdpEvent :: PageFrameStartedNavigating (map . next_value :: < super :: browser_protocol :: page :: EventFrameStartedNavigating > () ?) , super :: browser_protocol :: page :: EventFrameRequestedNavigation :: IDENTIFIER => CdpEvent :: PageFrameRequestedNavigation (map . next_value :: < super :: browser_protocol :: page :: EventFrameRequestedNavigation > () ?) , super :: browser_protocol :: page :: EventFrameStartedLoading :: IDENTIFIER => CdpEvent :: PageFrameStartedLoading (map . next_value :: < super :: browser_protocol :: page :: EventFrameStartedLoading > () ?) , super :: browser_protocol :: page :: EventFrameStoppedLoading :: IDENTIFIER => CdpEvent :: PageFrameStoppedLoading (map . next_value :: < super :: browser_protocol :: page :: EventFrameStoppedLoading > () ?) , super :: browser_protocol :: page :: EventInterstitialHidden :: IDENTIFIER => CdpEvent :: PageInterstitialHidden (map . next_value :: < super :: browser_protocol :: page :: EventInterstitialHidden > () ?) , super :: browser_protocol :: page :: EventInterstitialShown :: IDENTIFIER => CdpEvent :: PageInterstitialShown (map . next_value :: < super :: browser_protocol :: page :: EventInterstitialShown > () ?) , super :: browser_protocol :: page :: EventJavascriptDialogClosed :: IDENTIFIER => CdpEvent :: PageJavascriptDialogClosed (map . next_value :: < super :: browser_protocol :: page :: EventJavascriptDialogClosed > () ?) , super :: browser_protocol :: page :: EventJavascriptDialogOpening :: IDENTIFIER => CdpEvent :: PageJavascriptDialogOpening (map . next_value :: < super :: browser_protocol :: page :: EventJavascriptDialogOpening > () ?) , super :: browser_protocol :: page :: EventLifecycleEvent :: IDENTIFIER => CdpEvent :: PageLifecycleEvent (map . next_value :: < super :: browser_protocol :: page :: EventLifecycleEvent > () ?) , super :: browser_protocol :: page :: EventBackForwardCacheNotUsed :: IDENTIFIER => CdpEvent :: PageBackForwardCacheNotUsed (map . next_value :: < super :: browser_protocol :: page :: EventBackForwardCacheNotUsed > () ?) , super :: browser_protocol :: page :: EventLoadEventFired :: IDENTIFIER => CdpEvent :: PageLoadEventFired (map . next_value :: < super :: browser_protocol :: page :: EventLoadEventFired > () ?) , super :: browser_protocol :: page :: EventNavigatedWithinDocument :: IDENTIFIER => CdpEvent :: PageNavigatedWithinDocument (map . next_value :: < super :: browser_protocol :: page :: EventNavigatedWithinDocument > () ?) , super :: browser_protocol :: page :: EventScreencastFrame :: IDENTIFIER => CdpEvent :: PageScreencastFrame (map . next_value :: < super :: browser_protocol :: page :: EventScreencastFrame > () ?) , super :: browser_protocol :: page :: EventScreencastVisibilityChanged :: IDENTIFIER => CdpEvent :: PageScreencastVisibilityChanged (map . next_value :: < super :: browser_protocol :: page :: EventScreencastVisibilityChanged > () ?) , super :: browser_protocol :: page :: EventWindowOpen :: IDENTIFIER => CdpEvent :: PageWindowOpen (map . next_value :: < super :: browser_protocol :: page :: EventWindowOpen > () ?) , super :: browser_protocol :: page :: EventCompilationCacheProduced :: IDENTIFIER => CdpEvent :: PageCompilationCacheProduced (map . next_value :: < super :: browser_protocol :: page :: EventCompilationCacheProduced > () ?) , super :: browser_protocol :: performance :: EventMetrics :: IDENTIFIER => CdpEvent :: PerformanceMetrics (map . next_value :: < super :: browser_protocol :: performance :: EventMetrics > () ?) , super :: browser_protocol :: performance_timeline :: EventTimelineEventAdded :: IDENTIFIER => CdpEvent :: PerformanceTimelineTimelineEventAdded (Box :: new (map . next_value :: < super :: browser_protocol :: performance_timeline :: EventTimelineEventAdded > () ?)) , super :: browser_protocol :: preload :: EventRuleSetUpdated :: IDENTIFIER => CdpEvent :: PreloadRuleSetUpdated (Box :: new (map . next_value :: < super :: browser_protocol :: preload :: EventRuleSetUpdated > () ?)) , super :: browser_protocol :: preload :: EventRuleSetRemoved :: IDENTIFIER => CdpEvent :: PreloadRuleSetRemoved (map . next_value :: < super :: browser_protocol :: preload :: EventRuleSetRemoved > () ?) , super :: browser_protocol :: preload :: EventPreloadEnabledStateUpdated :: IDENTIFIER => CdpEvent :: PreloadPreloadEnabledStateUpdated (map . next_value :: < super :: browser_protocol :: preload :: EventPreloadEnabledStateUpdated > () ?) , super :: browser_protocol :: preload :: EventPrefetchStatusUpdated :: IDENTIFIER => CdpEvent :: PreloadPrefetchStatusUpdated (Box :: new (map . next_value :: < super :: browser_protocol :: preload :: EventPrefetchStatusUpdated > () ?)) , super :: browser_protocol :: preload :: EventPrerenderStatusUpdated :: IDENTIFIER => CdpEvent :: PreloadPrerenderStatusUpdated (map . next_value :: < super :: browser_protocol :: preload :: EventPrerenderStatusUpdated > () ?) , super :: browser_protocol :: preload :: EventPreloadingAttemptSourcesUpdated :: IDENTIFIER => CdpEvent :: PreloadPreloadingAttemptSourcesUpdated (map . next_value :: < super :: browser_protocol :: preload :: EventPreloadingAttemptSourcesUpdated > () ?) , super :: browser_protocol :: security :: EventVisibleSecurityStateChanged :: IDENTIFIER => CdpEvent :: SecurityVisibleSecurityStateChanged (Box :: new (map . next_value :: < super :: browser_protocol :: security :: EventVisibleSecurityStateChanged > () ?)) , super :: browser_protocol :: service_worker :: EventWorkerErrorReported :: IDENTIFIER => CdpEvent :: ServiceWorkerWorkerErrorReported (map . next_value :: < super :: browser_protocol :: service_worker :: EventWorkerErrorReported > () ?) , super :: browser_protocol :: service_worker :: EventWorkerRegistrationUpdated :: IDENTIFIER => CdpEvent :: ServiceWorkerWorkerRegistrationUpdated (map . next_value :: < super :: browser_protocol :: service_worker :: EventWorkerRegistrationUpdated > () ?) , super :: browser_protocol :: service_worker :: EventWorkerVersionUpdated :: IDENTIFIER => CdpEvent :: ServiceWorkerWorkerVersionUpdated (map . next_value :: < super :: browser_protocol :: service_worker :: EventWorkerVersionUpdated > () ?) , super :: browser_protocol :: storage :: EventCacheStorageContentUpdated :: IDENTIFIER => CdpEvent :: StorageCacheStorageContentUpdated (map . next_value :: < super :: browser_protocol :: storage :: EventCacheStorageContentUpdated > () ?) , super :: browser_protocol :: storage :: EventCacheStorageListUpdated :: IDENTIFIER => CdpEvent :: StorageCacheStorageListUpdated (map . next_value :: < super :: browser_protocol :: storage :: EventCacheStorageListUpdated > () ?) , super :: browser_protocol :: storage :: EventIndexedDbContentUpdated :: IDENTIFIER => CdpEvent :: StorageIndexedDbContentUpdated (map . next_value :: < super :: browser_protocol :: storage :: EventIndexedDbContentUpdated > () ?) , super :: browser_protocol :: storage :: EventIndexedDbListUpdated :: IDENTIFIER => CdpEvent :: StorageIndexedDbListUpdated (map . next_value :: < super :: browser_protocol :: storage :: EventIndexedDbListUpdated > () ?) , super :: browser_protocol :: storage :: EventInterestGroupAccessed :: IDENTIFIER => CdpEvent :: StorageInterestGroupAccessed (map . next_value :: < super :: browser_protocol :: storage :: EventInterestGroupAccessed > () ?) , super :: browser_protocol :: storage :: EventInterestGroupAuctionEventOccurred :: IDENTIFIER => CdpEvent :: StorageInterestGroupAuctionEventOccurred (map . next_value :: < super :: browser_protocol :: storage :: EventInterestGroupAuctionEventOccurred > () ?) , super :: browser_protocol :: storage :: EventInterestGroupAuctionNetworkRequestCreated :: IDENTIFIER => CdpEvent :: StorageInterestGroupAuctionNetworkRequestCreated (map . next_value :: < super :: browser_protocol :: storage :: EventInterestGroupAuctionNetworkRequestCreated > () ?) , super :: browser_protocol :: storage :: EventSharedStorageAccessed :: IDENTIFIER => CdpEvent :: StorageSharedStorageAccessed (Box :: new (map . next_value :: < super :: browser_protocol :: storage :: EventSharedStorageAccessed > () ?)) , super :: browser_protocol :: storage :: EventSharedStorageWorkletOperationExecutionFinished :: IDENTIFIER => CdpEvent :: StorageSharedStorageWorkletOperationExecutionFinished (map . next_value :: < super :: browser_protocol :: storage :: EventSharedStorageWorkletOperationExecutionFinished > () ?) , super :: browser_protocol :: storage :: EventStorageBucketCreatedOrUpdated :: IDENTIFIER => CdpEvent :: StorageStorageBucketCreatedOrUpdated (map . next_value :: < super :: browser_protocol :: storage :: EventStorageBucketCreatedOrUpdated > () ?) , super :: browser_protocol :: storage :: EventStorageBucketDeleted :: IDENTIFIER => CdpEvent :: StorageStorageBucketDeleted (map . next_value :: < super :: browser_protocol :: storage :: EventStorageBucketDeleted > () ?) , super :: browser_protocol :: storage :: EventAttributionReportingSourceRegistered :: IDENTIFIER => CdpEvent :: StorageAttributionReportingSourceRegistered (Box :: new (map . next_value :: < super :: browser_protocol :: storage :: EventAttributionReportingSourceRegistered > () ?)) , super :: browser_protocol :: storage :: EventAttributionReportingTriggerRegistered :: IDENTIFIER => CdpEvent :: StorageAttributionReportingTriggerRegistered (Box :: new (map . next_value :: < super :: browser_protocol :: storage :: EventAttributionReportingTriggerRegistered > () ?)) , super :: browser_protocol :: storage :: EventAttributionReportingReportSent :: IDENTIFIER => CdpEvent :: StorageAttributionReportingReportSent (map . next_value :: < super :: browser_protocol :: storage :: EventAttributionReportingReportSent > () ?) , super :: browser_protocol :: storage :: EventAttributionReportingVerboseDebugReportSent :: IDENTIFIER => CdpEvent :: StorageAttributionReportingVerboseDebugReportSent (map . next_value :: < super :: browser_protocol :: storage :: EventAttributionReportingVerboseDebugReportSent > () ?) , super :: browser_protocol :: target :: EventAttachedToTarget :: IDENTIFIER => CdpEvent :: TargetAttachedToTarget (Box :: new (map . next_value :: < super :: browser_protocol :: target :: EventAttachedToTarget > () ?)) , super :: browser_protocol :: target :: EventDetachedFromTarget :: IDENTIFIER => CdpEvent :: TargetDetachedFromTarget (map . next_value :: < super :: browser_protocol :: target :: EventDetachedFromTarget > () ?) , super :: browser_protocol :: target :: EventReceivedMessageFromTarget :: IDENTIFIER => CdpEvent :: TargetReceivedMessageFromTarget (map . next_value :: < super :: browser_protocol :: target :: EventReceivedMessageFromTarget > () ?) , super :: browser_protocol :: target :: EventTargetCreated :: IDENTIFIER => CdpEvent :: TargetTargetCreated (Box :: new (map . next_value :: < super :: browser_protocol :: target :: EventTargetCreated > () ?)) , super :: browser_protocol :: target :: EventTargetDestroyed :: IDENTIFIER => CdpEvent :: TargetTargetDestroyed (map . next_value :: < super :: browser_protocol :: target :: EventTargetDestroyed > () ?) , super :: browser_protocol :: target :: EventTargetCrashed :: IDENTIFIER => CdpEvent :: TargetTargetCrashed (map . next_value :: < super :: browser_protocol :: target :: EventTargetCrashed > () ?) , super :: browser_protocol :: target :: EventTargetInfoChanged :: IDENTIFIER => CdpEvent :: TargetTargetInfoChanged (Box :: new (map . next_value :: < super :: browser_protocol :: target :: EventTargetInfoChanged > () ?)) , super :: browser_protocol :: tethering :: EventAccepted :: IDENTIFIER => CdpEvent :: TetheringAccepted (map . next_value :: < super :: browser_protocol :: tethering :: EventAccepted > () ?) , super :: browser_protocol :: tracing :: EventBufferUsage :: IDENTIFIER => CdpEvent :: TracingBufferUsage (map . next_value :: < super :: browser_protocol :: tracing :: EventBufferUsage > () ?) , super :: browser_protocol :: tracing :: EventDataCollected :: IDENTIFIER => CdpEvent :: TracingDataCollected (map . next_value :: < super :: browser_protocol :: tracing :: EventDataCollected > () ?) , super :: browser_protocol :: tracing :: EventTracingComplete :: IDENTIFIER => CdpEvent :: TracingTracingComplete (map . next_value :: < super :: browser_protocol :: tracing :: EventTracingComplete > () ?) , super :: browser_protocol :: web_audio :: EventContextCreated :: IDENTIFIER => CdpEvent :: WebAudioContextCreated (map . next_value :: < super :: browser_protocol :: web_audio :: EventContextCreated > () ?) , super :: browser_protocol :: web_audio :: EventContextWillBeDestroyed :: IDENTIFIER => CdpEvent :: WebAudioContextWillBeDestroyed (map . next_value :: < super :: browser_protocol :: web_audio :: EventContextWillBeDestroyed > () ?) , super :: browser_protocol :: web_audio :: EventContextChanged :: IDENTIFIER => CdpEvent :: WebAudioContextChanged (map . next_value :: < super :: browser_protocol :: web_audio :: EventContextChanged > () ?) , super :: browser_protocol :: web_audio :: EventAudioListenerCreated :: IDENTIFIER => CdpEvent :: WebAudioAudioListenerCreated (map . next_value :: < super :: browser_protocol :: web_audio :: EventAudioListenerCreated > () ?) , super :: browser_protocol :: web_audio :: EventAudioListenerWillBeDestroyed :: IDENTIFIER => CdpEvent :: WebAudioAudioListenerWillBeDestroyed (map . next_value :: < super :: browser_protocol :: web_audio :: EventAudioListenerWillBeDestroyed > () ?) , super :: browser_protocol :: web_audio :: EventAudioNodeCreated :: IDENTIFIER => CdpEvent :: WebAudioAudioNodeCreated (map . next_value :: < super :: browser_protocol :: web_audio :: EventAudioNodeCreated > () ?) , super :: browser_protocol :: web_audio :: EventAudioNodeWillBeDestroyed :: IDENTIFIER => CdpEvent :: WebAudioAudioNodeWillBeDestroyed (map . next_value :: < super :: browser_protocol :: web_audio :: EventAudioNodeWillBeDestroyed > () ?) , super :: browser_protocol :: web_audio :: EventAudioParamCreated :: IDENTIFIER => CdpEvent :: WebAudioAudioParamCreated (map . next_value :: < super :: browser_protocol :: web_audio :: EventAudioParamCreated > () ?) , super :: browser_protocol :: web_audio :: EventAudioParamWillBeDestroyed :: IDENTIFIER => CdpEvent :: WebAudioAudioParamWillBeDestroyed (map . next_value :: < super :: browser_protocol :: web_audio :: EventAudioParamWillBeDestroyed > () ?) , super :: browser_protocol :: web_audio :: EventNodesConnected :: IDENTIFIER => CdpEvent :: WebAudioNodesConnected (map . next_value :: < super :: browser_protocol :: web_audio :: EventNodesConnected > () ?) , super :: browser_protocol :: web_audio :: EventNodesDisconnected :: IDENTIFIER => CdpEvent :: WebAudioNodesDisconnected (map . next_value :: < super :: browser_protocol :: web_audio :: EventNodesDisconnected > () ?) , super :: browser_protocol :: web_audio :: EventNodeParamConnected :: IDENTIFIER => CdpEvent :: WebAudioNodeParamConnected (map . next_value :: < super :: browser_protocol :: web_audio :: EventNodeParamConnected > () ?) , super :: browser_protocol :: web_audio :: EventNodeParamDisconnected :: IDENTIFIER => CdpEvent :: WebAudioNodeParamDisconnected (map . next_value :: < super :: browser_protocol :: web_audio :: EventNodeParamDisconnected > () ?) , super :: browser_protocol :: web_authn :: EventCredentialAdded :: IDENTIFIER => CdpEvent :: WebAuthnCredentialAdded (Box :: new (map . next_value :: < super :: browser_protocol :: web_authn :: EventCredentialAdded > () ?)) , super :: browser_protocol :: web_authn :: EventCredentialDeleted :: IDENTIFIER => CdpEvent :: WebAuthnCredentialDeleted (map . next_value :: < super :: browser_protocol :: web_authn :: EventCredentialDeleted > () ?) , super :: browser_protocol :: web_authn :: EventCredentialUpdated :: IDENTIFIER => CdpEvent :: WebAuthnCredentialUpdated (Box :: new (map . next_value :: < super :: browser_protocol :: web_authn :: EventCredentialUpdated > () ?)) , super :: browser_protocol :: web_authn :: EventCredentialAsserted :: IDENTIFIER => CdpEvent :: WebAuthnCredentialAsserted (Box :: new (map . next_value :: < super :: browser_protocol :: web_authn :: EventCredentialAsserted > () ?)) , _ => CdpEvent :: Other (map . next_value :: < serde_json :: Value > () ?) }) ; + } + } + } + let method = method.ok_or_else(|| de::Error::missing_field("method"))?; + let params = params.ok_or_else(|| de::Error::missing_field("params"))?; + Ok(CdpEventMessage { + method: ::std::borrow::Cow::Owned(method), + session_id, + params, + }) + } + } + const FIELDS: &[&str] = &["method", "sessionId", "params"]; + deserializer.deserialize_struct("CdpEventMessage", FIELDS, MessageVisitor) + } + } + impl std::convert::TryInto for CdpEventMessage { + type Error = serde_json::Error; + fn try_into(self) -> Result { + use chromiumoxide_types::Method; + Ok(chromiumoxide_types::CdpJsonEventMessage { + method: self.identifier(), + session_id: self.session_id, + params: self.params.into_json()?, + }) + } + } + impl std::convert::TryFrom for super::js_protocol::debugger::EventPaused { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DebuggerPaused(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::debugger::EventPaused) -> CdpEvent { + CdpEvent::DebuggerPaused(el) + } + } + impl std::convert::TryFrom for super::js_protocol::debugger::EventResumed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DebuggerResumed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::debugger::EventResumed) -> CdpEvent { + CdpEvent::DebuggerResumed(el) + } + } + impl std::convert::TryFrom for super::js_protocol::debugger::EventScriptFailedToParse { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DebuggerScriptFailedToParse(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::debugger::EventScriptFailedToParse) -> CdpEvent { + CdpEvent::DebuggerScriptFailedToParse(Box::new(el)) + } + } + impl std::convert::TryFrom for super::js_protocol::debugger::EventScriptParsed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DebuggerScriptParsed(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::debugger::EventScriptParsed) -> CdpEvent { + CdpEvent::DebuggerScriptParsed(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::js_protocol::heap_profiler::EventAddHeapSnapshotChunk + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::HeapProfilerAddHeapSnapshotChunk(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::heap_profiler::EventAddHeapSnapshotChunk) -> CdpEvent { + CdpEvent::HeapProfilerAddHeapSnapshotChunk(el) + } + } + impl std::convert::TryFrom for super::js_protocol::heap_profiler::EventHeapStatsUpdate { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::HeapProfilerHeapStatsUpdate(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::heap_profiler::EventHeapStatsUpdate) -> CdpEvent { + CdpEvent::HeapProfilerHeapStatsUpdate(el) + } + } + impl std::convert::TryFrom for super::js_protocol::heap_profiler::EventLastSeenObjectId { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::HeapProfilerLastSeenObjectId(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::heap_profiler::EventLastSeenObjectId) -> CdpEvent { + CdpEvent::HeapProfilerLastSeenObjectId(el) + } + } + impl std::convert::TryFrom + for super::js_protocol::heap_profiler::EventReportHeapSnapshotProgress + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::HeapProfilerReportHeapSnapshotProgress(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::js_protocol::heap_profiler::EventReportHeapSnapshotProgress, + ) -> CdpEvent { + CdpEvent::HeapProfilerReportHeapSnapshotProgress(el) + } + } + impl std::convert::TryFrom for super::js_protocol::heap_profiler::EventResetProfiles { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::HeapProfilerResetProfiles(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::heap_profiler::EventResetProfiles) -> CdpEvent { + CdpEvent::HeapProfilerResetProfiles(el) + } + } + impl std::convert::TryFrom for super::js_protocol::profiler::EventConsoleProfileFinished { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::ProfilerConsoleProfileFinished(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::profiler::EventConsoleProfileFinished) -> CdpEvent { + CdpEvent::ProfilerConsoleProfileFinished(el) + } + } + impl std::convert::TryFrom for super::js_protocol::profiler::EventConsoleProfileStarted { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::ProfilerConsoleProfileStarted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::profiler::EventConsoleProfileStarted) -> CdpEvent { + CdpEvent::ProfilerConsoleProfileStarted(el) + } + } + impl std::convert::TryFrom + for super::js_protocol::profiler::EventPreciseCoverageDeltaUpdate + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::ProfilerPreciseCoverageDeltaUpdate(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::profiler::EventPreciseCoverageDeltaUpdate) -> CdpEvent { + CdpEvent::ProfilerPreciseCoverageDeltaUpdate(el) + } + } + impl std::convert::TryFrom for super::js_protocol::runtime::EventBindingCalled { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeBindingCalled(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventBindingCalled) -> CdpEvent { + CdpEvent::RuntimeBindingCalled(el) + } + } + impl std::convert::TryFrom for super::js_protocol::runtime::EventConsoleApiCalled { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeConsoleApiCalled(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventConsoleApiCalled) -> CdpEvent { + CdpEvent::RuntimeConsoleApiCalled(el) + } + } + impl std::convert::TryFrom for super::js_protocol::runtime::EventExceptionRevoked { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeExceptionRevoked(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventExceptionRevoked) -> CdpEvent { + CdpEvent::RuntimeExceptionRevoked(el) + } + } + impl std::convert::TryFrom for super::js_protocol::runtime::EventExceptionThrown { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeExceptionThrown(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventExceptionThrown) -> CdpEvent { + CdpEvent::RuntimeExceptionThrown(Box::new(el)) + } + } + impl std::convert::TryFrom for super::js_protocol::runtime::EventExecutionContextCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeExecutionContextCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventExecutionContextCreated) -> CdpEvent { + CdpEvent::RuntimeExecutionContextCreated(el) + } + } + impl std::convert::TryFrom + for super::js_protocol::runtime::EventExecutionContextDestroyed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeExecutionContextDestroyed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventExecutionContextDestroyed) -> CdpEvent { + CdpEvent::RuntimeExecutionContextDestroyed(el) + } + } + impl std::convert::TryFrom + for super::js_protocol::runtime::EventExecutionContextsCleared + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeExecutionContextsCleared(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventExecutionContextsCleared) -> CdpEvent { + CdpEvent::RuntimeExecutionContextsCleared(el) + } + } + impl std::convert::TryFrom for super::js_protocol::runtime::EventInspectRequested { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::RuntimeInspectRequested(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::js_protocol::runtime::EventInspectRequested) -> CdpEvent { + CdpEvent::RuntimeInspectRequested(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::accessibility::EventLoadComplete { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AccessibilityLoadComplete(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::accessibility::EventLoadComplete) -> CdpEvent { + CdpEvent::AccessibilityLoadComplete(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::accessibility::EventNodesUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AccessibilityNodesUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::accessibility::EventNodesUpdated) -> CdpEvent { + CdpEvent::AccessibilityNodesUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::animation::EventAnimationCanceled + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AnimationAnimationCanceled(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::animation::EventAnimationCanceled) -> CdpEvent { + CdpEvent::AnimationAnimationCanceled(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::animation::EventAnimationCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AnimationAnimationCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::animation::EventAnimationCreated) -> CdpEvent { + CdpEvent::AnimationAnimationCreated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::animation::EventAnimationStarted { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AnimationAnimationStarted(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::animation::EventAnimationStarted) -> CdpEvent { + CdpEvent::AnimationAnimationStarted(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::animation::EventAnimationUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AnimationAnimationUpdated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::animation::EventAnimationUpdated) -> CdpEvent { + CdpEvent::AnimationAnimationUpdated(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::audits::EventIssueAdded { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AuditsIssueAdded(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::audits::EventIssueAdded) -> CdpEvent { + CdpEvent::AuditsIssueAdded(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::autofill::EventAddressFormFilled { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::AutofillAddressFormFilled(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::autofill::EventAddressFormFilled) -> CdpEvent { + CdpEvent::AutofillAddressFormFilled(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::background_service::EventRecordingStateChanged + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BackgroundServiceRecordingStateChanged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::background_service::EventRecordingStateChanged, + ) -> CdpEvent { + CdpEvent::BackgroundServiceRecordingStateChanged(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::background_service::EventBackgroundServiceEventReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BackgroundServiceBackgroundServiceEventReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::background_service::EventBackgroundServiceEventReceived, + ) -> CdpEvent { + CdpEvent::BackgroundServiceBackgroundServiceEventReceived(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::bluetooth_emulation::EventGattOperationReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BluetoothEmulationGattOperationReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::bluetooth_emulation::EventGattOperationReceived, + ) -> CdpEvent { + CdpEvent::BluetoothEmulationGattOperationReceived(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::bluetooth_emulation::EventCharacteristicOperationReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BluetoothEmulationCharacteristicOperationReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::bluetooth_emulation::EventCharacteristicOperationReceived, + ) -> CdpEvent { + CdpEvent::BluetoothEmulationCharacteristicOperationReceived(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::bluetooth_emulation::EventDescriptorOperationReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BluetoothEmulationDescriptorOperationReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::bluetooth_emulation::EventDescriptorOperationReceived, + ) -> CdpEvent { + CdpEvent::BluetoothEmulationDescriptorOperationReceived(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::browser::EventDownloadWillBegin { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BrowserDownloadWillBegin(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::browser::EventDownloadWillBegin) -> CdpEvent { + CdpEvent::BrowserDownloadWillBegin(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::browser::EventDownloadProgress { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::BrowserDownloadProgress(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::browser::EventDownloadProgress) -> CdpEvent { + CdpEvent::BrowserDownloadProgress(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::css::EventFontsUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CssFontsUpdated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::css::EventFontsUpdated) -> CdpEvent { + CdpEvent::CssFontsUpdated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::css::EventMediaQueryResultChanged + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CssMediaQueryResultChanged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::css::EventMediaQueryResultChanged) -> CdpEvent { + CdpEvent::CssMediaQueryResultChanged(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::css::EventStyleSheetAdded { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CssStyleSheetAdded(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::css::EventStyleSheetAdded) -> CdpEvent { + CdpEvent::CssStyleSheetAdded(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::css::EventStyleSheetChanged { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CssStyleSheetChanged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::css::EventStyleSheetChanged) -> CdpEvent { + CdpEvent::CssStyleSheetChanged(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::css::EventStyleSheetRemoved { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CssStyleSheetRemoved(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::css::EventStyleSheetRemoved) -> CdpEvent { + CdpEvent::CssStyleSheetRemoved(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::css::EventComputedStyleUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CssComputedStyleUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::css::EventComputedStyleUpdated) -> CdpEvent { + CdpEvent::CssComputedStyleUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::cast::EventSinksUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CastSinksUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::cast::EventSinksUpdated) -> CdpEvent { + CdpEvent::CastSinksUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::cast::EventIssueUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::CastIssueUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::cast::EventIssueUpdated) -> CdpEvent { + CdpEvent::CastIssueUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventAttributeModified { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomAttributeModified(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventAttributeModified) -> CdpEvent { + CdpEvent::DomAttributeModified(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom::EventAdoptedStyleSheetsModified + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomAdoptedStyleSheetsModified(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventAdoptedStyleSheetsModified) -> CdpEvent { + CdpEvent::DomAdoptedStyleSheetsModified(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventAttributeRemoved { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomAttributeRemoved(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventAttributeRemoved) -> CdpEvent { + CdpEvent::DomAttributeRemoved(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventCharacterDataModified { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomCharacterDataModified(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventCharacterDataModified) -> CdpEvent { + CdpEvent::DomCharacterDataModified(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventChildNodeCountUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomChildNodeCountUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventChildNodeCountUpdated) -> CdpEvent { + CdpEvent::DomChildNodeCountUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventChildNodeInserted { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomChildNodeInserted(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventChildNodeInserted) -> CdpEvent { + CdpEvent::DomChildNodeInserted(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventChildNodeRemoved { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomChildNodeRemoved(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventChildNodeRemoved) -> CdpEvent { + CdpEvent::DomChildNodeRemoved(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom::EventDistributedNodesUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomDistributedNodesUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventDistributedNodesUpdated) -> CdpEvent { + CdpEvent::DomDistributedNodesUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventDocumentUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomDocumentUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventDocumentUpdated) -> CdpEvent { + CdpEvent::DomDocumentUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventInlineStyleInvalidated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomInlineStyleInvalidated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventInlineStyleInvalidated) -> CdpEvent { + CdpEvent::DomInlineStyleInvalidated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventPseudoElementAdded { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomPseudoElementAdded(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventPseudoElementAdded) -> CdpEvent { + CdpEvent::DomPseudoElementAdded(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom::EventTopLayerElementsUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomTopLayerElementsUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventTopLayerElementsUpdated) -> CdpEvent { + CdpEvent::DomTopLayerElementsUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventScrollableFlagUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomScrollableFlagUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventScrollableFlagUpdated) -> CdpEvent { + CdpEvent::DomScrollableFlagUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom::EventAffectedByStartingStylesFlagUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomAffectedByStartingStylesFlagUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::dom::EventAffectedByStartingStylesFlagUpdated, + ) -> CdpEvent { + CdpEvent::DomAffectedByStartingStylesFlagUpdated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventPseudoElementRemoved { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomPseudoElementRemoved(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventPseudoElementRemoved) -> CdpEvent { + CdpEvent::DomPseudoElementRemoved(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventSetChildNodes { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomSetChildNodes(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventSetChildNodes) -> CdpEvent { + CdpEvent::DomSetChildNodes(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventShadowRootPopped { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomShadowRootPopped(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventShadowRootPopped) -> CdpEvent { + CdpEvent::DomShadowRootPopped(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::dom::EventShadowRootPushed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomShadowRootPushed(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom::EventShadowRootPushed) -> CdpEvent { + CdpEvent::DomShadowRootPushed(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom_storage::EventDomStorageItemAdded + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomStorageDomStorageItemAdded(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom_storage::EventDomStorageItemAdded) -> CdpEvent { + CdpEvent::DomStorageDomStorageItemAdded(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom_storage::EventDomStorageItemRemoved + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomStorageDomStorageItemRemoved(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom_storage::EventDomStorageItemRemoved) -> CdpEvent { + CdpEvent::DomStorageDomStorageItemRemoved(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom_storage::EventDomStorageItemUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomStorageDomStorageItemUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom_storage::EventDomStorageItemUpdated) -> CdpEvent { + CdpEvent::DomStorageDomStorageItemUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::dom_storage::EventDomStorageItemsCleared + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DomStorageDomStorageItemsCleared(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::dom_storage::EventDomStorageItemsCleared) -> CdpEvent { + CdpEvent::DomStorageDomStorageItemsCleared(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::device_access::EventDeviceRequestPrompted + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::DeviceAccessDeviceRequestPrompted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::device_access::EventDeviceRequestPrompted, + ) -> CdpEvent { + CdpEvent::DeviceAccessDeviceRequestPrompted(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::emulation::EventVirtualTimeBudgetExpired + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::EmulationVirtualTimeBudgetExpired(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::emulation::EventVirtualTimeBudgetExpired) -> CdpEvent { + CdpEvent::EmulationVirtualTimeBudgetExpired(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::fed_cm::EventDialogShown { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::FedCmDialogShown(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::fed_cm::EventDialogShown) -> CdpEvent { + CdpEvent::FedCmDialogShown(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::fed_cm::EventDialogClosed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::FedCmDialogClosed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::fed_cm::EventDialogClosed) -> CdpEvent { + CdpEvent::FedCmDialogClosed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::fetch::EventRequestPaused { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::FetchRequestPaused(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::fetch::EventRequestPaused) -> CdpEvent { + CdpEvent::FetchRequestPaused(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::fetch::EventAuthRequired { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::FetchAuthRequired(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::fetch::EventAuthRequired) -> CdpEvent { + CdpEvent::FetchAuthRequired(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::input::EventDragIntercepted { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::InputDragIntercepted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::input::EventDragIntercepted) -> CdpEvent { + CdpEvent::InputDragIntercepted(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::inspector::EventDetached { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::InspectorDetached(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::inspector::EventDetached) -> CdpEvent { + CdpEvent::InspectorDetached(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::inspector::EventTargetCrashed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::InspectorTargetCrashed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::inspector::EventTargetCrashed) -> CdpEvent { + CdpEvent::InspectorTargetCrashed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::inspector::EventTargetReloadedAfterCrash + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::InspectorTargetReloadedAfterCrash(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::inspector::EventTargetReloadedAfterCrash) -> CdpEvent { + CdpEvent::InspectorTargetReloadedAfterCrash(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::inspector::EventWorkerScriptLoaded + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::InspectorWorkerScriptLoaded(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::inspector::EventWorkerScriptLoaded) -> CdpEvent { + CdpEvent::InspectorWorkerScriptLoaded(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::layer_tree::EventLayerPainted { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::LayerTreeLayerPainted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::layer_tree::EventLayerPainted) -> CdpEvent { + CdpEvent::LayerTreeLayerPainted(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::layer_tree::EventLayerTreeDidChange + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::LayerTreeLayerTreeDidChange(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::layer_tree::EventLayerTreeDidChange) -> CdpEvent { + CdpEvent::LayerTreeLayerTreeDidChange(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::log::EventEntryAdded { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::LogEntryAdded(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::log::EventEntryAdded) -> CdpEvent { + CdpEvent::LogEntryAdded(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::media::EventPlayerPropertiesChanged + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::MediaPlayerPropertiesChanged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::media::EventPlayerPropertiesChanged) -> CdpEvent { + CdpEvent::MediaPlayerPropertiesChanged(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::media::EventPlayerEventsAdded { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::MediaPlayerEventsAdded(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::media::EventPlayerEventsAdded) -> CdpEvent { + CdpEvent::MediaPlayerEventsAdded(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::media::EventPlayerMessagesLogged { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::MediaPlayerMessagesLogged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::media::EventPlayerMessagesLogged) -> CdpEvent { + CdpEvent::MediaPlayerMessagesLogged(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::media::EventPlayerErrorsRaised { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::MediaPlayerErrorsRaised(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::media::EventPlayerErrorsRaised) -> CdpEvent { + CdpEvent::MediaPlayerErrorsRaised(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::media::EventPlayerCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::MediaPlayerCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::media::EventPlayerCreated) -> CdpEvent { + CdpEvent::MediaPlayerCreated(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventDataReceived { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDataReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDataReceived) -> CdpEvent { + CdpEvent::NetworkDataReceived(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventEventSourceMessageReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkEventSourceMessageReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventEventSourceMessageReceived) -> CdpEvent { + CdpEvent::NetworkEventSourceMessageReceived(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventLoadingFailed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkLoadingFailed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventLoadingFailed) -> CdpEvent { + CdpEvent::NetworkLoadingFailed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventLoadingFinished { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkLoadingFinished(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventLoadingFinished) -> CdpEvent { + CdpEvent::NetworkLoadingFinished(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventRequestServedFromCache + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkRequestServedFromCache(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventRequestServedFromCache) -> CdpEvent { + CdpEvent::NetworkRequestServedFromCache(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventRequestWillBeSent { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkRequestWillBeSent(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventRequestWillBeSent) -> CdpEvent { + CdpEvent::NetworkRequestWillBeSent(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventResourceChangedPriority + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkResourceChangedPriority(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventResourceChangedPriority) -> CdpEvent { + CdpEvent::NetworkResourceChangedPriority(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventSignedExchangeReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkSignedExchangeReceived(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventSignedExchangeReceived) -> CdpEvent { + CdpEvent::NetworkSignedExchangeReceived(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventResponseReceived { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkResponseReceived(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventResponseReceived) -> CdpEvent { + CdpEvent::NetworkResponseReceived(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventWebSocketClosed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketClosed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebSocketClosed) -> CdpEvent { + CdpEvent::NetworkWebSocketClosed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventWebSocketCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketCreated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebSocketCreated) -> CdpEvent { + CdpEvent::NetworkWebSocketCreated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventWebSocketFrameError + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketFrameError(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebSocketFrameError) -> CdpEvent { + CdpEvent::NetworkWebSocketFrameError(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventWebSocketFrameReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketFrameReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebSocketFrameReceived) -> CdpEvent { + CdpEvent::NetworkWebSocketFrameReceived(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventWebSocketFrameSent { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketFrameSent(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebSocketFrameSent) -> CdpEvent { + CdpEvent::NetworkWebSocketFrameSent(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventWebSocketHandshakeResponseReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketHandshakeResponseReceived(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventWebSocketHandshakeResponseReceived, + ) -> CdpEvent { + CdpEvent::NetworkWebSocketHandshakeResponseReceived(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventWebSocketWillSendHandshakeRequest + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebSocketWillSendHandshakeRequest(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventWebSocketWillSendHandshakeRequest, + ) -> CdpEvent { + CdpEvent::NetworkWebSocketWillSendHandshakeRequest(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventWebTransportCreated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebTransportCreated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebTransportCreated) -> CdpEvent { + CdpEvent::NetworkWebTransportCreated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventWebTransportConnectionEstablished + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebTransportConnectionEstablished(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventWebTransportConnectionEstablished, + ) -> CdpEvent { + CdpEvent::NetworkWebTransportConnectionEstablished(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventWebTransportClosed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkWebTransportClosed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventWebTransportClosed) -> CdpEvent { + CdpEvent::NetworkWebTransportClosed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectTcpSocketCreated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectTcpSocketCreated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectTcpSocketCreated) -> CdpEvent { + CdpEvent::NetworkDirectTcpSocketCreated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectTcpSocketOpened + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectTcpSocketOpened(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectTcpSocketOpened) -> CdpEvent { + CdpEvent::NetworkDirectTcpSocketOpened(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectTcpSocketAborted + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectTcpSocketAborted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectTcpSocketAborted) -> CdpEvent { + CdpEvent::NetworkDirectTcpSocketAborted(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectTcpSocketClosed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectTcpSocketClosed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectTcpSocketClosed) -> CdpEvent { + CdpEvent::NetworkDirectTcpSocketClosed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectTcpSocketChunkSent + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectTcpSocketChunkSent(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectTcpSocketChunkSent) -> CdpEvent { + CdpEvent::NetworkDirectTcpSocketChunkSent(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectTcpSocketChunkReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectTcpSocketChunkReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventDirectTcpSocketChunkReceived, + ) -> CdpEvent { + CdpEvent::NetworkDirectTcpSocketChunkReceived(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketJoinedMulticastGroup + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketJoinedMulticastGroup(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventDirectUdpSocketJoinedMulticastGroup, + ) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketJoinedMulticastGroup(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketLeftMulticastGroup + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketLeftMulticastGroup(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventDirectUdpSocketLeftMulticastGroup, + ) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketLeftMulticastGroup(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketCreated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketCreated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectUdpSocketCreated) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketCreated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketOpened + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketOpened(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectUdpSocketOpened) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketOpened(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketAborted + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketAborted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectUdpSocketAborted) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketAborted(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketClosed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketClosed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectUdpSocketClosed) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketClosed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketChunkSent + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketChunkSent(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDirectUdpSocketChunkSent) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketChunkSent(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDirectUdpSocketChunkReceived + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDirectUdpSocketChunkReceived(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventDirectUdpSocketChunkReceived, + ) -> CdpEvent { + CdpEvent::NetworkDirectUdpSocketChunkReceived(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventRequestWillBeSentExtraInfo + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkRequestWillBeSentExtraInfo(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventRequestWillBeSentExtraInfo) -> CdpEvent { + CdpEvent::NetworkRequestWillBeSentExtraInfo(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventResponseReceivedExtraInfo + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkResponseReceivedExtraInfo(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventResponseReceivedExtraInfo) -> CdpEvent { + CdpEvent::NetworkResponseReceivedExtraInfo(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventResponseReceivedEarlyHints + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkResponseReceivedEarlyHints(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventResponseReceivedEarlyHints) -> CdpEvent { + CdpEvent::NetworkResponseReceivedEarlyHints(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventTrustTokenOperationDone + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkTrustTokenOperationDone(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventTrustTokenOperationDone) -> CdpEvent { + CdpEvent::NetworkTrustTokenOperationDone(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::network::EventPolicyUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkPolicyUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventPolicyUpdated) -> CdpEvent { + CdpEvent::NetworkPolicyUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventReportingApiReportAdded + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkReportingApiReportAdded(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventReportingApiReportAdded) -> CdpEvent { + CdpEvent::NetworkReportingApiReportAdded(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventReportingApiReportUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkReportingApiReportUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventReportingApiReportUpdated) -> CdpEvent { + CdpEvent::NetworkReportingApiReportUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventReportingApiEndpointsChangedForOrigin + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkReportingApiEndpointsChangedForOrigin(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::network::EventReportingApiEndpointsChangedForOrigin, + ) -> CdpEvent { + CdpEvent::NetworkReportingApiEndpointsChangedForOrigin(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDeviceBoundSessionsAdded + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDeviceBoundSessionsAdded(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::network::EventDeviceBoundSessionsAdded) -> CdpEvent { + CdpEvent::NetworkDeviceBoundSessionsAdded(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::network::EventDeviceBoundSessionEventOccurred + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::NetworkDeviceBoundSessionEventOccurred(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::network::EventDeviceBoundSessionEventOccurred, + ) -> CdpEvent { + CdpEvent::NetworkDeviceBoundSessionEventOccurred(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::overlay::EventInspectNodeRequested + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::OverlayInspectNodeRequested(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::overlay::EventInspectNodeRequested) -> CdpEvent { + CdpEvent::OverlayInspectNodeRequested(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::overlay::EventNodeHighlightRequested + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::OverlayNodeHighlightRequested(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::overlay::EventNodeHighlightRequested) -> CdpEvent { + CdpEvent::OverlayNodeHighlightRequested(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::overlay::EventScreenshotRequested + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::OverlayScreenshotRequested(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::overlay::EventScreenshotRequested) -> CdpEvent { + CdpEvent::OverlayScreenshotRequested(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::overlay::EventInspectModeCanceled + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::OverlayInspectModeCanceled(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::overlay::EventInspectModeCanceled) -> CdpEvent { + CdpEvent::OverlayInspectModeCanceled(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventDomContentEventFired { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageDomContentEventFired(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventDomContentEventFired) -> CdpEvent { + CdpEvent::PageDomContentEventFired(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFileChooserOpened { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFileChooserOpened(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFileChooserOpened) -> CdpEvent { + CdpEvent::PageFileChooserOpened(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFrameAttached { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameAttached(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameAttached) -> CdpEvent { + CdpEvent::PageFrameAttached(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFrameDetached { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameDetached(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameDetached) -> CdpEvent { + CdpEvent::PageFrameDetached(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventFrameSubtreeWillBeDetached + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameSubtreeWillBeDetached(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameSubtreeWillBeDetached) -> CdpEvent { + CdpEvent::PageFrameSubtreeWillBeDetached(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFrameNavigated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameNavigated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameNavigated) -> CdpEvent { + CdpEvent::PageFrameNavigated(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventDocumentOpened { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageDocumentOpened(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventDocumentOpened) -> CdpEvent { + CdpEvent::PageDocumentOpened(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFrameResized { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameResized(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameResized) -> CdpEvent { + CdpEvent::PageFrameResized(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventFrameStartedNavigating + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameStartedNavigating(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameStartedNavigating) -> CdpEvent { + CdpEvent::PageFrameStartedNavigating(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventFrameRequestedNavigation + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameRequestedNavigation(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameRequestedNavigation) -> CdpEvent { + CdpEvent::PageFrameRequestedNavigation(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFrameStartedLoading { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameStartedLoading(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameStartedLoading) -> CdpEvent { + CdpEvent::PageFrameStartedLoading(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventFrameStoppedLoading { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageFrameStoppedLoading(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventFrameStoppedLoading) -> CdpEvent { + CdpEvent::PageFrameStoppedLoading(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventInterstitialHidden { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageInterstitialHidden(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventInterstitialHidden) -> CdpEvent { + CdpEvent::PageInterstitialHidden(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventInterstitialShown { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageInterstitialShown(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventInterstitialShown) -> CdpEvent { + CdpEvent::PageInterstitialShown(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventJavascriptDialogClosed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageJavascriptDialogClosed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventJavascriptDialogClosed) -> CdpEvent { + CdpEvent::PageJavascriptDialogClosed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventJavascriptDialogOpening + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageJavascriptDialogOpening(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventJavascriptDialogOpening) -> CdpEvent { + CdpEvent::PageJavascriptDialogOpening(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventLifecycleEvent { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageLifecycleEvent(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventLifecycleEvent) -> CdpEvent { + CdpEvent::PageLifecycleEvent(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventBackForwardCacheNotUsed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageBackForwardCacheNotUsed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventBackForwardCacheNotUsed) -> CdpEvent { + CdpEvent::PageBackForwardCacheNotUsed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventLoadEventFired { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageLoadEventFired(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventLoadEventFired) -> CdpEvent { + CdpEvent::PageLoadEventFired(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventNavigatedWithinDocument + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageNavigatedWithinDocument(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventNavigatedWithinDocument) -> CdpEvent { + CdpEvent::PageNavigatedWithinDocument(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventScreencastFrame { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageScreencastFrame(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventScreencastFrame) -> CdpEvent { + CdpEvent::PageScreencastFrame(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventScreencastVisibilityChanged + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageScreencastVisibilityChanged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventScreencastVisibilityChanged) -> CdpEvent { + CdpEvent::PageScreencastVisibilityChanged(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::page::EventWindowOpen { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageWindowOpen(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventWindowOpen) -> CdpEvent { + CdpEvent::PageWindowOpen(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::page::EventCompilationCacheProduced + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PageCompilationCacheProduced(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::page::EventCompilationCacheProduced) -> CdpEvent { + CdpEvent::PageCompilationCacheProduced(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::performance::EventMetrics { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PerformanceMetrics(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::performance::EventMetrics) -> CdpEvent { + CdpEvent::PerformanceMetrics(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::performance_timeline::EventTimelineEventAdded + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PerformanceTimelineTimelineEventAdded(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::performance_timeline::EventTimelineEventAdded, + ) -> CdpEvent { + CdpEvent::PerformanceTimelineTimelineEventAdded(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::preload::EventRuleSetUpdated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PreloadRuleSetUpdated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::preload::EventRuleSetUpdated) -> CdpEvent { + CdpEvent::PreloadRuleSetUpdated(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::preload::EventRuleSetRemoved { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PreloadRuleSetRemoved(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::preload::EventRuleSetRemoved) -> CdpEvent { + CdpEvent::PreloadRuleSetRemoved(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::preload::EventPreloadEnabledStateUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PreloadPreloadEnabledStateUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::preload::EventPreloadEnabledStateUpdated) -> CdpEvent { + CdpEvent::PreloadPreloadEnabledStateUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::preload::EventPrefetchStatusUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PreloadPrefetchStatusUpdated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::preload::EventPrefetchStatusUpdated) -> CdpEvent { + CdpEvent::PreloadPrefetchStatusUpdated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::preload::EventPrerenderStatusUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PreloadPrerenderStatusUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::preload::EventPrerenderStatusUpdated) -> CdpEvent { + CdpEvent::PreloadPrerenderStatusUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::preload::EventPreloadingAttemptSourcesUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::PreloadPreloadingAttemptSourcesUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::preload::EventPreloadingAttemptSourcesUpdated, + ) -> CdpEvent { + CdpEvent::PreloadPreloadingAttemptSourcesUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::security::EventVisibleSecurityStateChanged + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::SecurityVisibleSecurityStateChanged(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::security::EventVisibleSecurityStateChanged, + ) -> CdpEvent { + CdpEvent::SecurityVisibleSecurityStateChanged(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::service_worker::EventWorkerErrorReported + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::ServiceWorkerWorkerErrorReported(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::service_worker::EventWorkerErrorReported) -> CdpEvent { + CdpEvent::ServiceWorkerWorkerErrorReported(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::service_worker::EventWorkerRegistrationUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::ServiceWorkerWorkerRegistrationUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::service_worker::EventWorkerRegistrationUpdated, + ) -> CdpEvent { + CdpEvent::ServiceWorkerWorkerRegistrationUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::service_worker::EventWorkerVersionUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::ServiceWorkerWorkerVersionUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::service_worker::EventWorkerVersionUpdated, + ) -> CdpEvent { + CdpEvent::ServiceWorkerWorkerVersionUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventCacheStorageContentUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageCacheStorageContentUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventCacheStorageContentUpdated) -> CdpEvent { + CdpEvent::StorageCacheStorageContentUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventCacheStorageListUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageCacheStorageListUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventCacheStorageListUpdated) -> CdpEvent { + CdpEvent::StorageCacheStorageListUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventIndexedDbContentUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageIndexedDbContentUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventIndexedDbContentUpdated) -> CdpEvent { + CdpEvent::StorageIndexedDbContentUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventIndexedDbListUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageIndexedDbListUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventIndexedDbListUpdated) -> CdpEvent { + CdpEvent::StorageIndexedDbListUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventInterestGroupAccessed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageInterestGroupAccessed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventInterestGroupAccessed) -> CdpEvent { + CdpEvent::StorageInterestGroupAccessed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventInterestGroupAuctionEventOccurred + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageInterestGroupAuctionEventOccurred(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::storage::EventInterestGroupAuctionEventOccurred, + ) -> CdpEvent { + CdpEvent::StorageInterestGroupAuctionEventOccurred(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventInterestGroupAuctionNetworkRequestCreated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageInterestGroupAuctionNetworkRequestCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::storage::EventInterestGroupAuctionNetworkRequestCreated, + ) -> CdpEvent { + CdpEvent::StorageInterestGroupAuctionNetworkRequestCreated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventSharedStorageAccessed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageSharedStorageAccessed(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventSharedStorageAccessed) -> CdpEvent { + CdpEvent::StorageSharedStorageAccessed(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventSharedStorageWorkletOperationExecutionFinished + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageSharedStorageWorkletOperationExecutionFinished(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el : super :: browser_protocol :: storage :: EventSharedStorageWorkletOperationExecutionFinished, + ) -> CdpEvent { + CdpEvent::StorageSharedStorageWorkletOperationExecutionFinished(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventStorageBucketCreatedOrUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageStorageBucketCreatedOrUpdated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::storage::EventStorageBucketCreatedOrUpdated, + ) -> CdpEvent { + CdpEvent::StorageStorageBucketCreatedOrUpdated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventStorageBucketDeleted + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageStorageBucketDeleted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::storage::EventStorageBucketDeleted) -> CdpEvent { + CdpEvent::StorageStorageBucketDeleted(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventAttributionReportingSourceRegistered + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageAttributionReportingSourceRegistered(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::storage::EventAttributionReportingSourceRegistered, + ) -> CdpEvent { + CdpEvent::StorageAttributionReportingSourceRegistered(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventAttributionReportingTriggerRegistered + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageAttributionReportingTriggerRegistered(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::storage::EventAttributionReportingTriggerRegistered, + ) -> CdpEvent { + CdpEvent::StorageAttributionReportingTriggerRegistered(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventAttributionReportingReportSent + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageAttributionReportingReportSent(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::storage::EventAttributionReportingReportSent, + ) -> CdpEvent { + CdpEvent::StorageAttributionReportingReportSent(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::storage::EventAttributionReportingVerboseDebugReportSent + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::StorageAttributionReportingVerboseDebugReportSent(val) => Ok(val), + _ => Err(event), + } + } + } + impl From + for CdpEvent + { + fn from( + el: super::browser_protocol::storage::EventAttributionReportingVerboseDebugReportSent, + ) -> CdpEvent { + CdpEvent::StorageAttributionReportingVerboseDebugReportSent(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::target::EventAttachedToTarget { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetAttachedToTarget(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventAttachedToTarget) -> CdpEvent { + CdpEvent::TargetAttachedToTarget(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::target::EventDetachedFromTarget { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetDetachedFromTarget(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventDetachedFromTarget) -> CdpEvent { + CdpEvent::TargetDetachedFromTarget(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::target::EventReceivedMessageFromTarget + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetReceivedMessageFromTarget(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventReceivedMessageFromTarget) -> CdpEvent { + CdpEvent::TargetReceivedMessageFromTarget(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::target::EventTargetCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetTargetCreated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventTargetCreated) -> CdpEvent { + CdpEvent::TargetTargetCreated(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::target::EventTargetDestroyed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetTargetDestroyed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventTargetDestroyed) -> CdpEvent { + CdpEvent::TargetTargetDestroyed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::target::EventTargetCrashed { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetTargetCrashed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventTargetCrashed) -> CdpEvent { + CdpEvent::TargetTargetCrashed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::target::EventTargetInfoChanged { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TargetTargetInfoChanged(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::target::EventTargetInfoChanged) -> CdpEvent { + CdpEvent::TargetTargetInfoChanged(Box::new(el)) + } + } + impl std::convert::TryFrom for super::browser_protocol::tethering::EventAccepted { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TetheringAccepted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::tethering::EventAccepted) -> CdpEvent { + CdpEvent::TetheringAccepted(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::tracing::EventBufferUsage { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TracingBufferUsage(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::tracing::EventBufferUsage) -> CdpEvent { + CdpEvent::TracingBufferUsage(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::tracing::EventDataCollected { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TracingDataCollected(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::tracing::EventDataCollected) -> CdpEvent { + CdpEvent::TracingDataCollected(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::tracing::EventTracingComplete { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::TracingTracingComplete(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::tracing::EventTracingComplete) -> CdpEvent { + CdpEvent::TracingTracingComplete(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::web_audio::EventContextCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioContextCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventContextCreated) -> CdpEvent { + CdpEvent::WebAudioContextCreated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventContextWillBeDestroyed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioContextWillBeDestroyed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventContextWillBeDestroyed) -> CdpEvent { + CdpEvent::WebAudioContextWillBeDestroyed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::web_audio::EventContextChanged { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioContextChanged(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventContextChanged) -> CdpEvent { + CdpEvent::WebAudioContextChanged(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventAudioListenerCreated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioAudioListenerCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventAudioListenerCreated) -> CdpEvent { + CdpEvent::WebAudioAudioListenerCreated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventAudioListenerWillBeDestroyed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioAudioListenerWillBeDestroyed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::web_audio::EventAudioListenerWillBeDestroyed, + ) -> CdpEvent { + CdpEvent::WebAudioAudioListenerWillBeDestroyed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::web_audio::EventAudioNodeCreated { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioAudioNodeCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventAudioNodeCreated) -> CdpEvent { + CdpEvent::WebAudioAudioNodeCreated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventAudioNodeWillBeDestroyed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioAudioNodeWillBeDestroyed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventAudioNodeWillBeDestroyed) -> CdpEvent { + CdpEvent::WebAudioAudioNodeWillBeDestroyed(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventAudioParamCreated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioAudioParamCreated(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventAudioParamCreated) -> CdpEvent { + CdpEvent::WebAudioAudioParamCreated(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventAudioParamWillBeDestroyed + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioAudioParamWillBeDestroyed(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from( + el: super::browser_protocol::web_audio::EventAudioParamWillBeDestroyed, + ) -> CdpEvent { + CdpEvent::WebAudioAudioParamWillBeDestroyed(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::web_audio::EventNodesConnected { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioNodesConnected(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventNodesConnected) -> CdpEvent { + CdpEvent::WebAudioNodesConnected(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventNodesDisconnected + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioNodesDisconnected(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventNodesDisconnected) -> CdpEvent { + CdpEvent::WebAudioNodesDisconnected(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventNodeParamConnected + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioNodeParamConnected(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventNodeParamConnected) -> CdpEvent { + CdpEvent::WebAudioNodeParamConnected(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_audio::EventNodeParamDisconnected + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAudioNodeParamDisconnected(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_audio::EventNodeParamDisconnected) -> CdpEvent { + CdpEvent::WebAudioNodeParamDisconnected(el) + } + } + impl std::convert::TryFrom for super::browser_protocol::web_authn::EventCredentialAdded { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAuthnCredentialAdded(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_authn::EventCredentialAdded) -> CdpEvent { + CdpEvent::WebAuthnCredentialAdded(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_authn::EventCredentialDeleted + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAuthnCredentialDeleted(val) => Ok(val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_authn::EventCredentialDeleted) -> CdpEvent { + CdpEvent::WebAuthnCredentialDeleted(el) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_authn::EventCredentialUpdated + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAuthnCredentialUpdated(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_authn::EventCredentialUpdated) -> CdpEvent { + CdpEvent::WebAuthnCredentialUpdated(Box::new(el)) + } + } + impl std::convert::TryFrom + for super::browser_protocol::web_authn::EventCredentialAsserted + { + type Error = CdpEvent; + fn try_from(event: CdpEvent) -> Result { + match event { + CdpEvent::WebAuthnCredentialAsserted(val) => Ok(*val), + _ => Err(event), + } + } + } + impl From for CdpEvent { + fn from(el: super::browser_protocol::web_authn::EventCredentialAsserted) -> CdpEvent { + CdpEvent::WebAuthnCredentialAsserted(Box::new(el)) + } + } + impl super::sealed::SealedEvent for super::js_protocol::debugger::EventPaused { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::debugger::EventPaused { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::debugger::EventResumed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::debugger::EventResumed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::debugger::EventScriptFailedToParse { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::debugger::EventScriptFailedToParse { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::debugger::EventScriptParsed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::debugger::EventScriptParsed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::heap_profiler::EventAddHeapSnapshotChunk { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::heap_profiler::EventAddHeapSnapshotChunk { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::heap_profiler::EventHeapStatsUpdate { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::heap_profiler::EventHeapStatsUpdate { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::heap_profiler::EventLastSeenObjectId { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::heap_profiler::EventLastSeenObjectId { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::js_protocol::heap_profiler::EventReportHeapSnapshotProgress + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::heap_profiler::EventReportHeapSnapshotProgress { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::heap_profiler::EventResetProfiles { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::heap_profiler::EventResetProfiles { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::profiler::EventConsoleProfileFinished { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::profiler::EventConsoleProfileFinished { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::profiler::EventConsoleProfileStarted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::profiler::EventConsoleProfileStarted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::profiler::EventPreciseCoverageDeltaUpdate { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::profiler::EventPreciseCoverageDeltaUpdate { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventBindingCalled { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventBindingCalled { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventConsoleApiCalled { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventConsoleApiCalled { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventExceptionRevoked { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventExceptionRevoked { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventExceptionThrown { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventExceptionThrown { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventExecutionContextCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventExecutionContextCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventExecutionContextDestroyed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventExecutionContextDestroyed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventExecutionContextsCleared { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventExecutionContextsCleared { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::js_protocol::runtime::EventInspectRequested { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::js_protocol::runtime::EventInspectRequested { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::accessibility::EventLoadComplete { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::accessibility::EventLoadComplete { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::accessibility::EventNodesUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::accessibility::EventNodesUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::animation::EventAnimationCanceled { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::animation::EventAnimationCanceled { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::animation::EventAnimationCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::animation::EventAnimationCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::animation::EventAnimationStarted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::animation::EventAnimationStarted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::animation::EventAnimationUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::animation::EventAnimationUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::audits::EventIssueAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::audits::EventIssueAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::autofill::EventAddressFormFilled { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::autofill::EventAddressFormFilled { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::background_service::EventRecordingStateChanged + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::background_service::EventRecordingStateChanged + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::background_service::EventBackgroundServiceEventReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::background_service::EventBackgroundServiceEventReceived + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::bluetooth_emulation::EventGattOperationReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::bluetooth_emulation::EventGattOperationReceived + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::bluetooth_emulation::EventCharacteristicOperationReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::bluetooth_emulation::EventCharacteristicOperationReceived + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::bluetooth_emulation::EventDescriptorOperationReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::bluetooth_emulation::EventDescriptorOperationReceived + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::browser::EventDownloadWillBegin { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::browser::EventDownloadWillBegin { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::browser::EventDownloadProgress { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::browser::EventDownloadProgress { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::css::EventFontsUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::css::EventFontsUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::css::EventMediaQueryResultChanged { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::css::EventMediaQueryResultChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::css::EventStyleSheetAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::css::EventStyleSheetAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::css::EventStyleSheetChanged { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::css::EventStyleSheetChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::css::EventStyleSheetRemoved { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::css::EventStyleSheetRemoved { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::css::EventComputedStyleUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::css::EventComputedStyleUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::cast::EventSinksUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::cast::EventSinksUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::cast::EventIssueUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::cast::EventIssueUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventAttributeModified { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventAttributeModified { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventAdoptedStyleSheetsModified { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventAdoptedStyleSheetsModified { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventAttributeRemoved { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventAttributeRemoved { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventCharacterDataModified { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventCharacterDataModified { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventChildNodeCountUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventChildNodeCountUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventChildNodeInserted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventChildNodeInserted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventChildNodeRemoved { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventChildNodeRemoved { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventDistributedNodesUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventDistributedNodesUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventDocumentUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventDocumentUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventInlineStyleInvalidated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventInlineStyleInvalidated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventPseudoElementAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventPseudoElementAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventTopLayerElementsUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventTopLayerElementsUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventScrollableFlagUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventScrollableFlagUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::dom::EventAffectedByStartingStylesFlagUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::dom::EventAffectedByStartingStylesFlagUpdated + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventPseudoElementRemoved { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventPseudoElementRemoved { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventSetChildNodes { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventSetChildNodes { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventShadowRootPopped { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventShadowRootPopped { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom::EventShadowRootPushed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom::EventShadowRootPushed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::dom_storage::EventDomStorageItemAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom_storage::EventDomStorageItemAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::dom_storage::EventDomStorageItemRemoved + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom_storage::EventDomStorageItemRemoved { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::dom_storage::EventDomStorageItemUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom_storage::EventDomStorageItemUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::dom_storage::EventDomStorageItemsCleared + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::dom_storage::EventDomStorageItemsCleared { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::device_access::EventDeviceRequestPrompted + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::device_access::EventDeviceRequestPrompted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::emulation::EventVirtualTimeBudgetExpired + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::emulation::EventVirtualTimeBudgetExpired { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::fed_cm::EventDialogShown { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::fed_cm::EventDialogShown { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::fed_cm::EventDialogClosed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::fed_cm::EventDialogClosed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::fetch::EventRequestPaused { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::fetch::EventRequestPaused { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::fetch::EventAuthRequired { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::fetch::EventAuthRequired { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::input::EventDragIntercepted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::input::EventDragIntercepted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::inspector::EventDetached { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::inspector::EventDetached { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::inspector::EventTargetCrashed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::inspector::EventTargetCrashed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::inspector::EventTargetReloadedAfterCrash + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::inspector::EventTargetReloadedAfterCrash { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::inspector::EventWorkerScriptLoaded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::inspector::EventWorkerScriptLoaded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::layer_tree::EventLayerPainted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::layer_tree::EventLayerPainted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::layer_tree::EventLayerTreeDidChange { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::layer_tree::EventLayerTreeDidChange { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::log::EventEntryAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::log::EventEntryAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::media::EventPlayerPropertiesChanged { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::media::EventPlayerPropertiesChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::media::EventPlayerEventsAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::media::EventPlayerEventsAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::media::EventPlayerMessagesLogged { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::media::EventPlayerMessagesLogged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::media::EventPlayerErrorsRaised { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::media::EventPlayerErrorsRaised { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::media::EventPlayerCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::media::EventPlayerCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDataReceived { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDataReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventEventSourceMessageReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventEventSourceMessageReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventLoadingFailed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventLoadingFailed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventLoadingFinished { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventLoadingFinished { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventRequestServedFromCache { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventRequestServedFromCache { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventRequestWillBeSent { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventRequestWillBeSent { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventResourceChangedPriority { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventResourceChangedPriority { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventSignedExchangeReceived { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventSignedExchangeReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventResponseReceived { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventResponseReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebSocketClosed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebSocketClosed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebSocketCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebSocketCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebSocketFrameError { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebSocketFrameError { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebSocketFrameReceived { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebSocketFrameReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebSocketFrameSent { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebSocketFrameSent { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventWebSocketHandshakeResponseReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventWebSocketHandshakeResponseReceived + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventWebSocketWillSendHandshakeRequest + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventWebSocketWillSendHandshakeRequest + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebTransportCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebTransportCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventWebTransportConnectionEstablished + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventWebTransportConnectionEstablished + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventWebTransportClosed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventWebTransportClosed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectTcpSocketCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectTcpSocketCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectTcpSocketOpened { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectTcpSocketOpened { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectTcpSocketAborted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectTcpSocketAborted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectTcpSocketClosed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectTcpSocketClosed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDirectTcpSocketChunkSent + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectTcpSocketChunkSent { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDirectTcpSocketChunkReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectTcpSocketChunkReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDirectUdpSocketJoinedMulticastGroup + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventDirectUdpSocketJoinedMulticastGroup + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDirectUdpSocketLeftMulticastGroup + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventDirectUdpSocketLeftMulticastGroup + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectUdpSocketCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectUdpSocketCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectUdpSocketOpened { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectUdpSocketOpened { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectUdpSocketAborted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectUdpSocketAborted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventDirectUdpSocketClosed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectUdpSocketClosed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDirectUdpSocketChunkSent + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectUdpSocketChunkSent { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDirectUdpSocketChunkReceived + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDirectUdpSocketChunkReceived { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventRequestWillBeSentExtraInfo + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventRequestWillBeSentExtraInfo { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventResponseReceivedExtraInfo + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventResponseReceivedExtraInfo { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventResponseReceivedEarlyHints + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventResponseReceivedEarlyHints { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventTrustTokenOperationDone { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventTrustTokenOperationDone { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventPolicyUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventPolicyUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::network::EventReportingApiReportAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventReportingApiReportAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventReportingApiReportUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventReportingApiReportUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventReportingApiEndpointsChangedForOrigin + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventReportingApiEndpointsChangedForOrigin + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDeviceBoundSessionsAdded + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::network::EventDeviceBoundSessionsAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::network::EventDeviceBoundSessionEventOccurred + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::network::EventDeviceBoundSessionEventOccurred + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::overlay::EventInspectNodeRequested { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::overlay::EventInspectNodeRequested { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::overlay::EventNodeHighlightRequested { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::overlay::EventNodeHighlightRequested { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::overlay::EventScreenshotRequested { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::overlay::EventScreenshotRequested { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::overlay::EventInspectModeCanceled { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::overlay::EventInspectModeCanceled { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventDomContentEventFired { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventDomContentEventFired { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFileChooserOpened { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFileChooserOpened { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameAttached { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameAttached { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameDetached { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameDetached { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameSubtreeWillBeDetached { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameSubtreeWillBeDetached { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameNavigated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameNavigated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventDocumentOpened { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventDocumentOpened { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameResized { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameResized { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameStartedNavigating { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameStartedNavigating { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameRequestedNavigation { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameRequestedNavigation { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameStartedLoading { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameStartedLoading { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventFrameStoppedLoading { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventFrameStoppedLoading { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventInterstitialHidden { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventInterstitialHidden { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventInterstitialShown { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventInterstitialShown { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventJavascriptDialogClosed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventJavascriptDialogClosed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventJavascriptDialogOpening { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventJavascriptDialogOpening { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventLifecycleEvent { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventLifecycleEvent { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventBackForwardCacheNotUsed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventBackForwardCacheNotUsed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventLoadEventFired { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventLoadEventFired { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventNavigatedWithinDocument { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventNavigatedWithinDocument { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventScreencastFrame { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventScreencastFrame { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::page::EventScreencastVisibilityChanged + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventScreencastVisibilityChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventWindowOpen { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventWindowOpen { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::page::EventCompilationCacheProduced { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::page::EventCompilationCacheProduced { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::performance::EventMetrics { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::performance::EventMetrics { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::performance_timeline::EventTimelineEventAdded + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::performance_timeline::EventTimelineEventAdded + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::preload::EventRuleSetUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::preload::EventRuleSetUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::preload::EventRuleSetRemoved { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::preload::EventRuleSetRemoved { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::preload::EventPreloadEnabledStateUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::preload::EventPreloadEnabledStateUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::preload::EventPrefetchStatusUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::preload::EventPrefetchStatusUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::preload::EventPrerenderStatusUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::preload::EventPrerenderStatusUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::preload::EventPreloadingAttemptSourcesUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::preload::EventPreloadingAttemptSourcesUpdated + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::security::EventVisibleSecurityStateChanged + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::security::EventVisibleSecurityStateChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::service_worker::EventWorkerErrorReported + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::service_worker::EventWorkerErrorReported { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::service_worker::EventWorkerRegistrationUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::service_worker::EventWorkerRegistrationUpdated + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::service_worker::EventWorkerVersionUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::service_worker::EventWorkerVersionUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventCacheStorageContentUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventCacheStorageContentUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::storage::EventCacheStorageListUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventCacheStorageListUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::storage::EventIndexedDbContentUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventIndexedDbContentUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::storage::EventIndexedDbListUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventIndexedDbListUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::storage::EventInterestGroupAccessed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventInterestGroupAccessed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventInterestGroupAuctionEventOccurred + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventInterestGroupAuctionEventOccurred + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventInterestGroupAuctionNetworkRequestCreated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventInterestGroupAuctionNetworkRequestCreated + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::storage::EventSharedStorageAccessed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventSharedStorageAccessed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventSharedStorageWorkletOperationExecutionFinished + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventSharedStorageWorkletOperationExecutionFinished + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventStorageBucketCreatedOrUpdated + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventStorageBucketCreatedOrUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::storage::EventStorageBucketDeleted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::storage::EventStorageBucketDeleted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventAttributionReportingSourceRegistered + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventAttributionReportingSourceRegistered + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventAttributionReportingTriggerRegistered + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventAttributionReportingTriggerRegistered + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventAttributionReportingReportSent + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventAttributionReportingReportSent + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::storage::EventAttributionReportingVerboseDebugReportSent + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::storage::EventAttributionReportingVerboseDebugReportSent + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::target::EventAttachedToTarget { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventAttachedToTarget { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::target::EventDetachedFromTarget { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventDetachedFromTarget { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::target::EventReceivedMessageFromTarget + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventReceivedMessageFromTarget { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::target::EventTargetCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventTargetCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::target::EventTargetDestroyed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventTargetDestroyed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::target::EventTargetCrashed { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventTargetCrashed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::target::EventTargetInfoChanged { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::target::EventTargetInfoChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::tethering::EventAccepted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::tethering::EventAccepted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::tracing::EventBufferUsage { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::tracing::EventBufferUsage { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::tracing::EventDataCollected { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::tracing::EventDataCollected { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::tracing::EventTracingComplete { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::tracing::EventTracingComplete { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventContextCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventContextCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::web_audio::EventContextWillBeDestroyed + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventContextWillBeDestroyed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventContextChanged { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventContextChanged { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventAudioListenerCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventAudioListenerCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::web_audio::EventAudioListenerWillBeDestroyed + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind + for super::browser_protocol::web_audio::EventAudioListenerWillBeDestroyed + { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventAudioNodeCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventAudioNodeCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::web_audio::EventAudioNodeWillBeDestroyed + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventAudioNodeWillBeDestroyed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventAudioParamCreated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventAudioParamCreated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent + for super::browser_protocol::web_audio::EventAudioParamWillBeDestroyed + { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventAudioParamWillBeDestroyed { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventNodesConnected { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventNodesConnected { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventNodesDisconnected { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventNodesDisconnected { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventNodeParamConnected { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventNodeParamConnected { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_audio::EventNodeParamDisconnected { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_audio::EventNodeParamDisconnected { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_authn::EventCredentialAdded { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_authn::EventCredentialAdded { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_authn::EventCredentialDeleted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_authn::EventCredentialDeleted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_authn::EventCredentialUpdated { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_authn::EventCredentialUpdated { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + impl super::sealed::SealedEvent for super::browser_protocol::web_authn::EventCredentialAsserted { + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + } + impl super::IntoEventKind for super::browser_protocol::web_authn::EventCredentialAsserted { + fn event_kind() -> super::EventKind + where + Self: Sized + 'static, + { + super::EventKind::BuiltIn + } + } + #[macro_export] + #[doc(hidden)] + macro_rules! consume_event { + (match $ ev : ident { $ builtin : expr , $ custom : expr }) => {{ + match $ev { + CdpEvent::DebuggerPaused(event) => { + $builtin(event); + } + CdpEvent::DebuggerResumed(event) => { + $builtin(event); + } + CdpEvent::DebuggerScriptFailedToParse(event) => { + $builtin(*event); + } + CdpEvent::DebuggerScriptParsed(event) => { + $builtin(*event); + } + CdpEvent::HeapProfilerAddHeapSnapshotChunk(event) => { + $builtin(event); + } + CdpEvent::HeapProfilerHeapStatsUpdate(event) => { + $builtin(event); + } + CdpEvent::HeapProfilerLastSeenObjectId(event) => { + $builtin(event); + } + CdpEvent::HeapProfilerReportHeapSnapshotProgress(event) => { + $builtin(event); + } + CdpEvent::HeapProfilerResetProfiles(event) => { + $builtin(event); + } + CdpEvent::ProfilerConsoleProfileFinished(event) => { + $builtin(event); + } + CdpEvent::ProfilerConsoleProfileStarted(event) => { + $builtin(event); + } + CdpEvent::ProfilerPreciseCoverageDeltaUpdate(event) => { + $builtin(event); + } + CdpEvent::RuntimeBindingCalled(event) => { + $builtin(event); + } + CdpEvent::RuntimeConsoleApiCalled(event) => { + $builtin(event); + } + CdpEvent::RuntimeExceptionRevoked(event) => { + $builtin(event); + } + CdpEvent::RuntimeExceptionThrown(event) => { + $builtin(*event); + } + CdpEvent::RuntimeExecutionContextCreated(event) => { + $builtin(event); + } + CdpEvent::RuntimeExecutionContextDestroyed(event) => { + $builtin(event); + } + CdpEvent::RuntimeExecutionContextsCleared(event) => { + $builtin(event); + } + CdpEvent::RuntimeInspectRequested(event) => { + $builtin(*event); + } + CdpEvent::AccessibilityLoadComplete(event) => { + $builtin(*event); + } + CdpEvent::AccessibilityNodesUpdated(event) => { + $builtin(event); + } + CdpEvent::AnimationAnimationCanceled(event) => { + $builtin(event); + } + CdpEvent::AnimationAnimationCreated(event) => { + $builtin(event); + } + CdpEvent::AnimationAnimationStarted(event) => { + $builtin(*event); + } + CdpEvent::AnimationAnimationUpdated(event) => { + $builtin(*event); + } + CdpEvent::AuditsIssueAdded(event) => { + $builtin(*event); + } + CdpEvent::AutofillAddressFormFilled(event) => { + $builtin(event); + } + CdpEvent::BackgroundServiceRecordingStateChanged(event) => { + $builtin(event); + } + CdpEvent::BackgroundServiceBackgroundServiceEventReceived(event) => { + $builtin(event); + } + CdpEvent::BluetoothEmulationGattOperationReceived(event) => { + $builtin(event); + } + CdpEvent::BluetoothEmulationCharacteristicOperationReceived(event) => { + $builtin(event); + } + CdpEvent::BluetoothEmulationDescriptorOperationReceived(event) => { + $builtin(event); + } + CdpEvent::BrowserDownloadWillBegin(event) => { + $builtin(event); + } + CdpEvent::BrowserDownloadProgress(event) => { + $builtin(event); + } + CdpEvent::CssFontsUpdated(event) => { + $builtin(*event); + } + CdpEvent::CssMediaQueryResultChanged(event) => { + $builtin(event); + } + CdpEvent::CssStyleSheetAdded(event) => { + $builtin(event); + } + CdpEvent::CssStyleSheetChanged(event) => { + $builtin(event); + } + CdpEvent::CssStyleSheetRemoved(event) => { + $builtin(event); + } + CdpEvent::CssComputedStyleUpdated(event) => { + $builtin(event); + } + CdpEvent::CastSinksUpdated(event) => { + $builtin(event); + } + CdpEvent::CastIssueUpdated(event) => { + $builtin(event); + } + CdpEvent::DomAttributeModified(event) => { + $builtin(event); + } + CdpEvent::DomAdoptedStyleSheetsModified(event) => { + $builtin(event); + } + CdpEvent::DomAttributeRemoved(event) => { + $builtin(event); + } + CdpEvent::DomCharacterDataModified(event) => { + $builtin(event); + } + CdpEvent::DomChildNodeCountUpdated(event) => { + $builtin(event); + } + CdpEvent::DomChildNodeInserted(event) => { + $builtin(*event); + } + CdpEvent::DomChildNodeRemoved(event) => { + $builtin(event); + } + CdpEvent::DomDistributedNodesUpdated(event) => { + $builtin(event); + } + CdpEvent::DomDocumentUpdated(event) => { + $builtin(event); + } + CdpEvent::DomInlineStyleInvalidated(event) => { + $builtin(event); + } + CdpEvent::DomPseudoElementAdded(event) => { + $builtin(*event); + } + CdpEvent::DomTopLayerElementsUpdated(event) => { + $builtin(event); + } + CdpEvent::DomScrollableFlagUpdated(event) => { + $builtin(event); + } + CdpEvent::DomAffectedByStartingStylesFlagUpdated(event) => { + $builtin(event); + } + CdpEvent::DomPseudoElementRemoved(event) => { + $builtin(event); + } + CdpEvent::DomSetChildNodes(event) => { + $builtin(event); + } + CdpEvent::DomShadowRootPopped(event) => { + $builtin(event); + } + CdpEvent::DomShadowRootPushed(event) => { + $builtin(*event); + } + CdpEvent::DomStorageDomStorageItemAdded(event) => { + $builtin(event); + } + CdpEvent::DomStorageDomStorageItemRemoved(event) => { + $builtin(event); + } + CdpEvent::DomStorageDomStorageItemUpdated(event) => { + $builtin(event); + } + CdpEvent::DomStorageDomStorageItemsCleared(event) => { + $builtin(event); + } + CdpEvent::DeviceAccessDeviceRequestPrompted(event) => { + $builtin(event); + } + CdpEvent::EmulationVirtualTimeBudgetExpired(event) => { + $builtin(event); + } + CdpEvent::FedCmDialogShown(event) => { + $builtin(event); + } + CdpEvent::FedCmDialogClosed(event) => { + $builtin(event); + } + CdpEvent::FetchRequestPaused(event) => { + $builtin(*event); + } + CdpEvent::FetchAuthRequired(event) => { + $builtin(*event); + } + CdpEvent::InputDragIntercepted(event) => { + $builtin(event); + } + CdpEvent::InspectorDetached(event) => { + $builtin(event); + } + CdpEvent::InspectorTargetCrashed(event) => { + $builtin(event); + } + CdpEvent::InspectorTargetReloadedAfterCrash(event) => { + $builtin(event); + } + CdpEvent::InspectorWorkerScriptLoaded(event) => { + $builtin(event); + } + CdpEvent::LayerTreeLayerPainted(event) => { + $builtin(event); + } + CdpEvent::LayerTreeLayerTreeDidChange(event) => { + $builtin(event); + } + CdpEvent::LogEntryAdded(event) => { + $builtin(*event); + } + CdpEvent::MediaPlayerPropertiesChanged(event) => { + $builtin(event); + } + CdpEvent::MediaPlayerEventsAdded(event) => { + $builtin(event); + } + CdpEvent::MediaPlayerMessagesLogged(event) => { + $builtin(event); + } + CdpEvent::MediaPlayerErrorsRaised(event) => { + $builtin(event); + } + CdpEvent::MediaPlayerCreated(event) => { + $builtin(event); + } + CdpEvent::NetworkDataReceived(event) => { + $builtin(event); + } + CdpEvent::NetworkEventSourceMessageReceived(event) => { + $builtin(event); + } + CdpEvent::NetworkLoadingFailed(event) => { + $builtin(event); + } + CdpEvent::NetworkLoadingFinished(event) => { + $builtin(event); + } + CdpEvent::NetworkRequestServedFromCache(event) => { + $builtin(event); + } + CdpEvent::NetworkRequestWillBeSent(event) => { + $builtin(*event); + } + CdpEvent::NetworkResourceChangedPriority(event) => { + $builtin(event); + } + CdpEvent::NetworkSignedExchangeReceived(event) => { + $builtin(*event); + } + CdpEvent::NetworkResponseReceived(event) => { + $builtin(*event); + } + CdpEvent::NetworkWebSocketClosed(event) => { + $builtin(event); + } + CdpEvent::NetworkWebSocketCreated(event) => { + $builtin(*event); + } + CdpEvent::NetworkWebSocketFrameError(event) => { + $builtin(event); + } + CdpEvent::NetworkWebSocketFrameReceived(event) => { + $builtin(event); + } + CdpEvent::NetworkWebSocketFrameSent(event) => { + $builtin(event); + } + CdpEvent::NetworkWebSocketHandshakeResponseReceived(event) => { + $builtin(*event); + } + CdpEvent::NetworkWebSocketWillSendHandshakeRequest(event) => { + $builtin(event); + } + CdpEvent::NetworkWebTransportCreated(event) => { + $builtin(*event); + } + CdpEvent::NetworkWebTransportConnectionEstablished(event) => { + $builtin(event); + } + CdpEvent::NetworkWebTransportClosed(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectTcpSocketCreated(event) => { + $builtin(*event); + } + CdpEvent::NetworkDirectTcpSocketOpened(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectTcpSocketAborted(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectTcpSocketClosed(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectTcpSocketChunkSent(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectTcpSocketChunkReceived(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketJoinedMulticastGroup(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketLeftMulticastGroup(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketCreated(event) => { + $builtin(*event); + } + CdpEvent::NetworkDirectUdpSocketOpened(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketAborted(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketClosed(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketChunkSent(event) => { + $builtin(event); + } + CdpEvent::NetworkDirectUdpSocketChunkReceived(event) => { + $builtin(event); + } + CdpEvent::NetworkRequestWillBeSentExtraInfo(event) => { + $builtin(event); + } + CdpEvent::NetworkResponseReceivedExtraInfo(event) => { + $builtin(*event); + } + CdpEvent::NetworkResponseReceivedEarlyHints(event) => { + $builtin(event); + } + CdpEvent::NetworkTrustTokenOperationDone(event) => { + $builtin(event); + } + CdpEvent::NetworkPolicyUpdated(event) => { + $builtin(event); + } + CdpEvent::NetworkReportingApiReportAdded(event) => { + $builtin(event); + } + CdpEvent::NetworkReportingApiReportUpdated(event) => { + $builtin(event); + } + CdpEvent::NetworkReportingApiEndpointsChangedForOrigin(event) => { + $builtin(event); + } + CdpEvent::NetworkDeviceBoundSessionsAdded(event) => { + $builtin(event); + } + CdpEvent::NetworkDeviceBoundSessionEventOccurred(event) => { + $builtin(*event); + } + CdpEvent::OverlayInspectNodeRequested(event) => { + $builtin(event); + } + CdpEvent::OverlayNodeHighlightRequested(event) => { + $builtin(event); + } + CdpEvent::OverlayScreenshotRequested(event) => { + $builtin(event); + } + CdpEvent::OverlayInspectModeCanceled(event) => { + $builtin(event); + } + CdpEvent::PageDomContentEventFired(event) => { + $builtin(event); + } + CdpEvent::PageFileChooserOpened(event) => { + $builtin(event); + } + CdpEvent::PageFrameAttached(event) => { + $builtin(event); + } + CdpEvent::PageFrameDetached(event) => { + $builtin(event); + } + CdpEvent::PageFrameSubtreeWillBeDetached(event) => { + $builtin(event); + } + CdpEvent::PageFrameNavigated(event) => { + $builtin(*event); + } + CdpEvent::PageDocumentOpened(event) => { + $builtin(*event); + } + CdpEvent::PageFrameResized(event) => { + $builtin(event); + } + CdpEvent::PageFrameStartedNavigating(event) => { + $builtin(event); + } + CdpEvent::PageFrameRequestedNavigation(event) => { + $builtin(event); + } + CdpEvent::PageFrameStartedLoading(event) => { + $builtin(event); + } + CdpEvent::PageFrameStoppedLoading(event) => { + $builtin(event); + } + CdpEvent::PageInterstitialHidden(event) => { + $builtin(event); + } + CdpEvent::PageInterstitialShown(event) => { + $builtin(event); + } + CdpEvent::PageJavascriptDialogClosed(event) => { + $builtin(event); + } + CdpEvent::PageJavascriptDialogOpening(event) => { + $builtin(event); + } + CdpEvent::PageLifecycleEvent(event) => { + $builtin(event); + } + CdpEvent::PageBackForwardCacheNotUsed(event) => { + $builtin(event); + } + CdpEvent::PageLoadEventFired(event) => { + $builtin(event); + } + CdpEvent::PageNavigatedWithinDocument(event) => { + $builtin(event); + } + CdpEvent::PageScreencastFrame(event) => { + $builtin(event); + } + CdpEvent::PageScreencastVisibilityChanged(event) => { + $builtin(event); + } + CdpEvent::PageWindowOpen(event) => { + $builtin(event); + } + CdpEvent::PageCompilationCacheProduced(event) => { + $builtin(event); + } + CdpEvent::PerformanceMetrics(event) => { + $builtin(event); + } + CdpEvent::PerformanceTimelineTimelineEventAdded(event) => { + $builtin(*event); + } + CdpEvent::PreloadRuleSetUpdated(event) => { + $builtin(*event); + } + CdpEvent::PreloadRuleSetRemoved(event) => { + $builtin(event); + } + CdpEvent::PreloadPreloadEnabledStateUpdated(event) => { + $builtin(event); + } + CdpEvent::PreloadPrefetchStatusUpdated(event) => { + $builtin(*event); + } + CdpEvent::PreloadPrerenderStatusUpdated(event) => { + $builtin(event); + } + CdpEvent::PreloadPreloadingAttemptSourcesUpdated(event) => { + $builtin(event); + } + CdpEvent::SecurityVisibleSecurityStateChanged(event) => { + $builtin(*event); + } + CdpEvent::ServiceWorkerWorkerErrorReported(event) => { + $builtin(event); + } + CdpEvent::ServiceWorkerWorkerRegistrationUpdated(event) => { + $builtin(event); + } + CdpEvent::ServiceWorkerWorkerVersionUpdated(event) => { + $builtin(event); + } + CdpEvent::StorageCacheStorageContentUpdated(event) => { + $builtin(event); + } + CdpEvent::StorageCacheStorageListUpdated(event) => { + $builtin(event); + } + CdpEvent::StorageIndexedDbContentUpdated(event) => { + $builtin(event); + } + CdpEvent::StorageIndexedDbListUpdated(event) => { + $builtin(event); + } + CdpEvent::StorageInterestGroupAccessed(event) => { + $builtin(event); + } + CdpEvent::StorageInterestGroupAuctionEventOccurred(event) => { + $builtin(event); + } + CdpEvent::StorageInterestGroupAuctionNetworkRequestCreated(event) => { + $builtin(event); + } + CdpEvent::StorageSharedStorageAccessed(event) => { + $builtin(*event); + } + CdpEvent::StorageSharedStorageWorkletOperationExecutionFinished(event) => { + $builtin(event); + } + CdpEvent::StorageStorageBucketCreatedOrUpdated(event) => { + $builtin(event); + } + CdpEvent::StorageStorageBucketDeleted(event) => { + $builtin(event); + } + CdpEvent::StorageAttributionReportingSourceRegistered(event) => { + $builtin(*event); + } + CdpEvent::StorageAttributionReportingTriggerRegistered(event) => { + $builtin(*event); + } + CdpEvent::StorageAttributionReportingReportSent(event) => { + $builtin(event); + } + CdpEvent::StorageAttributionReportingVerboseDebugReportSent(event) => { + $builtin(event); + } + CdpEvent::TargetAttachedToTarget(event) => { + $builtin(*event); + } + CdpEvent::TargetDetachedFromTarget(event) => { + $builtin(event); + } + CdpEvent::TargetReceivedMessageFromTarget(event) => { + $builtin(event); + } + CdpEvent::TargetTargetCreated(event) => { + $builtin(*event); + } + CdpEvent::TargetTargetDestroyed(event) => { + $builtin(event); + } + CdpEvent::TargetTargetCrashed(event) => { + $builtin(event); + } + CdpEvent::TargetTargetInfoChanged(event) => { + $builtin(*event); + } + CdpEvent::TetheringAccepted(event) => { + $builtin(event); + } + CdpEvent::TracingBufferUsage(event) => { + $builtin(event); + } + CdpEvent::TracingDataCollected(event) => { + $builtin(event); + } + CdpEvent::TracingTracingComplete(event) => { + $builtin(event); + } + CdpEvent::WebAudioContextCreated(event) => { + $builtin(event); + } + CdpEvent::WebAudioContextWillBeDestroyed(event) => { + $builtin(event); + } + CdpEvent::WebAudioContextChanged(event) => { + $builtin(event); + } + CdpEvent::WebAudioAudioListenerCreated(event) => { + $builtin(event); + } + CdpEvent::WebAudioAudioListenerWillBeDestroyed(event) => { + $builtin(event); + } + CdpEvent::WebAudioAudioNodeCreated(event) => { + $builtin(event); + } + CdpEvent::WebAudioAudioNodeWillBeDestroyed(event) => { + $builtin(event); + } + CdpEvent::WebAudioAudioParamCreated(event) => { + $builtin(event); + } + CdpEvent::WebAudioAudioParamWillBeDestroyed(event) => { + $builtin(event); + } + CdpEvent::WebAudioNodesConnected(event) => { + $builtin(event); + } + CdpEvent::WebAudioNodesDisconnected(event) => { + $builtin(event); + } + CdpEvent::WebAudioNodeParamConnected(event) => { + $builtin(event); + } + CdpEvent::WebAudioNodeParamDisconnected(event) => { + $builtin(event); + } + CdpEvent::WebAuthnCredentialAdded(event) => { + $builtin(*event); + } + CdpEvent::WebAuthnCredentialDeleted(event) => { + $builtin(event); + } + CdpEvent::WebAuthnCredentialUpdated(event) => { + $builtin(*event); + } + CdpEvent::WebAuthnCredentialAsserted(event) => { + $builtin(*event); + } + CdpEvent::Other(json) => { + $custom(json); + } + } + }}; + } +} +#[allow(clippy::wrong_self_convention)] +pub mod js_protocol { + #[doc = r" The version of this protocol definition"] + pub const VERSION: &str = "1.3"; + #[doc = "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing\nbreakpoints, stepping through execution, exploring stack traces, etc."] + pub mod debugger { + use serde::{Deserialize, Serialize}; + #[doc = "Breakpoint identifier.\n[BreakpointId](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-BreakpointId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct BreakpointId(String); + impl BreakpointId { + pub fn new(val: impl Into) -> Self { + BreakpointId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for BreakpointId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: BreakpointId) -> String { + el.0 + } + } + impl From for BreakpointId { + fn from(expr: String) -> Self { + BreakpointId(expr) + } + } + impl std::borrow::Borrow for BreakpointId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl BreakpointId { + pub const IDENTIFIER: &'static str = "Debugger.BreakpointId"; + } + #[doc = "Call frame identifier.\n[CallFrameId](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-CallFrameId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct CallFrameId(String); + impl CallFrameId { + pub fn new(val: impl Into) -> Self { + CallFrameId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for CallFrameId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: CallFrameId) -> String { + el.0 + } + } + impl From for CallFrameId { + fn from(expr: String) -> Self { + CallFrameId(expr) + } + } + impl std::borrow::Borrow for CallFrameId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl CallFrameId { + pub const IDENTIFIER: &'static str = "Debugger.CallFrameId"; + } + #[doc = "Location in the source code.\n[Location](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-Location)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct Location { + #[doc = "Script identifier as reported in the `Debugger.scriptParsed`."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "Line number in the script (0-based)."] + #[serde(rename = "lineNumber")] + pub line_number: i64, + #[doc = "Column number in the script (0-based)."] + #[serde(rename = "columnNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub column_number: Option, + } + impl Location { + pub fn new( + script_id: impl Into, + line_number: impl Into, + ) -> Self { + Self { + script_id: script_id.into(), + line_number: line_number.into(), + column_number: None, + } + } + } + impl Location { + pub fn builder() -> LocationBuilder { + LocationBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct LocationBuilder { + script_id: Option, + line_number: Option, + column_number: Option, + } + impl LocationBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn column_number(mut self, column_number: impl Into) -> Self { + self.column_number = Some(column_number.into()); + self + } + pub fn build(self) -> Result { + Ok(Location { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + column_number: self.column_number, + }) + } + } + impl Location { + pub const IDENTIFIER: &'static str = "Debugger.Location"; + } + #[doc = "Location in the source code.\n[ScriptPosition](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-ScriptPosition)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ScriptPosition { + #[serde(rename = "lineNumber")] + pub line_number: i64, + #[serde(rename = "columnNumber")] + pub column_number: i64, + } + impl ScriptPosition { + pub fn new(line_number: impl Into, column_number: impl Into) -> Self { + Self { + line_number: line_number.into(), + column_number: column_number.into(), + } + } + } + impl ScriptPosition { + pub fn builder() -> ScriptPositionBuilder { + ScriptPositionBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ScriptPositionBuilder { + line_number: Option, + column_number: Option, + } + impl ScriptPositionBuilder { + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn column_number(mut self, column_number: impl Into) -> Self { + self.column_number = Some(column_number.into()); + self + } + pub fn build(self) -> Result { + Ok(ScriptPosition { + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + column_number: self.column_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(column_number)) + })?, + }) + } + } + impl ScriptPosition { + pub const IDENTIFIER: &'static str = "Debugger.ScriptPosition"; + } + #[doc = "Location range within one script.\n[LocationRange](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-LocationRange)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct LocationRange { + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[serde(rename = "start")] + pub start: ScriptPosition, + #[serde(rename = "end")] + pub end: ScriptPosition, + } + impl LocationRange { + pub fn new( + script_id: impl Into, + start: impl Into, + end: impl Into, + ) -> Self { + Self { + script_id: script_id.into(), + start: start.into(), + end: end.into(), + } + } + } + impl LocationRange { + pub fn builder() -> LocationRangeBuilder { + LocationRangeBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct LocationRangeBuilder { + script_id: Option, + start: Option, + end: Option, + } + impl LocationRangeBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn start(mut self, start: impl Into) -> Self { + self.start = Some(start.into()); + self + } + pub fn end(mut self, end: impl Into) -> Self { + self.end = Some(end.into()); + self + } + pub fn build(self) -> Result { + Ok(LocationRange { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + start: self.start.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(start)) + })?, + end: self + .end + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(end)))?, + }) + } + } + impl LocationRange { + pub const IDENTIFIER: &'static str = "Debugger.LocationRange"; + } + #[doc = "JavaScript call frame. Array of call frames form the call stack.\n[CallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-CallFrame)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CallFrame { + #[doc = "Call frame identifier. This identifier is only valid while the virtual machine is paused."] + #[serde(rename = "callFrameId")] + pub call_frame_id: CallFrameId, + #[doc = "Name of the JavaScript function called on this call frame."] + #[serde(rename = "functionName")] + pub function_name: String, + #[doc = "Location in the source code."] + #[serde(rename = "functionLocation")] + #[serde(skip_serializing_if = "Option::is_none")] + pub function_location: Option, + #[doc = "Location in the source code."] + #[serde(rename = "location")] + pub location: Location, + #[doc = "Scope chain for this call frame."] + #[serde(rename = "scopeChain")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub scope_chain: Vec, + #[doc = "`this` object for this call frame."] + #[serde(rename = "this")] + pub this: super::runtime::RemoteObject, + #[doc = "The value being returned, if the function is at return point."] + #[serde(rename = "returnValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub return_value: Option, + #[doc = "Valid only while the VM is paused and indicates whether this frame\ncan be restarted or not. Note that a `true` value here does not\nguarantee that Debugger#restartFrame with this CallFrameId will be\nsuccessful, but it is very likely."] + #[serde(rename = "canBeRestarted")] + #[serde(skip_serializing_if = "Option::is_none")] + pub can_be_restarted: Option, + } + impl CallFrame { + pub fn builder() -> CallFrameBuilder { + CallFrameBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CallFrameBuilder { + call_frame_id: Option, + function_name: Option, + function_location: Option, + location: Option, + scope_chain: Option>, + this: Option, + return_value: Option, + can_be_restarted: Option, + } + impl CallFrameBuilder { + pub fn call_frame_id(mut self, call_frame_id: impl Into) -> Self { + self.call_frame_id = Some(call_frame_id.into()); + self + } + pub fn function_name(mut self, function_name: impl Into) -> Self { + self.function_name = Some(function_name.into()); + self + } + pub fn function_location(mut self, function_location: impl Into) -> Self { + self.function_location = Some(function_location.into()); + self + } + pub fn location(mut self, location: impl Into) -> Self { + self.location = Some(location.into()); + self + } + pub fn scope_chain(mut self, scope_chain: impl Into) -> Self { + let v = self.scope_chain.get_or_insert(Vec::new()); + v.push(scope_chain.into()); + self + } + pub fn scope_chains(mut self, scope_chains: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.scope_chain.get_or_insert(Vec::new()); + for val in scope_chains { + v.push(val.into()); + } + self + } + pub fn this(mut self, this: impl Into) -> Self { + self.this = Some(this.into()); + self + } + pub fn return_value( + mut self, + return_value: impl Into, + ) -> Self { + self.return_value = Some(return_value.into()); + self + } + pub fn can_be_restarted(mut self, can_be_restarted: impl Into) -> Self { + self.can_be_restarted = Some(can_be_restarted.into()); + self + } + pub fn build(self) -> Result { + Ok(CallFrame { + call_frame_id: self.call_frame_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frame_id)) + })?, + function_name: self.function_name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(function_name)) + })?, + function_location: self.function_location, + location: self.location.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(location)) + })?, + scope_chain: self.scope_chain.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(scope_chain)) + })?, + this: self.this.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(this)) + })?, + return_value: self.return_value, + can_be_restarted: self.can_be_restarted, + }) + } + } + impl CallFrame { + pub const IDENTIFIER: &'static str = "Debugger.CallFrame"; + } + #[doc = "Scope description.\n[Scope](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-Scope)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct Scope { + #[doc = "Scope type."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: ScopeType, + #[doc = "Object representing the scope. For `global` and `with` scopes it represents the actual\nobject; for the rest of the scopes, it is artificial transient object enumerating scope\nvariables as its properties."] + #[serde(rename = "object")] + pub object: super::runtime::RemoteObject, + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[doc = "Location in the source code where scope starts"] + #[serde(rename = "startLocation")] + #[serde(skip_serializing_if = "Option::is_none")] + pub start_location: Option, + #[doc = "Location in the source code where scope ends"] + #[serde(rename = "endLocation")] + #[serde(skip_serializing_if = "Option::is_none")] + pub end_location: Option, + } + #[doc = "Scope type."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum ScopeType { + #[serde(rename = "global")] + Global, + #[serde(rename = "local")] + Local, + #[serde(rename = "with")] + With, + #[serde(rename = "closure")] + Closure, + #[serde(rename = "catch")] + Catch, + #[serde(rename = "block")] + Block, + #[serde(rename = "script")] + Script, + #[serde(rename = "eval")] + Eval, + #[serde(rename = "module")] + Module, + #[serde(rename = "wasm-expression-stack")] + WasmExpressionStack, + } + impl AsRef for ScopeType { + fn as_ref(&self) -> &str { + match self { + ScopeType::Global => "global", + ScopeType::Local => "local", + ScopeType::With => "with", + ScopeType::Closure => "closure", + ScopeType::Catch => "catch", + ScopeType::Block => "block", + ScopeType::Script => "script", + ScopeType::Eval => "eval", + ScopeType::Module => "module", + ScopeType::WasmExpressionStack => "wasm-expression-stack", + } + } + } + impl ::std::str::FromStr for ScopeType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "global" | "Global" => Ok(ScopeType::Global), + "local" | "Local" => Ok(ScopeType::Local), + "with" | "With" => Ok(ScopeType::With), + "closure" | "Closure" => Ok(ScopeType::Closure), + "catch" | "Catch" => Ok(ScopeType::Catch), + "block" | "Block" => Ok(ScopeType::Block), + "script" | "Script" => Ok(ScopeType::Script), + "eval" | "Eval" => Ok(ScopeType::Eval), + "module" | "Module" => Ok(ScopeType::Module), + "wasm-expression-stack" | "WasmExpressionStack" => { + Ok(ScopeType::WasmExpressionStack) + } + _ => Err(s.to_string()), + } + } + } + impl Scope { + pub fn new( + r#type: impl Into, + object: impl Into, + ) -> Self { + Self { + r#type: r#type.into(), + object: object.into(), + name: None, + start_location: None, + end_location: None, + } + } + } + impl Scope { + pub fn builder() -> ScopeBuilder { + ScopeBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ScopeBuilder { + r#type: Option, + object: Option, + name: Option, + start_location: Option, + end_location: Option, + } + impl ScopeBuilder { + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn object(mut self, object: impl Into) -> Self { + self.object = Some(object.into()); + self + } + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn start_location(mut self, start_location: impl Into) -> Self { + self.start_location = Some(start_location.into()); + self + } + pub fn end_location(mut self, end_location: impl Into) -> Self { + self.end_location = Some(end_location.into()); + self + } + pub fn build(self) -> Result { + Ok(Scope { + r#type: self.r#type.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(r#type)) + })?, + object: self.object.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object)) + })?, + name: self.name, + start_location: self.start_location, + end_location: self.end_location, + }) + } + } + impl Scope { + pub const IDENTIFIER: &'static str = "Debugger.Scope"; + } + #[doc = "Search match for resource.\n[SearchMatch](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-SearchMatch)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SearchMatch { + #[doc = "Line number in resource content."] + #[serde(rename = "lineNumber")] + pub line_number: f64, + #[doc = "Line with match content."] + #[serde(rename = "lineContent")] + pub line_content: String, + } + impl SearchMatch { + pub fn new(line_number: impl Into, line_content: impl Into) -> Self { + Self { + line_number: line_number.into(), + line_content: line_content.into(), + } + } + } + impl SearchMatch { + pub fn builder() -> SearchMatchBuilder { + SearchMatchBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SearchMatchBuilder { + line_number: Option, + line_content: Option, + } + impl SearchMatchBuilder { + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn line_content(mut self, line_content: impl Into) -> Self { + self.line_content = Some(line_content.into()); + self + } + pub fn build(self) -> Result { + Ok(SearchMatch { + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + line_content: self.line_content.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_content)) + })?, + }) + } + } + impl SearchMatch { + pub const IDENTIFIER: &'static str = "Debugger.SearchMatch"; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct BreakLocation { + #[doc = "Script identifier as reported in the `Debugger.scriptParsed`."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "Line number in the script (0-based)."] + #[serde(rename = "lineNumber")] + pub line_number: i64, + #[doc = "Column number in the script (0-based)."] + #[serde(rename = "columnNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub column_number: Option, + #[serde(rename = "type")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub r#type: Option, + } + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum BreakLocationType { + #[serde(rename = "debuggerStatement")] + DebuggerStatement, + #[serde(rename = "call")] + Call, + #[serde(rename = "return")] + Return, + } + impl AsRef for BreakLocationType { + fn as_ref(&self) -> &str { + match self { + BreakLocationType::DebuggerStatement => "debuggerStatement", + BreakLocationType::Call => "call", + BreakLocationType::Return => "return", + } + } + } + impl ::std::str::FromStr for BreakLocationType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "debuggerStatement" | "DebuggerStatement" | "debuggerstatement" => { + Ok(BreakLocationType::DebuggerStatement) + } + "call" | "Call" => Ok(BreakLocationType::Call), + "return" | "Return" => Ok(BreakLocationType::Return), + _ => Err(s.to_string()), + } + } + } + impl BreakLocation { + pub fn new( + script_id: impl Into, + line_number: impl Into, + ) -> Self { + Self { + script_id: script_id.into(), + line_number: line_number.into(), + column_number: None, + r#type: None, + } + } + } + impl BreakLocation { + pub fn builder() -> BreakLocationBuilder { + BreakLocationBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct BreakLocationBuilder { + script_id: Option, + line_number: Option, + column_number: Option, + r#type: Option, + } + impl BreakLocationBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn column_number(mut self, column_number: impl Into) -> Self { + self.column_number = Some(column_number.into()); + self + } + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn build(self) -> Result { + Ok(BreakLocation { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + column_number: self.column_number, + r#type: self.r#type, + }) + } + } + impl BreakLocation { + pub const IDENTIFIER: &'static str = "Debugger.BreakLocation"; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct WasmDisassemblyChunk { + #[doc = "The next chunk of disassembled lines."] + #[serde(rename = "lines")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub lines: Vec, + #[doc = "The bytecode offsets describing the start of each line."] + #[serde(rename = "bytecodeOffsets")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub bytecode_offsets: Vec, + } + impl WasmDisassemblyChunk { + pub fn new(lines: Vec, bytecode_offsets: Vec) -> Self { + Self { + lines, + bytecode_offsets, + } + } + } + impl WasmDisassemblyChunk { + pub fn builder() -> WasmDisassemblyChunkBuilder { + WasmDisassemblyChunkBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct WasmDisassemblyChunkBuilder { + lines: Option>, + bytecode_offsets: Option>, + } + impl WasmDisassemblyChunkBuilder { + pub fn line(mut self, line: impl Into) -> Self { + let v = self.lines.get_or_insert(Vec::new()); + v.push(line.into()); + self + } + pub fn lines(mut self, lines: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.lines.get_or_insert(Vec::new()); + for val in lines { + v.push(val.into()); + } + self + } + pub fn bytecode_offset(mut self, bytecode_offset: impl Into) -> Self { + let v = self.bytecode_offsets.get_or_insert(Vec::new()); + v.push(bytecode_offset.into()); + self + } + pub fn bytecode_offsets(mut self, bytecode_offsets: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.bytecode_offsets.get_or_insert(Vec::new()); + for val in bytecode_offsets { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(WasmDisassemblyChunk { + lines: self.lines.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(lines)) + })?, + bytecode_offsets: self.bytecode_offsets.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(bytecode_offsets) + ) + })?, + }) + } + } + impl WasmDisassemblyChunk { + pub const IDENTIFIER: &'static str = "Debugger.WasmDisassemblyChunk"; + } + #[doc = "Enum of possible script languages."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum ScriptLanguage { + #[serde(rename = "JavaScript")] + JavaScript, + #[serde(rename = "WebAssembly")] + WebAssembly, + } + impl AsRef for ScriptLanguage { + fn as_ref(&self) -> &str { + match self { + ScriptLanguage::JavaScript => "JavaScript", + ScriptLanguage::WebAssembly => "WebAssembly", + } + } + } + impl ::std::str::FromStr for ScriptLanguage { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "JavaScript" | "javascript" => Ok(ScriptLanguage::JavaScript), + "WebAssembly" | "webassembly" => Ok(ScriptLanguage::WebAssembly), + _ => Err(s.to_string()), + } + } + } + #[doc = "Debug symbols available for a wasm script.\n[DebugSymbols](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-DebugSymbols)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct DebugSymbols { + #[doc = "Type of the debug symbols."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: DebugSymbolsType, + #[doc = "URL of the external symbol source."] + #[serde(rename = "externalURL")] + #[serde(skip_serializing_if = "Option::is_none")] + pub external_url: Option, + } + #[doc = "Type of the debug symbols."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum DebugSymbolsType { + #[serde(rename = "SourceMap")] + SourceMap, + #[serde(rename = "EmbeddedDWARF")] + EmbeddedDwarf, + #[serde(rename = "ExternalDWARF")] + ExternalDwarf, + } + impl AsRef for DebugSymbolsType { + fn as_ref(&self) -> &str { + match self { + DebugSymbolsType::SourceMap => "SourceMap", + DebugSymbolsType::EmbeddedDwarf => "EmbeddedDWARF", + DebugSymbolsType::ExternalDwarf => "ExternalDWARF", + } + } + } + impl ::std::str::FromStr for DebugSymbolsType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "SourceMap" | "sourcemap" => Ok(DebugSymbolsType::SourceMap), + "EmbeddedDWARF" | "EmbeddedDwarf" | "embeddeddwarf" => { + Ok(DebugSymbolsType::EmbeddedDwarf) + } + "ExternalDWARF" | "ExternalDwarf" | "externaldwarf" => { + Ok(DebugSymbolsType::ExternalDwarf) + } + _ => Err(s.to_string()), + } + } + } + impl DebugSymbols { + pub fn new(r#type: impl Into) -> Self { + Self { + r#type: r#type.into(), + external_url: None, + } + } + } + impl DebugSymbols { + pub fn builder() -> DebugSymbolsBuilder { + DebugSymbolsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct DebugSymbolsBuilder { + r#type: Option, + external_url: Option, + } + impl DebugSymbolsBuilder { + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn external_url(mut self, external_url: impl Into) -> Self { + self.external_url = Some(external_url.into()); + self + } + pub fn build(self) -> Result { + Ok(DebugSymbols { + r#type: self.r#type.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(r#type)) + })?, + external_url: self.external_url, + }) + } + } + impl DebugSymbols { + pub const IDENTIFIER: &'static str = "Debugger.DebugSymbols"; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ResolvedBreakpoint { + #[doc = "Breakpoint unique identifier."] + #[serde(rename = "breakpointId")] + pub breakpoint_id: BreakpointId, + #[doc = "Actual breakpoint location."] + #[serde(rename = "location")] + pub location: Location, + } + impl ResolvedBreakpoint { + pub fn new( + breakpoint_id: impl Into, + location: impl Into, + ) -> Self { + Self { + breakpoint_id: breakpoint_id.into(), + location: location.into(), + } + } + } + impl ResolvedBreakpoint { + pub fn builder() -> ResolvedBreakpointBuilder { + ResolvedBreakpointBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ResolvedBreakpointBuilder { + breakpoint_id: Option, + location: Option, + } + impl ResolvedBreakpointBuilder { + pub fn breakpoint_id(mut self, breakpoint_id: impl Into) -> Self { + self.breakpoint_id = Some(breakpoint_id.into()); + self + } + pub fn location(mut self, location: impl Into) -> Self { + self.location = Some(location.into()); + self + } + pub fn build(self) -> Result { + Ok(ResolvedBreakpoint { + breakpoint_id: self.breakpoint_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(breakpoint_id)) + })?, + location: self.location.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(location)) + })?, + }) + } + } + impl ResolvedBreakpoint { + pub const IDENTIFIER: &'static str = "Debugger.ResolvedBreakpoint"; + } + #[doc = "Continues execution until specific location is reached.\n[continueToLocation](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-continueToLocation)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ContinueToLocationParams { + #[doc = "Location to continue to."] + #[serde(rename = "location")] + pub location: Location, + #[serde(rename = "targetCallFrames")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub target_call_frames: Option, + } + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum ContinueToLocationTargetCallFrames { + #[serde(rename = "any")] + Any, + #[serde(rename = "current")] + Current, + } + impl AsRef for ContinueToLocationTargetCallFrames { + fn as_ref(&self) -> &str { + match self { + ContinueToLocationTargetCallFrames::Any => "any", + ContinueToLocationTargetCallFrames::Current => "current", + } + } + } + impl ::std::str::FromStr for ContinueToLocationTargetCallFrames { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "any" | "Any" => Ok(ContinueToLocationTargetCallFrames::Any), + "current" | "Current" => Ok(ContinueToLocationTargetCallFrames::Current), + _ => Err(s.to_string()), + } + } + } + impl ContinueToLocationParams { + pub fn new(location: impl Into) -> Self { + Self { + location: location.into(), + target_call_frames: None, + } + } + } + impl ContinueToLocationParams { + pub fn builder() -> ContinueToLocationParamsBuilder { + ContinueToLocationParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ContinueToLocationParamsBuilder { + location: Option, + target_call_frames: Option, + } + impl ContinueToLocationParamsBuilder { + pub fn location(mut self, location: impl Into) -> Self { + self.location = Some(location.into()); + self + } + pub fn target_call_frames( + mut self, + target_call_frames: impl Into, + ) -> Self { + self.target_call_frames = Some(target_call_frames.into()); + self + } + pub fn build(self) -> Result { + Ok(ContinueToLocationParams { + location: self.location.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(location)) + })?, + target_call_frames: self.target_call_frames, + }) + } + } + impl ContinueToLocationParams { + pub const IDENTIFIER: &'static str = "Debugger.continueToLocation"; + } + impl chromiumoxide_types::Method for ContinueToLocationParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for ContinueToLocationParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Continues execution until specific location is reached.\n[continueToLocation](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-continueToLocation)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct ContinueToLocationReturns {} + impl chromiumoxide_types::Command for ContinueToLocationParams { + type Response = ContinueToLocationReturns; + } + #[doc = "Disables debugger for given page.\n[disable](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-disable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableParams {} + impl DisableParams { + pub const IDENTIFIER: &'static str = "Debugger.disable"; + } + impl chromiumoxide_types::Method for DisableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for DisableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Disables debugger for given page.\n[disable](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-disable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableReturns {} + impl chromiumoxide_types::Command for DisableParams { + type Response = DisableReturns; + } + #[doc = "Enables debugger for the given page. Clients should not assume that the debugging has been\nenabled until the result for this command is received.\n[enable](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-enable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableParams { + #[doc = "The maximum size in bytes of collected scripts (not referenced by other heap objects)\nthe debugger can hold. Puts no limit if parameter is omitted."] + #[serde(rename = "maxScriptsCacheSize")] + #[serde(skip_serializing_if = "Option::is_none")] + pub max_scripts_cache_size: Option, + } + impl EnableParams { + pub fn builder() -> EnableParamsBuilder { + EnableParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EnableParamsBuilder { + max_scripts_cache_size: Option, + } + impl EnableParamsBuilder { + pub fn max_scripts_cache_size( + mut self, + max_scripts_cache_size: impl Into, + ) -> Self { + self.max_scripts_cache_size = Some(max_scripts_cache_size.into()); + self + } + pub fn build(self) -> EnableParams { + EnableParams { + max_scripts_cache_size: self.max_scripts_cache_size, + } + } + } + impl EnableParams { + pub const IDENTIFIER: &'static str = "Debugger.enable"; + } + impl chromiumoxide_types::Method for EnableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EnableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Enables debugger for the given page. Clients should not assume that the debugging has been\nenabled until the result for this command is received.\n[enable](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-enable)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EnableReturns { + #[doc = "Unique identifier of the debugger."] + #[serde(rename = "debuggerId")] + pub debugger_id: super::runtime::UniqueDebuggerId, + } + impl EnableReturns { + pub fn new(debugger_id: impl Into) -> Self { + Self { + debugger_id: debugger_id.into(), + } + } + } + impl EnableReturns { + pub fn builder() -> EnableReturnsBuilder { + EnableReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EnableReturnsBuilder { + debugger_id: Option, + } + impl EnableReturnsBuilder { + pub fn debugger_id( + mut self, + debugger_id: impl Into, + ) -> Self { + self.debugger_id = Some(debugger_id.into()); + self + } + pub fn build(self) -> Result { + Ok(EnableReturns { + debugger_id: self.debugger_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(debugger_id)) + })?, + }) + } + } + impl chromiumoxide_types::Command for EnableParams { + type Response = EnableReturns; + } + #[doc = "Evaluates expression on a given call frame.\n[evaluateOnCallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-evaluateOnCallFrame)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EvaluateOnCallFrameParams { + #[doc = "Call frame identifier to evaluate on."] + #[serde(rename = "callFrameId")] + pub call_frame_id: CallFrameId, + #[doc = "Expression to evaluate."] + #[serde(rename = "expression")] + pub expression: String, + #[doc = "String object group name to put result into (allows rapid releasing resulting object handles\nusing `releaseObjectGroup`)."] + #[serde(rename = "objectGroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[doc = "Specifies whether command line API should be available to the evaluated expression, defaults\nto false."] + #[serde(rename = "includeCommandLineAPI")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_command_line_api: Option, + #[doc = "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state."] + #[serde(rename = "silent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[doc = "Whether the result is expected to be a JSON object that should be sent by value."] + #[serde(rename = "returnByValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[doc = "Whether preview should be generated for the result."] + #[serde(rename = "generatePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[doc = "Whether to throw an exception if side effect cannot be ruled out during evaluation."] + #[serde(rename = "throwOnSideEffect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub throw_on_side_effect: Option, + #[doc = "Terminate execution after timing out (number of milliseconds)."] + #[serde(rename = "timeout")] + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout: Option, + } + impl EvaluateOnCallFrameParams { + pub fn new( + call_frame_id: impl Into, + expression: impl Into, + ) -> Self { + Self { + call_frame_id: call_frame_id.into(), + expression: expression.into(), + object_group: None, + include_command_line_api: None, + silent: None, + return_by_value: None, + generate_preview: None, + throw_on_side_effect: None, + timeout: None, + } + } + } + impl EvaluateOnCallFrameParams { + pub fn builder() -> EvaluateOnCallFrameParamsBuilder { + EvaluateOnCallFrameParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EvaluateOnCallFrameParamsBuilder { + call_frame_id: Option, + expression: Option, + object_group: Option, + include_command_line_api: Option, + silent: Option, + return_by_value: Option, + generate_preview: Option, + throw_on_side_effect: Option, + timeout: Option, + } + impl EvaluateOnCallFrameParamsBuilder { + pub fn call_frame_id(mut self, call_frame_id: impl Into) -> Self { + self.call_frame_id = Some(call_frame_id.into()); + self + } + pub fn expression(mut self, expression: impl Into) -> Self { + self.expression = Some(expression.into()); + self + } + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn include_command_line_api( + mut self, + include_command_line_api: impl Into, + ) -> Self { + self.include_command_line_api = Some(include_command_line_api.into()); + self + } + pub fn silent(mut self, silent: impl Into) -> Self { + self.silent = Some(silent.into()); + self + } + pub fn return_by_value(mut self, return_by_value: impl Into) -> Self { + self.return_by_value = Some(return_by_value.into()); + self + } + pub fn generate_preview(mut self, generate_preview: impl Into) -> Self { + self.generate_preview = Some(generate_preview.into()); + self + } + pub fn throw_on_side_effect(mut self, throw_on_side_effect: impl Into) -> Self { + self.throw_on_side_effect = Some(throw_on_side_effect.into()); + self + } + pub fn timeout(mut self, timeout: impl Into) -> Self { + self.timeout = Some(timeout.into()); + self + } + pub fn build(self) -> Result { + Ok(EvaluateOnCallFrameParams { + call_frame_id: self.call_frame_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frame_id)) + })?, + expression: self.expression.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(expression)) + })?, + object_group: self.object_group, + include_command_line_api: self.include_command_line_api, + silent: self.silent, + return_by_value: self.return_by_value, + generate_preview: self.generate_preview, + throw_on_side_effect: self.throw_on_side_effect, + timeout: self.timeout, + }) + } + } + impl EvaluateOnCallFrameParams { + pub const IDENTIFIER: &'static str = "Debugger.evaluateOnCallFrame"; + } + impl chromiumoxide_types::Method for EvaluateOnCallFrameParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EvaluateOnCallFrameParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Evaluates expression on a given call frame.\n[evaluateOnCallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-evaluateOnCallFrame)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EvaluateOnCallFrameReturns { + #[doc = "Object wrapper for the evaluation result."] + #[serde(rename = "result")] + pub result: super::runtime::RemoteObject, + #[doc = "Exception details."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl EvaluateOnCallFrameReturns { + pub fn new(result: impl Into) -> Self { + Self { + result: result.into(), + exception_details: None, + } + } + } + impl EvaluateOnCallFrameReturns { + pub fn builder() -> EvaluateOnCallFrameReturnsBuilder { + EvaluateOnCallFrameReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EvaluateOnCallFrameReturnsBuilder { + result: Option, + exception_details: Option, + } + impl EvaluateOnCallFrameReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + self.result = Some(result.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> Result { + Ok(EvaluateOnCallFrameReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + exception_details: self.exception_details, + }) + } + } + impl chromiumoxide_types::Command for EvaluateOnCallFrameParams { + type Response = EvaluateOnCallFrameReturns; + } + #[doc = "Returns possible locations for breakpoint. scriptId in start and end range locations should be\nthe same.\n[getPossibleBreakpoints](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getPossibleBreakpoints)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetPossibleBreakpointsParams { + #[doc = "Start of range to search possible breakpoint locations in."] + #[serde(rename = "start")] + pub start: Location, + #[doc = "End of range to search possible breakpoint locations in (excluding). When not specified, end\nof scripts is used as end of range."] + #[serde(rename = "end")] + #[serde(skip_serializing_if = "Option::is_none")] + pub end: Option, + #[doc = "Only consider locations which are in the same (non-nested) function as start."] + #[serde(rename = "restrictToFunction")] + #[serde(skip_serializing_if = "Option::is_none")] + pub restrict_to_function: Option, + } + impl GetPossibleBreakpointsParams { + pub fn new(start: impl Into) -> Self { + Self { + start: start.into(), + end: None, + restrict_to_function: None, + } + } + } + impl GetPossibleBreakpointsParams { + pub fn builder() -> GetPossibleBreakpointsParamsBuilder { + GetPossibleBreakpointsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetPossibleBreakpointsParamsBuilder { + start: Option, + end: Option, + restrict_to_function: Option, + } + impl GetPossibleBreakpointsParamsBuilder { + pub fn start(mut self, start: impl Into) -> Self { + self.start = Some(start.into()); + self + } + pub fn end(mut self, end: impl Into) -> Self { + self.end = Some(end.into()); + self + } + pub fn restrict_to_function(mut self, restrict_to_function: impl Into) -> Self { + self.restrict_to_function = Some(restrict_to_function.into()); + self + } + pub fn build(self) -> Result { + Ok(GetPossibleBreakpointsParams { + start: self.start.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(start)) + })?, + end: self.end, + restrict_to_function: self.restrict_to_function, + }) + } + } + impl GetPossibleBreakpointsParams { + pub const IDENTIFIER: &'static str = "Debugger.getPossibleBreakpoints"; + } + impl chromiumoxide_types::Method for GetPossibleBreakpointsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetPossibleBreakpointsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns possible locations for breakpoint. scriptId in start and end range locations should be\nthe same.\n[getPossibleBreakpoints](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getPossibleBreakpoints)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetPossibleBreakpointsReturns { + #[doc = "List of the possible breakpoint locations."] + #[serde(rename = "locations")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub locations: Vec, + } + impl GetPossibleBreakpointsReturns { + pub fn new(locations: Vec) -> Self { + Self { locations } + } + } + impl GetPossibleBreakpointsReturns { + pub fn builder() -> GetPossibleBreakpointsReturnsBuilder { + GetPossibleBreakpointsReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetPossibleBreakpointsReturnsBuilder { + locations: Option>, + } + impl GetPossibleBreakpointsReturnsBuilder { + pub fn location(mut self, location: impl Into) -> Self { + let v = self.locations.get_or_insert(Vec::new()); + v.push(location.into()); + self + } + pub fn locations(mut self, locations: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.locations.get_or_insert(Vec::new()); + for val in locations { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(GetPossibleBreakpointsReturns { + locations: self.locations.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(locations)) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetPossibleBreakpointsParams { + type Response = GetPossibleBreakpointsReturns; + } + #[doc = "Returns source for the script with given id.\n[getScriptSource](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getScriptSource)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetScriptSourceParams { + #[doc = "Id of the script to get source for."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + } + impl GetScriptSourceParams { + pub fn new(script_id: impl Into) -> Self { + Self { + script_id: script_id.into(), + } + } + } + impl GetScriptSourceParams { + pub fn builder() -> GetScriptSourceParamsBuilder { + GetScriptSourceParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetScriptSourceParamsBuilder { + script_id: Option, + } + impl GetScriptSourceParamsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn build(self) -> Result { + Ok(GetScriptSourceParams { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + }) + } + } + impl GetScriptSourceParams { + pub const IDENTIFIER: &'static str = "Debugger.getScriptSource"; + } + impl chromiumoxide_types::Method for GetScriptSourceParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetScriptSourceParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns source for the script with given id.\n[getScriptSource](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getScriptSource)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetScriptSourceReturns { + #[doc = "Script source (empty in case of Wasm bytecode)."] + #[serde(rename = "scriptSource")] + pub script_source: String, + #[doc = "Wasm bytecode."] + #[serde(rename = "bytecode")] + #[serde(skip_serializing_if = "Option::is_none")] + pub bytecode: Option, + } + impl GetScriptSourceReturns { + pub fn new(script_source: impl Into) -> Self { + Self { + script_source: script_source.into(), + bytecode: None, + } + } + } + impl> From for GetScriptSourceReturns { + fn from(url: T) -> Self { + GetScriptSourceReturns::new(url) + } + } + impl GetScriptSourceReturns { + pub fn builder() -> GetScriptSourceReturnsBuilder { + GetScriptSourceReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetScriptSourceReturnsBuilder { + script_source: Option, + bytecode: Option, + } + impl GetScriptSourceReturnsBuilder { + pub fn script_source(mut self, script_source: impl Into) -> Self { + self.script_source = Some(script_source.into()); + self + } + pub fn bytecode(mut self, bytecode: impl Into) -> Self { + self.bytecode = Some(bytecode.into()); + self + } + pub fn build(self) -> Result { + Ok(GetScriptSourceReturns { + script_source: self.script_source.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_source)) + })?, + bytecode: self.bytecode, + }) + } + } + impl chromiumoxide_types::Command for GetScriptSourceParams { + type Response = GetScriptSourceReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct DisassembleWasmModuleParams { + #[doc = "Id of the script to disassemble"] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + } + impl DisassembleWasmModuleParams { + pub fn new(script_id: impl Into) -> Self { + Self { + script_id: script_id.into(), + } + } + } + impl DisassembleWasmModuleParams { + pub fn builder() -> DisassembleWasmModuleParamsBuilder { + DisassembleWasmModuleParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct DisassembleWasmModuleParamsBuilder { + script_id: Option, + } + impl DisassembleWasmModuleParamsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn build(self) -> Result { + Ok(DisassembleWasmModuleParams { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + }) + } + } + impl DisassembleWasmModuleParams { + pub const IDENTIFIER: &'static str = "Debugger.disassembleWasmModule"; + } + impl chromiumoxide_types::Method for DisassembleWasmModuleParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for DisassembleWasmModuleParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct DisassembleWasmModuleReturns { + #[doc = "For large modules, return a stream from which additional chunks of\ndisassembly can be read successively."] + #[serde(rename = "streamId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub stream_id: Option, + #[doc = "The total number of lines in the disassembly text."] + #[serde(rename = "totalNumberOfLines")] + pub total_number_of_lines: i64, + #[doc = "The offsets of all function bodies, in the format [start1, end1,\nstart2, end2, ...] where all ends are exclusive."] + #[serde(rename = "functionBodyOffsets")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub function_body_offsets: Vec, + #[doc = "The first chunk of disassembly."] + #[serde(rename = "chunk")] + pub chunk: WasmDisassemblyChunk, + } + impl DisassembleWasmModuleReturns { + pub fn new( + total_number_of_lines: impl Into, + function_body_offsets: Vec, + chunk: impl Into, + ) -> Self { + Self { + total_number_of_lines: total_number_of_lines.into(), + function_body_offsets, + chunk: chunk.into(), + stream_id: None, + } + } + } + impl DisassembleWasmModuleReturns { + pub fn builder() -> DisassembleWasmModuleReturnsBuilder { + DisassembleWasmModuleReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct DisassembleWasmModuleReturnsBuilder { + stream_id: Option, + total_number_of_lines: Option, + function_body_offsets: Option>, + chunk: Option, + } + impl DisassembleWasmModuleReturnsBuilder { + pub fn stream_id(mut self, stream_id: impl Into) -> Self { + self.stream_id = Some(stream_id.into()); + self + } + pub fn total_number_of_lines(mut self, total_number_of_lines: impl Into) -> Self { + self.total_number_of_lines = Some(total_number_of_lines.into()); + self + } + pub fn function_body_offset(mut self, function_body_offset: impl Into) -> Self { + let v = self.function_body_offsets.get_or_insert(Vec::new()); + v.push(function_body_offset.into()); + self + } + pub fn function_body_offsets(mut self, function_body_offsets: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.function_body_offsets.get_or_insert(Vec::new()); + for val in function_body_offsets { + v.push(val.into()); + } + self + } + pub fn chunk(mut self, chunk: impl Into) -> Self { + self.chunk = Some(chunk.into()); + self + } + pub fn build(self) -> Result { + Ok(DisassembleWasmModuleReturns { + stream_id: self.stream_id, + total_number_of_lines: self.total_number_of_lines.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(total_number_of_lines) + ) + })?, + function_body_offsets: self.function_body_offsets.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(function_body_offsets) + ) + })?, + chunk: self.chunk.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(chunk)) + })?, + }) + } + } + impl chromiumoxide_types::Command for DisassembleWasmModuleParams { + type Response = DisassembleWasmModuleReturns; + } + #[doc = "Disassemble the next chunk of lines for the module corresponding to the\nstream. If disassembly is complete, this API will invalidate the streamId\nand return an empty chunk. Any subsequent calls for the now invalid stream\nwill return errors.\n[nextWasmDisassemblyChunk](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-nextWasmDisassemblyChunk)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct NextWasmDisassemblyChunkParams { + #[serde(rename = "streamId")] + pub stream_id: String, + } + impl NextWasmDisassemblyChunkParams { + pub fn new(stream_id: impl Into) -> Self { + Self { + stream_id: stream_id.into(), + } + } + } + impl> From for NextWasmDisassemblyChunkParams { + fn from(url: T) -> Self { + NextWasmDisassemblyChunkParams::new(url) + } + } + impl NextWasmDisassemblyChunkParams { + pub fn builder() -> NextWasmDisassemblyChunkParamsBuilder { + NextWasmDisassemblyChunkParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct NextWasmDisassemblyChunkParamsBuilder { + stream_id: Option, + } + impl NextWasmDisassemblyChunkParamsBuilder { + pub fn stream_id(mut self, stream_id: impl Into) -> Self { + self.stream_id = Some(stream_id.into()); + self + } + pub fn build(self) -> Result { + Ok(NextWasmDisassemblyChunkParams { + stream_id: self.stream_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(stream_id)) + })?, + }) + } + } + impl NextWasmDisassemblyChunkParams { + pub const IDENTIFIER: &'static str = "Debugger.nextWasmDisassemblyChunk"; + } + impl chromiumoxide_types::Method for NextWasmDisassemblyChunkParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for NextWasmDisassemblyChunkParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Disassemble the next chunk of lines for the module corresponding to the\nstream. If disassembly is complete, this API will invalidate the streamId\nand return an empty chunk. Any subsequent calls for the now invalid stream\nwill return errors.\n[nextWasmDisassemblyChunk](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-nextWasmDisassemblyChunk)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct NextWasmDisassemblyChunkReturns { + #[doc = "The next chunk of disassembly."] + #[serde(rename = "chunk")] + pub chunk: WasmDisassemblyChunk, + } + impl NextWasmDisassemblyChunkReturns { + pub fn new(chunk: impl Into) -> Self { + Self { + chunk: chunk.into(), + } + } + } + impl NextWasmDisassemblyChunkReturns { + pub fn builder() -> NextWasmDisassemblyChunkReturnsBuilder { + NextWasmDisassemblyChunkReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct NextWasmDisassemblyChunkReturnsBuilder { + chunk: Option, + } + impl NextWasmDisassemblyChunkReturnsBuilder { + pub fn chunk(mut self, chunk: impl Into) -> Self { + self.chunk = Some(chunk.into()); + self + } + pub fn build(self) -> Result { + Ok(NextWasmDisassemblyChunkReturns { + chunk: self.chunk.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(chunk)) + })?, + }) + } + } + impl chromiumoxide_types::Command for NextWasmDisassemblyChunkParams { + type Response = NextWasmDisassemblyChunkReturns; + } + #[doc = "Returns stack trace with given `stackTraceId`.\n[getStackTrace](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getStackTrace)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetStackTraceParams { + #[serde(rename = "stackTraceId")] + pub stack_trace_id: super::runtime::StackTraceId, + } + impl GetStackTraceParams { + pub fn new(stack_trace_id: impl Into) -> Self { + Self { + stack_trace_id: stack_trace_id.into(), + } + } + } + impl GetStackTraceParams { + pub fn builder() -> GetStackTraceParamsBuilder { + GetStackTraceParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetStackTraceParamsBuilder { + stack_trace_id: Option, + } + impl GetStackTraceParamsBuilder { + pub fn stack_trace_id( + mut self, + stack_trace_id: impl Into, + ) -> Self { + self.stack_trace_id = Some(stack_trace_id.into()); + self + } + pub fn build(self) -> Result { + Ok(GetStackTraceParams { + stack_trace_id: self.stack_trace_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(stack_trace_id)) + })?, + }) + } + } + impl GetStackTraceParams { + pub const IDENTIFIER: &'static str = "Debugger.getStackTrace"; + } + impl chromiumoxide_types::Method for GetStackTraceParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetStackTraceParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns stack trace with given `stackTraceId`.\n[getStackTrace](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getStackTrace)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetStackTraceReturns { + #[serde(rename = "stackTrace")] + pub stack_trace: super::runtime::StackTrace, + } + impl GetStackTraceReturns { + pub fn new(stack_trace: impl Into) -> Self { + Self { + stack_trace: stack_trace.into(), + } + } + } + impl GetStackTraceReturns { + pub fn builder() -> GetStackTraceReturnsBuilder { + GetStackTraceReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetStackTraceReturnsBuilder { + stack_trace: Option, + } + impl GetStackTraceReturnsBuilder { + pub fn stack_trace( + mut self, + stack_trace: impl Into, + ) -> Self { + self.stack_trace = Some(stack_trace.into()); + self + } + pub fn build(self) -> Result { + Ok(GetStackTraceReturns { + stack_trace: self.stack_trace.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(stack_trace)) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetStackTraceParams { + type Response = GetStackTraceReturns; + } + #[doc = "Stops on the next JavaScript statement.\n[pause](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-pause)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct PauseParams {} + impl PauseParams { + pub const IDENTIFIER: &'static str = "Debugger.pause"; + } + impl chromiumoxide_types::Method for PauseParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for PauseParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Stops on the next JavaScript statement.\n[pause](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-pause)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct PauseReturns {} + impl chromiumoxide_types::Command for PauseParams { + type Response = PauseReturns; + } + #[doc = "Removes JavaScript breakpoint.\n[removeBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-removeBreakpoint)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct RemoveBreakpointParams { + #[serde(rename = "breakpointId")] + pub breakpoint_id: BreakpointId, + } + impl RemoveBreakpointParams { + pub fn new(breakpoint_id: impl Into) -> Self { + Self { + breakpoint_id: breakpoint_id.into(), + } + } + } + impl RemoveBreakpointParams { + pub fn builder() -> RemoveBreakpointParamsBuilder { + RemoveBreakpointParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct RemoveBreakpointParamsBuilder { + breakpoint_id: Option, + } + impl RemoveBreakpointParamsBuilder { + pub fn breakpoint_id(mut self, breakpoint_id: impl Into) -> Self { + self.breakpoint_id = Some(breakpoint_id.into()); + self + } + pub fn build(self) -> Result { + Ok(RemoveBreakpointParams { + breakpoint_id: self.breakpoint_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(breakpoint_id)) + })?, + }) + } + } + impl RemoveBreakpointParams { + pub const IDENTIFIER: &'static str = "Debugger.removeBreakpoint"; + } + impl chromiumoxide_types::Method for RemoveBreakpointParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for RemoveBreakpointParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Removes JavaScript breakpoint.\n[removeBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-removeBreakpoint)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct RemoveBreakpointReturns {} + impl chromiumoxide_types::Command for RemoveBreakpointParams { + type Response = RemoveBreakpointReturns; + } + #[doc = "Restarts particular call frame from the beginning. The old, deprecated\nbehavior of `restartFrame` is to stay paused and allow further CDP commands\nafter a restart was scheduled. This can cause problems with restarting, so\nwe now continue execution immediatly after it has been scheduled until we\nreach the beginning of the restarted frame.\n\nTo stay back-wards compatible, `restartFrame` now expects a `mode`\nparameter to be present. If the `mode` parameter is missing, `restartFrame`\nerrors out.\n\nThe various return values are deprecated and `callFrames` is always empty.\nUse the call frames from the `Debugger#paused` events instead, that fires\nonce V8 pauses at the beginning of the restarted function.\n[restartFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-restartFrame)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct RestartFrameParams { + #[doc = "Call frame identifier to evaluate on."] + #[serde(rename = "callFrameId")] + pub call_frame_id: CallFrameId, + #[doc = "The `mode` parameter must be present and set to 'StepInto', otherwise\n`restartFrame` will error out."] + #[serde(rename = "mode")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub mode: Option, + } + #[doc = "The `mode` parameter must be present and set to 'StepInto', otherwise\n`restartFrame` will error out."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum RestartFrameMode { + #[doc = "Pause at the beginning of the restarted function"] + #[serde(rename = "StepInto")] + StepInto, + } + impl AsRef for RestartFrameMode { + fn as_ref(&self) -> &str { + match self { + RestartFrameMode::StepInto => "StepInto", + } + } + } + impl ::std::str::FromStr for RestartFrameMode { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "StepInto" | "stepinto" => Ok(RestartFrameMode::StepInto), + _ => Err(s.to_string()), + } + } + } + impl RestartFrameParams { + pub fn new(call_frame_id: impl Into) -> Self { + Self { + call_frame_id: call_frame_id.into(), + mode: None, + } + } + } + impl RestartFrameParams { + pub fn builder() -> RestartFrameParamsBuilder { + RestartFrameParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct RestartFrameParamsBuilder { + call_frame_id: Option, + mode: Option, + } + impl RestartFrameParamsBuilder { + pub fn call_frame_id(mut self, call_frame_id: impl Into) -> Self { + self.call_frame_id = Some(call_frame_id.into()); + self + } + pub fn mode(mut self, mode: impl Into) -> Self { + self.mode = Some(mode.into()); + self + } + pub fn build(self) -> Result { + Ok(RestartFrameParams { + call_frame_id: self.call_frame_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frame_id)) + })?, + mode: self.mode, + }) + } + } + impl RestartFrameParams { + pub const IDENTIFIER: &'static str = "Debugger.restartFrame"; + } + impl chromiumoxide_types::Method for RestartFrameParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for RestartFrameParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Restarts particular call frame from the beginning. The old, deprecated\nbehavior of `restartFrame` is to stay paused and allow further CDP commands\nafter a restart was scheduled. This can cause problems with restarting, so\nwe now continue execution immediatly after it has been scheduled until we\nreach the beginning of the restarted frame.\n\nTo stay back-wards compatible, `restartFrame` now expects a `mode`\nparameter to be present. If the `mode` parameter is missing, `restartFrame`\nerrors out.\n\nThe various return values are deprecated and `callFrames` is always empty.\nUse the call frames from the `Debugger#paused` events instead, that fires\nonce V8 pauses at the beginning of the restarted function.\n[restartFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-restartFrame)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct RestartFrameReturns {} + impl chromiumoxide_types::Command for RestartFrameParams { + type Response = RestartFrameReturns; + } + #[doc = "Resumes JavaScript execution.\n[resume](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-resume)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct ResumeParams { + #[doc = "Set to true to terminate execution upon resuming execution. In contrast\nto Runtime.terminateExecution, this will allows to execute further\nJavaScript (i.e. via evaluation) until execution of the paused code\nis actually resumed, at which point termination is triggered.\nIf execution is currently not paused, this parameter has no effect."] + #[serde(rename = "terminateOnResume")] + #[serde(skip_serializing_if = "Option::is_none")] + pub terminate_on_resume: Option, + } + impl ResumeParams { + pub fn builder() -> ResumeParamsBuilder { + ResumeParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ResumeParamsBuilder { + terminate_on_resume: Option, + } + impl ResumeParamsBuilder { + pub fn terminate_on_resume(mut self, terminate_on_resume: impl Into) -> Self { + self.terminate_on_resume = Some(terminate_on_resume.into()); + self + } + pub fn build(self) -> ResumeParams { + ResumeParams { + terminate_on_resume: self.terminate_on_resume, + } + } + } + impl ResumeParams { + pub const IDENTIFIER: &'static str = "Debugger.resume"; + } + impl chromiumoxide_types::Method for ResumeParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for ResumeParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Resumes JavaScript execution.\n[resume](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-resume)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct ResumeReturns {} + impl chromiumoxide_types::Command for ResumeParams { + type Response = ResumeReturns; + } + #[doc = "Searches for given string in script content.\n[searchInContent](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-searchInContent)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SearchInContentParams { + #[doc = "Id of the script to search in."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "String to search for."] + #[serde(rename = "query")] + pub query: String, + #[doc = "If true, search is case sensitive."] + #[serde(rename = "caseSensitive")] + #[serde(skip_serializing_if = "Option::is_none")] + pub case_sensitive: Option, + #[doc = "If true, treats string parameter as regex."] + #[serde(rename = "isRegex")] + #[serde(skip_serializing_if = "Option::is_none")] + pub is_regex: Option, + } + impl SearchInContentParams { + pub fn new( + script_id: impl Into, + query: impl Into, + ) -> Self { + Self { + script_id: script_id.into(), + query: query.into(), + case_sensitive: None, + is_regex: None, + } + } + } + impl SearchInContentParams { + pub fn builder() -> SearchInContentParamsBuilder { + SearchInContentParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SearchInContentParamsBuilder { + script_id: Option, + query: Option, + case_sensitive: Option, + is_regex: Option, + } + impl SearchInContentParamsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn query(mut self, query: impl Into) -> Self { + self.query = Some(query.into()); + self + } + pub fn case_sensitive(mut self, case_sensitive: impl Into) -> Self { + self.case_sensitive = Some(case_sensitive.into()); + self + } + pub fn is_regex(mut self, is_regex: impl Into) -> Self { + self.is_regex = Some(is_regex.into()); + self + } + pub fn build(self) -> Result { + Ok(SearchInContentParams { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + query: self.query.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(query)) + })?, + case_sensitive: self.case_sensitive, + is_regex: self.is_regex, + }) + } + } + impl SearchInContentParams { + pub const IDENTIFIER: &'static str = "Debugger.searchInContent"; + } + impl chromiumoxide_types::Method for SearchInContentParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SearchInContentParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Searches for given string in script content.\n[searchInContent](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-searchInContent)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SearchInContentReturns { + #[doc = "List of search matches."] + #[serde(rename = "result")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub result: Vec, + } + impl SearchInContentReturns { + pub fn new(result: Vec) -> Self { + Self { result } + } + } + impl SearchInContentReturns { + pub fn builder() -> SearchInContentReturnsBuilder { + SearchInContentReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SearchInContentReturnsBuilder { + result: Option>, + } + impl SearchInContentReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + let v = self.result.get_or_insert(Vec::new()); + v.push(result.into()); + self + } + pub fn results(mut self, results: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.result.get_or_insert(Vec::new()); + for val in results { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(SearchInContentReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + }) + } + } + impl chromiumoxide_types::Command for SearchInContentParams { + type Response = SearchInContentReturns; + } + #[doc = "Enables or disables async call stacks tracking.\n[setAsyncCallStackDepth](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setAsyncCallStackDepth)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetAsyncCallStackDepthParams { + #[doc = "Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async\ncall stacks (default)."] + #[serde(rename = "maxDepth")] + pub max_depth: i64, + } + impl SetAsyncCallStackDepthParams { + pub fn new(max_depth: impl Into) -> Self { + Self { + max_depth: max_depth.into(), + } + } + } + impl SetAsyncCallStackDepthParams { + pub fn builder() -> SetAsyncCallStackDepthParamsBuilder { + SetAsyncCallStackDepthParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetAsyncCallStackDepthParamsBuilder { + max_depth: Option, + } + impl SetAsyncCallStackDepthParamsBuilder { + pub fn max_depth(mut self, max_depth: impl Into) -> Self { + self.max_depth = Some(max_depth.into()); + self + } + pub fn build(self) -> Result { + Ok(SetAsyncCallStackDepthParams { + max_depth: self.max_depth.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(max_depth)) + })?, + }) + } + } + impl SetAsyncCallStackDepthParams { + pub const IDENTIFIER: &'static str = "Debugger.setAsyncCallStackDepth"; + } + impl chromiumoxide_types::Method for SetAsyncCallStackDepthParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetAsyncCallStackDepthParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Enables or disables async call stacks tracking.\n[setAsyncCallStackDepth](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setAsyncCallStackDepth)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetAsyncCallStackDepthReturns {} + impl chromiumoxide_types::Command for SetAsyncCallStackDepthParams { + type Response = SetAsyncCallStackDepthReturns; + } + #[doc = "Replace previous blackbox execution contexts with passed ones. Forces backend to skip\nstepping/pausing in scripts in these execution contexts. VM will try to leave blackboxed script by\nperforming 'step in' several times, finally resorting to 'step out' if unsuccessful.\n[setBlackboxExecutionContexts](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBlackboxExecutionContexts)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBlackboxExecutionContextsParams { + #[doc = "Array of execution context unique ids for the debugger to ignore."] + #[serde(rename = "uniqueIds")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub unique_ids: Vec, + } + impl SetBlackboxExecutionContextsParams { + pub fn new(unique_ids: Vec) -> Self { + Self { unique_ids } + } + } + impl SetBlackboxExecutionContextsParams { + pub fn builder() -> SetBlackboxExecutionContextsParamsBuilder { + SetBlackboxExecutionContextsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBlackboxExecutionContextsParamsBuilder { + unique_ids: Option>, + } + impl SetBlackboxExecutionContextsParamsBuilder { + pub fn unique_id(mut self, unique_id: impl Into) -> Self { + let v = self.unique_ids.get_or_insert(Vec::new()); + v.push(unique_id.into()); + self + } + pub fn unique_ids(mut self, unique_ids: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.unique_ids.get_or_insert(Vec::new()); + for val in unique_ids { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(SetBlackboxExecutionContextsParams { + unique_ids: self.unique_ids.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(unique_ids)) + })?, + }) + } + } + impl SetBlackboxExecutionContextsParams { + pub const IDENTIFIER: &'static str = "Debugger.setBlackboxExecutionContexts"; + } + impl chromiumoxide_types::Method for SetBlackboxExecutionContextsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBlackboxExecutionContextsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Replace previous blackbox execution contexts with passed ones. Forces backend to skip\nstepping/pausing in scripts in these execution contexts. VM will try to leave blackboxed script by\nperforming 'step in' several times, finally resorting to 'step out' if unsuccessful.\n[setBlackboxExecutionContexts](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBlackboxExecutionContexts)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetBlackboxExecutionContextsReturns {} + impl chromiumoxide_types::Command for SetBlackboxExecutionContextsParams { + type Response = SetBlackboxExecutionContextsReturns; + } + #[doc = "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in\nscripts with url matching one of the patterns. VM will try to leave blackboxed script by\nperforming 'step in' several times, finally resorting to 'step out' if unsuccessful.\n[setBlackboxPatterns](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBlackboxPatterns)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBlackboxPatternsParams { + #[doc = "Array of regexps that will be used to check script url for blackbox state."] + #[serde(rename = "patterns")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub patterns: Vec, + #[doc = "If true, also ignore scripts with no source url."] + #[serde(rename = "skipAnonymous")] + #[serde(skip_serializing_if = "Option::is_none")] + pub skip_anonymous: Option, + } + impl SetBlackboxPatternsParams { + pub fn new(patterns: Vec) -> Self { + Self { + patterns, + skip_anonymous: None, + } + } + } + impl SetBlackboxPatternsParams { + pub fn builder() -> SetBlackboxPatternsParamsBuilder { + SetBlackboxPatternsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBlackboxPatternsParamsBuilder { + patterns: Option>, + skip_anonymous: Option, + } + impl SetBlackboxPatternsParamsBuilder { + pub fn pattern(mut self, pattern: impl Into) -> Self { + let v = self.patterns.get_or_insert(Vec::new()); + v.push(pattern.into()); + self + } + pub fn patterns(mut self, patterns: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.patterns.get_or_insert(Vec::new()); + for val in patterns { + v.push(val.into()); + } + self + } + pub fn skip_anonymous(mut self, skip_anonymous: impl Into) -> Self { + self.skip_anonymous = Some(skip_anonymous.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBlackboxPatternsParams { + patterns: self.patterns.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(patterns)) + })?, + skip_anonymous: self.skip_anonymous, + }) + } + } + impl SetBlackboxPatternsParams { + pub const IDENTIFIER: &'static str = "Debugger.setBlackboxPatterns"; + } + impl chromiumoxide_types::Method for SetBlackboxPatternsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBlackboxPatternsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in\nscripts with url matching one of the patterns. VM will try to leave blackboxed script by\nperforming 'step in' several times, finally resorting to 'step out' if unsuccessful.\n[setBlackboxPatterns](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBlackboxPatterns)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetBlackboxPatternsReturns {} + impl chromiumoxide_types::Command for SetBlackboxPatternsParams { + type Response = SetBlackboxPatternsReturns; + } + #[doc = "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted\nscripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful.\nPositions array contains positions where blackbox state is changed. First interval isn't\nblackboxed. Array should be sorted.\n[setBlackboxedRanges](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBlackboxedRanges)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBlackboxedRangesParams { + #[doc = "Id of the script."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[serde(rename = "positions")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub positions: Vec, + } + impl SetBlackboxedRangesParams { + pub fn new( + script_id: impl Into, + positions: Vec, + ) -> Self { + Self { + script_id: script_id.into(), + positions, + } + } + } + impl SetBlackboxedRangesParams { + pub fn builder() -> SetBlackboxedRangesParamsBuilder { + SetBlackboxedRangesParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBlackboxedRangesParamsBuilder { + script_id: Option, + positions: Option>, + } + impl SetBlackboxedRangesParamsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn position(mut self, position: impl Into) -> Self { + let v = self.positions.get_or_insert(Vec::new()); + v.push(position.into()); + self + } + pub fn positions(mut self, positions: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.positions.get_or_insert(Vec::new()); + for val in positions { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(SetBlackboxedRangesParams { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + positions: self.positions.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(positions)) + })?, + }) + } + } + impl SetBlackboxedRangesParams { + pub const IDENTIFIER: &'static str = "Debugger.setBlackboxedRanges"; + } + impl chromiumoxide_types::Method for SetBlackboxedRangesParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBlackboxedRangesParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted\nscripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful.\nPositions array contains positions where blackbox state is changed. First interval isn't\nblackboxed. Array should be sorted.\n[setBlackboxedRanges](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBlackboxedRanges)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetBlackboxedRangesReturns {} + impl chromiumoxide_types::Command for SetBlackboxedRangesParams { + type Response = SetBlackboxedRangesReturns; + } + #[doc = "Sets JavaScript breakpoint at a given location.\n[setBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpoint)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointParams { + #[doc = "Location to set breakpoint in."] + #[serde(rename = "location")] + pub location: Location, + #[doc = "Expression to use as a breakpoint condition. When specified, debugger will only stop on the\nbreakpoint if this expression evaluates to true."] + #[serde(rename = "condition")] + #[serde(skip_serializing_if = "Option::is_none")] + pub condition: Option, + } + impl SetBreakpointParams { + pub fn new(location: impl Into) -> Self { + Self { + location: location.into(), + condition: None, + } + } + } + impl SetBreakpointParams { + pub fn builder() -> SetBreakpointParamsBuilder { + SetBreakpointParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointParamsBuilder { + location: Option, + condition: Option, + } + impl SetBreakpointParamsBuilder { + pub fn location(mut self, location: impl Into) -> Self { + self.location = Some(location.into()); + self + } + pub fn condition(mut self, condition: impl Into) -> Self { + self.condition = Some(condition.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointParams { + location: self.location.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(location)) + })?, + condition: self.condition, + }) + } + } + impl SetBreakpointParams { + pub const IDENTIFIER: &'static str = "Debugger.setBreakpoint"; + } + impl chromiumoxide_types::Method for SetBreakpointParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBreakpointParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Sets JavaScript breakpoint at a given location.\n[setBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpoint)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointReturns { + #[doc = "Id of the created breakpoint for further reference."] + #[serde(rename = "breakpointId")] + pub breakpoint_id: BreakpointId, + #[doc = "Location this breakpoint resolved into."] + #[serde(rename = "actualLocation")] + pub actual_location: Location, + } + impl SetBreakpointReturns { + pub fn new( + breakpoint_id: impl Into, + actual_location: impl Into, + ) -> Self { + Self { + breakpoint_id: breakpoint_id.into(), + actual_location: actual_location.into(), + } + } + } + impl SetBreakpointReturns { + pub fn builder() -> SetBreakpointReturnsBuilder { + SetBreakpointReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointReturnsBuilder { + breakpoint_id: Option, + actual_location: Option, + } + impl SetBreakpointReturnsBuilder { + pub fn breakpoint_id(mut self, breakpoint_id: impl Into) -> Self { + self.breakpoint_id = Some(breakpoint_id.into()); + self + } + pub fn actual_location(mut self, actual_location: impl Into) -> Self { + self.actual_location = Some(actual_location.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointReturns { + breakpoint_id: self.breakpoint_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(breakpoint_id)) + })?, + actual_location: self.actual_location.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(actual_location)) + })?, + }) + } + } + impl chromiumoxide_types::Command for SetBreakpointParams { + type Response = SetBreakpointReturns; + } + #[doc = "Sets instrumentation breakpoint.\n[setInstrumentationBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setInstrumentationBreakpoint)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetInstrumentationBreakpointParams { + #[doc = "Instrumentation name."] + #[serde(rename = "instrumentation")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub instrumentation: SetInstrumentationBreakpointInstrumentation, + } + #[doc = "Instrumentation name."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum SetInstrumentationBreakpointInstrumentation { + #[serde(rename = "beforeScriptExecution")] + BeforeScriptExecution, + #[serde(rename = "beforeScriptWithSourceMapExecution")] + BeforeScriptWithSourceMapExecution, + } + impl AsRef for SetInstrumentationBreakpointInstrumentation { + fn as_ref(&self) -> &str { + match self { SetInstrumentationBreakpointInstrumentation :: BeforeScriptExecution => "beforeScriptExecution" , SetInstrumentationBreakpointInstrumentation :: BeforeScriptWithSourceMapExecution => "beforeScriptWithSourceMapExecution" } + } + } + impl ::std::str::FromStr for SetInstrumentationBreakpointInstrumentation { + type Err = String; + fn from_str(s: &str) -> Result { + match s { "beforeScriptExecution" | "BeforeScriptExecution" | "beforescriptexecution" => Ok (SetInstrumentationBreakpointInstrumentation :: BeforeScriptExecution) , "beforeScriptWithSourceMapExecution" | "BeforeScriptWithSourceMapExecution" | "beforescriptwithsourcemapexecution" => Ok (SetInstrumentationBreakpointInstrumentation :: BeforeScriptWithSourceMapExecution) , _ => Err (s . to_string ()) } + } + } + impl SetInstrumentationBreakpointParams { + pub fn new( + instrumentation: impl Into, + ) -> Self { + Self { + instrumentation: instrumentation.into(), + } + } + } + impl SetInstrumentationBreakpointParams { + pub fn builder() -> SetInstrumentationBreakpointParamsBuilder { + SetInstrumentationBreakpointParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetInstrumentationBreakpointParamsBuilder { + instrumentation: Option, + } + impl SetInstrumentationBreakpointParamsBuilder { + pub fn instrumentation( + mut self, + instrumentation: impl Into, + ) -> Self { + self.instrumentation = Some(instrumentation.into()); + self + } + pub fn build(self) -> Result { + Ok(SetInstrumentationBreakpointParams { + instrumentation: self.instrumentation.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(instrumentation)) + })?, + }) + } + } + impl SetInstrumentationBreakpointParams { + pub const IDENTIFIER: &'static str = "Debugger.setInstrumentationBreakpoint"; + } + impl chromiumoxide_types::Method for SetInstrumentationBreakpointParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetInstrumentationBreakpointParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Sets instrumentation breakpoint.\n[setInstrumentationBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setInstrumentationBreakpoint)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetInstrumentationBreakpointReturns { + #[doc = "Id of the created breakpoint for further reference."] + #[serde(rename = "breakpointId")] + pub breakpoint_id: BreakpointId, + } + impl SetInstrumentationBreakpointReturns { + pub fn new(breakpoint_id: impl Into) -> Self { + Self { + breakpoint_id: breakpoint_id.into(), + } + } + } + impl SetInstrumentationBreakpointReturns { + pub fn builder() -> SetInstrumentationBreakpointReturnsBuilder { + SetInstrumentationBreakpointReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetInstrumentationBreakpointReturnsBuilder { + breakpoint_id: Option, + } + impl SetInstrumentationBreakpointReturnsBuilder { + pub fn breakpoint_id(mut self, breakpoint_id: impl Into) -> Self { + self.breakpoint_id = Some(breakpoint_id.into()); + self + } + pub fn build(self) -> Result { + Ok(SetInstrumentationBreakpointReturns { + breakpoint_id: self.breakpoint_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(breakpoint_id)) + })?, + }) + } + } + impl chromiumoxide_types::Command for SetInstrumentationBreakpointParams { + type Response = SetInstrumentationBreakpointReturns; + } + #[doc = "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this\ncommand is issued, all existing parsed scripts will have breakpoints resolved and returned in\n`locations` property. Further matching script parsing will result in subsequent\n`breakpointResolved` events issued. This logical breakpoint will survive page reloads.\n[setBreakpointByUrl](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointByUrl)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointByUrlParams { + #[doc = "Line number to set breakpoint at."] + #[serde(rename = "lineNumber")] + pub line_number: i64, + #[doc = "URL of the resources to set breakpoint on."] + #[serde(rename = "url")] + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + #[doc = "Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or\n`urlRegex` must be specified."] + #[serde(rename = "urlRegex")] + #[serde(skip_serializing_if = "Option::is_none")] + pub url_regex: Option, + #[doc = "Script hash of the resources to set breakpoint on."] + #[serde(rename = "scriptHash")] + #[serde(skip_serializing_if = "Option::is_none")] + pub script_hash: Option, + #[doc = "Offset in the line to set breakpoint at."] + #[serde(rename = "columnNumber")] + #[serde(skip_serializing_if = "Option::is_none")] + pub column_number: Option, + #[doc = "Expression to use as a breakpoint condition. When specified, debugger will only stop on the\nbreakpoint if this expression evaluates to true."] + #[serde(rename = "condition")] + #[serde(skip_serializing_if = "Option::is_none")] + pub condition: Option, + } + impl SetBreakpointByUrlParams { + pub fn new(line_number: impl Into) -> Self { + Self { + line_number: line_number.into(), + url: None, + url_regex: None, + script_hash: None, + column_number: None, + condition: None, + } + } + } + impl SetBreakpointByUrlParams { + pub fn builder() -> SetBreakpointByUrlParamsBuilder { + SetBreakpointByUrlParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointByUrlParamsBuilder { + line_number: Option, + url: Option, + url_regex: Option, + script_hash: Option, + column_number: Option, + condition: Option, + } + impl SetBreakpointByUrlParamsBuilder { + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn url(mut self, url: impl Into) -> Self { + self.url = Some(url.into()); + self + } + pub fn url_regex(mut self, url_regex: impl Into) -> Self { + self.url_regex = Some(url_regex.into()); + self + } + pub fn script_hash(mut self, script_hash: impl Into) -> Self { + self.script_hash = Some(script_hash.into()); + self + } + pub fn column_number(mut self, column_number: impl Into) -> Self { + self.column_number = Some(column_number.into()); + self + } + pub fn condition(mut self, condition: impl Into) -> Self { + self.condition = Some(condition.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointByUrlParams { + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + url: self.url, + url_regex: self.url_regex, + script_hash: self.script_hash, + column_number: self.column_number, + condition: self.condition, + }) + } + } + impl SetBreakpointByUrlParams { + pub const IDENTIFIER: &'static str = "Debugger.setBreakpointByUrl"; + } + impl chromiumoxide_types::Method for SetBreakpointByUrlParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBreakpointByUrlParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this\ncommand is issued, all existing parsed scripts will have breakpoints resolved and returned in\n`locations` property. Further matching script parsing will result in subsequent\n`breakpointResolved` events issued. This logical breakpoint will survive page reloads.\n[setBreakpointByUrl](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointByUrl)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointByUrlReturns { + #[doc = "Id of the created breakpoint for further reference."] + #[serde(rename = "breakpointId")] + pub breakpoint_id: BreakpointId, + #[doc = "List of the locations this breakpoint resolved into upon addition."] + #[serde(rename = "locations")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub locations: Vec, + } + impl SetBreakpointByUrlReturns { + pub fn new(breakpoint_id: impl Into, locations: Vec) -> Self { + Self { + breakpoint_id: breakpoint_id.into(), + locations, + } + } + } + impl SetBreakpointByUrlReturns { + pub fn builder() -> SetBreakpointByUrlReturnsBuilder { + SetBreakpointByUrlReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointByUrlReturnsBuilder { + breakpoint_id: Option, + locations: Option>, + } + impl SetBreakpointByUrlReturnsBuilder { + pub fn breakpoint_id(mut self, breakpoint_id: impl Into) -> Self { + self.breakpoint_id = Some(breakpoint_id.into()); + self + } + pub fn location(mut self, location: impl Into) -> Self { + let v = self.locations.get_or_insert(Vec::new()); + v.push(location.into()); + self + } + pub fn locations(mut self, locations: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.locations.get_or_insert(Vec::new()); + for val in locations { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointByUrlReturns { + breakpoint_id: self.breakpoint_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(breakpoint_id)) + })?, + locations: self.locations.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(locations)) + })?, + }) + } + } + impl chromiumoxide_types::Command for SetBreakpointByUrlParams { + type Response = SetBreakpointByUrlReturns; + } + #[doc = "Sets JavaScript breakpoint before each call to the given function.\nIf another function was created from the same source as a given one,\ncalling it will also trigger the breakpoint.\n[setBreakpointOnFunctionCall](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointOnFunctionCall)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointOnFunctionCallParams { + #[doc = "Function object id."] + #[serde(rename = "objectId")] + pub object_id: super::runtime::RemoteObjectId, + #[doc = "Expression to use as a breakpoint condition. When specified, debugger will\nstop on the breakpoint if this expression evaluates to true."] + #[serde(rename = "condition")] + #[serde(skip_serializing_if = "Option::is_none")] + pub condition: Option, + } + impl SetBreakpointOnFunctionCallParams { + pub fn new(object_id: impl Into) -> Self { + Self { + object_id: object_id.into(), + condition: None, + } + } + } + impl SetBreakpointOnFunctionCallParams { + pub fn builder() -> SetBreakpointOnFunctionCallParamsBuilder { + SetBreakpointOnFunctionCallParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointOnFunctionCallParamsBuilder { + object_id: Option, + condition: Option, + } + impl SetBreakpointOnFunctionCallParamsBuilder { + pub fn object_id( + mut self, + object_id: impl Into, + ) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn condition(mut self, condition: impl Into) -> Self { + self.condition = Some(condition.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointOnFunctionCallParams { + object_id: self.object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object_id)) + })?, + condition: self.condition, + }) + } + } + impl SetBreakpointOnFunctionCallParams { + pub const IDENTIFIER: &'static str = "Debugger.setBreakpointOnFunctionCall"; + } + impl chromiumoxide_types::Method for SetBreakpointOnFunctionCallParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBreakpointOnFunctionCallParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Sets JavaScript breakpoint before each call to the given function.\nIf another function was created from the same source as a given one,\ncalling it will also trigger the breakpoint.\n[setBreakpointOnFunctionCall](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointOnFunctionCall)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointOnFunctionCallReturns { + #[doc = "Id of the created breakpoint for further reference."] + #[serde(rename = "breakpointId")] + pub breakpoint_id: BreakpointId, + } + impl SetBreakpointOnFunctionCallReturns { + pub fn new(breakpoint_id: impl Into) -> Self { + Self { + breakpoint_id: breakpoint_id.into(), + } + } + } + impl SetBreakpointOnFunctionCallReturns { + pub fn builder() -> SetBreakpointOnFunctionCallReturnsBuilder { + SetBreakpointOnFunctionCallReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointOnFunctionCallReturnsBuilder { + breakpoint_id: Option, + } + impl SetBreakpointOnFunctionCallReturnsBuilder { + pub fn breakpoint_id(mut self, breakpoint_id: impl Into) -> Self { + self.breakpoint_id = Some(breakpoint_id.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointOnFunctionCallReturns { + breakpoint_id: self.breakpoint_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(breakpoint_id)) + })?, + }) + } + } + impl chromiumoxide_types::Command for SetBreakpointOnFunctionCallParams { + type Response = SetBreakpointOnFunctionCallReturns; + } + #[doc = "Activates / deactivates all breakpoints on the page.\n[setBreakpointsActive](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointsActive)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetBreakpointsActiveParams { + #[doc = "New value for breakpoints active state."] + #[serde(rename = "active")] + pub active: bool, + } + impl SetBreakpointsActiveParams { + pub fn new(active: impl Into) -> Self { + Self { + active: active.into(), + } + } + } + impl SetBreakpointsActiveParams { + pub fn builder() -> SetBreakpointsActiveParamsBuilder { + SetBreakpointsActiveParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetBreakpointsActiveParamsBuilder { + active: Option, + } + impl SetBreakpointsActiveParamsBuilder { + pub fn active(mut self, active: impl Into) -> Self { + self.active = Some(active.into()); + self + } + pub fn build(self) -> Result { + Ok(SetBreakpointsActiveParams { + active: self.active.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(active)) + })?, + }) + } + } + impl SetBreakpointsActiveParams { + pub const IDENTIFIER: &'static str = "Debugger.setBreakpointsActive"; + } + impl chromiumoxide_types::Method for SetBreakpointsActiveParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetBreakpointsActiveParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Activates / deactivates all breakpoints on the page.\n[setBreakpointsActive](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointsActive)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetBreakpointsActiveReturns {} + impl chromiumoxide_types::Command for SetBreakpointsActiveParams { + type Response = SetBreakpointsActiveReturns; + } + #[doc = "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions,\nor caught exceptions, no exceptions. Initial pause on exceptions state is `none`.\n[setPauseOnExceptions](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setPauseOnExceptions)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetPauseOnExceptionsParams { + #[doc = "Pause on exceptions mode."] + #[serde(rename = "state")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub state: SetPauseOnExceptionsState, + } + #[doc = "Pause on exceptions mode."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum SetPauseOnExceptionsState { + #[serde(rename = "none")] + None, + #[serde(rename = "caught")] + Caught, + #[serde(rename = "uncaught")] + Uncaught, + #[serde(rename = "all")] + All, + } + impl AsRef for SetPauseOnExceptionsState { + fn as_ref(&self) -> &str { + match self { + SetPauseOnExceptionsState::None => "none", + SetPauseOnExceptionsState::Caught => "caught", + SetPauseOnExceptionsState::Uncaught => "uncaught", + SetPauseOnExceptionsState::All => "all", + } + } + } + impl ::std::str::FromStr for SetPauseOnExceptionsState { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "none" | "None" => Ok(SetPauseOnExceptionsState::None), + "caught" | "Caught" => Ok(SetPauseOnExceptionsState::Caught), + "uncaught" | "Uncaught" => Ok(SetPauseOnExceptionsState::Uncaught), + "all" | "All" => Ok(SetPauseOnExceptionsState::All), + _ => Err(s.to_string()), + } + } + } + impl SetPauseOnExceptionsParams { + pub fn new(state: impl Into) -> Self { + Self { + state: state.into(), + } + } + } + impl SetPauseOnExceptionsParams { + pub fn builder() -> SetPauseOnExceptionsParamsBuilder { + SetPauseOnExceptionsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetPauseOnExceptionsParamsBuilder { + state: Option, + } + impl SetPauseOnExceptionsParamsBuilder { + pub fn state(mut self, state: impl Into) -> Self { + self.state = Some(state.into()); + self + } + pub fn build(self) -> Result { + Ok(SetPauseOnExceptionsParams { + state: self.state.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(state)) + })?, + }) + } + } + impl SetPauseOnExceptionsParams { + pub const IDENTIFIER: &'static str = "Debugger.setPauseOnExceptions"; + } + impl chromiumoxide_types::Method for SetPauseOnExceptionsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetPauseOnExceptionsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions,\nor caught exceptions, no exceptions. Initial pause on exceptions state is `none`.\n[setPauseOnExceptions](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setPauseOnExceptions)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetPauseOnExceptionsReturns {} + impl chromiumoxide_types::Command for SetPauseOnExceptionsParams { + type Response = SetPauseOnExceptionsReturns; + } + #[doc = "Changes return value in top frame. Available only at return break position.\n[setReturnValue](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setReturnValue)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetReturnValueParams { + #[doc = "New return value."] + #[serde(rename = "newValue")] + pub new_value: super::runtime::CallArgument, + } + impl SetReturnValueParams { + pub fn new(new_value: impl Into) -> Self { + Self { + new_value: new_value.into(), + } + } + } + impl SetReturnValueParams { + pub fn builder() -> SetReturnValueParamsBuilder { + SetReturnValueParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetReturnValueParamsBuilder { + new_value: Option, + } + impl SetReturnValueParamsBuilder { + pub fn new_value(mut self, new_value: impl Into) -> Self { + self.new_value = Some(new_value.into()); + self + } + pub fn build(self) -> Result { + Ok(SetReturnValueParams { + new_value: self.new_value.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(new_value)) + })?, + }) + } + } + impl SetReturnValueParams { + pub const IDENTIFIER: &'static str = "Debugger.setReturnValue"; + } + impl chromiumoxide_types::Method for SetReturnValueParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetReturnValueParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Changes return value in top frame. Available only at return break position.\n[setReturnValue](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setReturnValue)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetReturnValueReturns {} + impl chromiumoxide_types::Command for SetReturnValueParams { + type Response = SetReturnValueReturns; + } + #[doc = "Edits JavaScript source live.\n\nIn general, functions that are currently on the stack can not be edited with\na single exception: If the edited function is the top-most stack frame and\nthat is the only activation of that function on the stack. In this case\nthe live edit will be successful and a `Debugger.restartFrame` for the\ntop-most function is automatically triggered.\n[setScriptSource](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setScriptSource)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetScriptSourceParams { + #[doc = "Id of the script to edit."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "New content of the script."] + #[serde(rename = "scriptSource")] + pub script_source: String, + #[doc = "If true the change will not actually be applied. Dry run may be used to get result\ndescription without actually modifying the code."] + #[serde(rename = "dryRun")] + #[serde(skip_serializing_if = "Option::is_none")] + pub dry_run: Option, + #[doc = "If true, then `scriptSource` is allowed to change the function on top of the stack\nas long as the top-most stack frame is the only activation of that function."] + #[serde(rename = "allowTopFrameEditing")] + #[serde(skip_serializing_if = "Option::is_none")] + pub allow_top_frame_editing: Option, + } + impl SetScriptSourceParams { + pub fn new( + script_id: impl Into, + script_source: impl Into, + ) -> Self { + Self { + script_id: script_id.into(), + script_source: script_source.into(), + dry_run: None, + allow_top_frame_editing: None, + } + } + } + impl SetScriptSourceParams { + pub fn builder() -> SetScriptSourceParamsBuilder { + SetScriptSourceParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetScriptSourceParamsBuilder { + script_id: Option, + script_source: Option, + dry_run: Option, + allow_top_frame_editing: Option, + } + impl SetScriptSourceParamsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn script_source(mut self, script_source: impl Into) -> Self { + self.script_source = Some(script_source.into()); + self + } + pub fn dry_run(mut self, dry_run: impl Into) -> Self { + self.dry_run = Some(dry_run.into()); + self + } + pub fn allow_top_frame_editing( + mut self, + allow_top_frame_editing: impl Into, + ) -> Self { + self.allow_top_frame_editing = Some(allow_top_frame_editing.into()); + self + } + pub fn build(self) -> Result { + Ok(SetScriptSourceParams { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + script_source: self.script_source.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_source)) + })?, + dry_run: self.dry_run, + allow_top_frame_editing: self.allow_top_frame_editing, + }) + } + } + impl SetScriptSourceParams { + pub const IDENTIFIER: &'static str = "Debugger.setScriptSource"; + } + impl chromiumoxide_types::Method for SetScriptSourceParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetScriptSourceParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Edits JavaScript source live.\n\nIn general, functions that are currently on the stack can not be edited with\na single exception: If the edited function is the top-most stack frame and\nthat is the only activation of that function on the stack. In this case\nthe live edit will be successful and a `Debugger.restartFrame` for the\ntop-most function is automatically triggered.\n[setScriptSource](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setScriptSource)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetScriptSourceReturns { + #[doc = "Whether the operation was successful or not. Only `Ok` denotes a\nsuccessful live edit while the other enum variants denote why\nthe live edit failed."] + #[serde(rename = "status")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub status: SetScriptSourceStatus, + #[doc = "Exception details if any. Only present when `status` is `CompileError`."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + #[doc = "Whether the operation was successful or not. Only `Ok` denotes a\nsuccessful live edit while the other enum variants denote why\nthe live edit failed."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum SetScriptSourceStatus { + #[serde(rename = "Ok")] + Ok, + #[serde(rename = "CompileError")] + CompileError, + #[serde(rename = "BlockedByActiveGenerator")] + BlockedByActiveGenerator, + #[serde(rename = "BlockedByActiveFunction")] + BlockedByActiveFunction, + #[serde(rename = "BlockedByTopLevelEsModuleChange")] + BlockedByTopLevelEsModuleChange, + } + impl AsRef for SetScriptSourceStatus { + fn as_ref(&self) -> &str { + match self { + SetScriptSourceStatus::Ok => "Ok", + SetScriptSourceStatus::CompileError => "CompileError", + SetScriptSourceStatus::BlockedByActiveGenerator => "BlockedByActiveGenerator", + SetScriptSourceStatus::BlockedByActiveFunction => "BlockedByActiveFunction", + SetScriptSourceStatus::BlockedByTopLevelEsModuleChange => { + "BlockedByTopLevelEsModuleChange" + } + } + } + } + impl ::std::str::FromStr for SetScriptSourceStatus { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "Ok" | "ok" => Ok(SetScriptSourceStatus::Ok), + "CompileError" | "compileerror" => Ok(SetScriptSourceStatus::CompileError), + "BlockedByActiveGenerator" | "blockedbyactivegenerator" => { + Ok(SetScriptSourceStatus::BlockedByActiveGenerator) + } + "BlockedByActiveFunction" | "blockedbyactivefunction" => { + Ok(SetScriptSourceStatus::BlockedByActiveFunction) + } + "BlockedByTopLevelEsModuleChange" | "blockedbytoplevelesmodulechange" => { + Ok(SetScriptSourceStatus::BlockedByTopLevelEsModuleChange) + } + _ => Err(s.to_string()), + } + } + } + impl SetScriptSourceReturns { + pub fn new(status: impl Into) -> Self { + Self { + status: status.into(), + exception_details: None, + } + } + } + impl SetScriptSourceReturns { + pub fn builder() -> SetScriptSourceReturnsBuilder { + SetScriptSourceReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetScriptSourceReturnsBuilder { + status: Option, + exception_details: Option, + } + impl SetScriptSourceReturnsBuilder { + pub fn status(mut self, status: impl Into) -> Self { + self.status = Some(status.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> Result { + Ok(SetScriptSourceReturns { + status: self.status.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(status)) + })?, + exception_details: self.exception_details, + }) + } + } + impl chromiumoxide_types::Command for SetScriptSourceParams { + type Response = SetScriptSourceReturns; + } + #[doc = "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc).\n[setSkipAllPauses](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setSkipAllPauses)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetSkipAllPausesParams { + #[doc = "New value for skip pauses state."] + #[serde(rename = "skip")] + pub skip: bool, + } + impl SetSkipAllPausesParams { + pub fn new(skip: impl Into) -> Self { + Self { skip: skip.into() } + } + } + impl SetSkipAllPausesParams { + pub fn builder() -> SetSkipAllPausesParamsBuilder { + SetSkipAllPausesParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetSkipAllPausesParamsBuilder { + skip: Option, + } + impl SetSkipAllPausesParamsBuilder { + pub fn skip(mut self, skip: impl Into) -> Self { + self.skip = Some(skip.into()); + self + } + pub fn build(self) -> Result { + Ok(SetSkipAllPausesParams { + skip: self.skip.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(skip)) + })?, + }) + } + } + impl SetSkipAllPausesParams { + pub const IDENTIFIER: &'static str = "Debugger.setSkipAllPauses"; + } + impl chromiumoxide_types::Method for SetSkipAllPausesParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetSkipAllPausesParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc).\n[setSkipAllPauses](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setSkipAllPauses)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetSkipAllPausesReturns {} + impl chromiumoxide_types::Command for SetSkipAllPausesParams { + type Response = SetSkipAllPausesReturns; + } + #[doc = "Changes value of variable in a callframe. Object-based scopes are not supported and must be\nmutated manually.\n[setVariableValue](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setVariableValue)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetVariableValueParams { + #[doc = "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch'\nscope types are allowed. Other scopes could be manipulated manually."] + #[serde(rename = "scopeNumber")] + pub scope_number: i64, + #[doc = "Variable name."] + #[serde(rename = "variableName")] + pub variable_name: String, + #[doc = "New variable value."] + #[serde(rename = "newValue")] + pub new_value: super::runtime::CallArgument, + #[doc = "Id of callframe that holds variable."] + #[serde(rename = "callFrameId")] + pub call_frame_id: CallFrameId, + } + impl SetVariableValueParams { + pub fn new( + scope_number: impl Into, + variable_name: impl Into, + new_value: impl Into, + call_frame_id: impl Into, + ) -> Self { + Self { + scope_number: scope_number.into(), + variable_name: variable_name.into(), + new_value: new_value.into(), + call_frame_id: call_frame_id.into(), + } + } + } + impl SetVariableValueParams { + pub fn builder() -> SetVariableValueParamsBuilder { + SetVariableValueParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetVariableValueParamsBuilder { + scope_number: Option, + variable_name: Option, + new_value: Option, + call_frame_id: Option, + } + impl SetVariableValueParamsBuilder { + pub fn scope_number(mut self, scope_number: impl Into) -> Self { + self.scope_number = Some(scope_number.into()); + self + } + pub fn variable_name(mut self, variable_name: impl Into) -> Self { + self.variable_name = Some(variable_name.into()); + self + } + pub fn new_value(mut self, new_value: impl Into) -> Self { + self.new_value = Some(new_value.into()); + self + } + pub fn call_frame_id(mut self, call_frame_id: impl Into) -> Self { + self.call_frame_id = Some(call_frame_id.into()); + self + } + pub fn build(self) -> Result { + Ok(SetVariableValueParams { + scope_number: self.scope_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(scope_number)) + })?, + variable_name: self.variable_name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(variable_name)) + })?, + new_value: self.new_value.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(new_value)) + })?, + call_frame_id: self.call_frame_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frame_id)) + })?, + }) + } + } + impl SetVariableValueParams { + pub const IDENTIFIER: &'static str = "Debugger.setVariableValue"; + } + impl chromiumoxide_types::Method for SetVariableValueParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetVariableValueParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Changes value of variable in a callframe. Object-based scopes are not supported and must be\nmutated manually.\n[setVariableValue](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setVariableValue)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetVariableValueReturns {} + impl chromiumoxide_types::Command for SetVariableValueParams { + type Response = SetVariableValueReturns; + } + #[doc = "Steps into the function call.\n[stepInto](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepInto)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StepIntoParams { + #[doc = "Debugger will pause on the execution of the first async task which was scheduled\nbefore next pause."] + #[serde(rename = "breakOnAsyncCall")] + #[serde(skip_serializing_if = "Option::is_none")] + pub break_on_async_call: Option, + #[doc = "The skipList specifies location ranges that should be skipped on step into."] + #[serde(rename = "skipList")] + #[serde(skip_serializing_if = "Option::is_none")] + pub skip_list: Option>, + } + impl StepIntoParams { + pub fn builder() -> StepIntoParamsBuilder { + StepIntoParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StepIntoParamsBuilder { + break_on_async_call: Option, + skip_list: Option>, + } + impl StepIntoParamsBuilder { + pub fn break_on_async_call(mut self, break_on_async_call: impl Into) -> Self { + self.break_on_async_call = Some(break_on_async_call.into()); + self + } + pub fn skip_list(mut self, skip_list: impl Into) -> Self { + let v = self.skip_list.get_or_insert(Vec::new()); + v.push(skip_list.into()); + self + } + pub fn skip_lists(mut self, skip_lists: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.skip_list.get_or_insert(Vec::new()); + for val in skip_lists { + v.push(val.into()); + } + self + } + pub fn build(self) -> StepIntoParams { + StepIntoParams { + break_on_async_call: self.break_on_async_call, + skip_list: self.skip_list, + } + } + } + impl StepIntoParams { + pub const IDENTIFIER: &'static str = "Debugger.stepInto"; + } + impl chromiumoxide_types::Method for StepIntoParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StepIntoParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Steps into the function call.\n[stepInto](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepInto)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StepIntoReturns {} + impl chromiumoxide_types::Command for StepIntoParams { + type Response = StepIntoReturns; + } + #[doc = "Steps out of the function call.\n[stepOut](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepOut)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StepOutParams {} + impl StepOutParams { + pub const IDENTIFIER: &'static str = "Debugger.stepOut"; + } + impl chromiumoxide_types::Method for StepOutParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StepOutParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Steps out of the function call.\n[stepOut](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepOut)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StepOutReturns {} + impl chromiumoxide_types::Command for StepOutParams { + type Response = StepOutReturns; + } + #[doc = "Steps over the statement.\n[stepOver](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepOver)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StepOverParams { + #[doc = "The skipList specifies location ranges that should be skipped on step over."] + #[serde(rename = "skipList")] + #[serde(skip_serializing_if = "Option::is_none")] + pub skip_list: Option>, + } + impl StepOverParams { + pub fn builder() -> StepOverParamsBuilder { + StepOverParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StepOverParamsBuilder { + skip_list: Option>, + } + impl StepOverParamsBuilder { + pub fn skip_list(mut self, skip_list: impl Into) -> Self { + let v = self.skip_list.get_or_insert(Vec::new()); + v.push(skip_list.into()); + self + } + pub fn skip_lists(mut self, skip_lists: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.skip_list.get_or_insert(Vec::new()); + for val in skip_lists { + v.push(val.into()); + } + self + } + pub fn build(self) -> StepOverParams { + StepOverParams { + skip_list: self.skip_list, + } + } + } + impl StepOverParams { + pub const IDENTIFIER: &'static str = "Debugger.stepOver"; + } + impl chromiumoxide_types::Method for StepOverParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StepOverParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Steps over the statement.\n[stepOver](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepOver)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StepOverReturns {} + impl chromiumoxide_types::Command for StepOverParams { + type Response = StepOverReturns; + } + #[doc = "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.\n[paused](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-paused)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventPaused { + #[doc = "Call stack the virtual machine stopped on."] + #[serde(rename = "callFrames")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub call_frames: Vec, + #[doc = "Pause reason."] + #[serde(rename = "reason")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub reason: PausedReason, + #[doc = "Object containing break-specific auxiliary properties."] + #[serde(rename = "data")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + #[doc = "Hit breakpoints IDs"] + #[serde(rename = "hitBreakpoints")] + #[serde(skip_serializing_if = "Option::is_none")] + pub hit_breakpoints: Option>, + #[doc = "Async stack trace, if any."] + #[serde(rename = "asyncStackTrace")] + #[serde(skip_serializing_if = "Option::is_none")] + pub async_stack_trace: Option, + #[doc = "Async stack trace, if any."] + #[serde(rename = "asyncStackTraceId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub async_stack_trace_id: Option, + } + #[doc = "Pause reason."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum PausedReason { + #[serde(rename = "ambiguous")] + Ambiguous, + #[serde(rename = "assert")] + Assert, + #[serde(rename = "CSPViolation")] + CspViolation, + #[serde(rename = "debugCommand")] + DebugCommand, + #[serde(rename = "DOM")] + Dom, + #[serde(rename = "EventListener")] + EventListener, + #[serde(rename = "exception")] + Exception, + #[serde(rename = "instrumentation")] + Instrumentation, + #[serde(rename = "OOM")] + Oom, + #[serde(rename = "other")] + Other, + #[serde(rename = "promiseRejection")] + PromiseRejection, + #[serde(rename = "XHR")] + Xhr, + #[serde(rename = "step")] + Step, + } + impl AsRef for PausedReason { + fn as_ref(&self) -> &str { + match self { + PausedReason::Ambiguous => "ambiguous", + PausedReason::Assert => "assert", + PausedReason::CspViolation => "CSPViolation", + PausedReason::DebugCommand => "debugCommand", + PausedReason::Dom => "DOM", + PausedReason::EventListener => "EventListener", + PausedReason::Exception => "exception", + PausedReason::Instrumentation => "instrumentation", + PausedReason::Oom => "OOM", + PausedReason::Other => "other", + PausedReason::PromiseRejection => "promiseRejection", + PausedReason::Xhr => "XHR", + PausedReason::Step => "step", + } + } + } + impl ::std::str::FromStr for PausedReason { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "ambiguous" | "Ambiguous" => Ok(PausedReason::Ambiguous), + "assert" | "Assert" => Ok(PausedReason::Assert), + "CSPViolation" | "CspViolation" | "cspviolation" => { + Ok(PausedReason::CspViolation) + } + "debugCommand" | "DebugCommand" | "debugcommand" => { + Ok(PausedReason::DebugCommand) + } + "DOM" | "Dom" | "dom" => Ok(PausedReason::Dom), + "EventListener" | "eventlistener" => Ok(PausedReason::EventListener), + "exception" | "Exception" => Ok(PausedReason::Exception), + "instrumentation" | "Instrumentation" => Ok(PausedReason::Instrumentation), + "OOM" | "Oom" | "oom" => Ok(PausedReason::Oom), + "other" | "Other" => Ok(PausedReason::Other), + "promiseRejection" | "PromiseRejection" | "promiserejection" => { + Ok(PausedReason::PromiseRejection) + } + "XHR" | "Xhr" | "xhr" => Ok(PausedReason::Xhr), + "step" | "Step" => Ok(PausedReason::Step), + _ => Err(s.to_string()), + } + } + } + impl EventPaused { + pub const IDENTIFIER: &'static str = "Debugger.paused"; + } + impl chromiumoxide_types::Method for EventPaused { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventPaused { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Fired when the virtual machine resumed execution.\n[resumed](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-resumed)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EventResumed {} + impl EventResumed { + pub const IDENTIFIER: &'static str = "Debugger.resumed"; + } + impl chromiumoxide_types::Method for EventResumed { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventResumed { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Fired when virtual machine fails to parse the script.\n[scriptFailedToParse](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-scriptFailedToParse)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventScriptFailedToParse { + #[doc = "Identifier of the script parsed."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "URL or name of the script parsed (if any)."] + #[serde(rename = "url")] + pub url: String, + #[doc = "Line offset of the script within the resource with given URL (for script tags)."] + #[serde(rename = "startLine")] + pub start_line: i64, + #[doc = "Column offset of the script within the resource with given URL."] + #[serde(rename = "startColumn")] + pub start_column: i64, + #[doc = "Last line of the script."] + #[serde(rename = "endLine")] + pub end_line: i64, + #[doc = "Length of the last line of the script."] + #[serde(rename = "endColumn")] + pub end_column: i64, + #[doc = "Specifies script creation context."] + #[serde(rename = "executionContextId")] + pub execution_context_id: super::runtime::ExecutionContextId, + #[doc = "Content hash of the script, SHA-256."] + #[serde(rename = "hash")] + pub hash: String, + #[doc = "For Wasm modules, the content of the `build_id` custom section. For JavaScript the `debugId` magic comment."] + #[serde(rename = "buildId")] + pub build_id: String, + #[doc = "Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string}"] + #[serde(rename = "executionContextAuxData")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_aux_data: Option, + #[doc = "URL of source map associated with script (if any)."] + #[serde(rename = "sourceMapURL")] + #[serde(skip_serializing_if = "Option::is_none")] + pub source_map_url: Option, + #[doc = "True, if this script has sourceURL."] + #[serde(rename = "hasSourceURL")] + #[serde(skip_serializing_if = "Option::is_none")] + pub has_source_url: Option, + #[doc = "True, if this script is ES6 module."] + #[serde(rename = "isModule")] + #[serde(skip_serializing_if = "Option::is_none")] + pub is_module: Option, + #[doc = "This script length."] + #[serde(rename = "length")] + #[serde(skip_serializing_if = "Option::is_none")] + pub length: Option, + #[doc = "JavaScript top stack frame of where the script parsed event was triggered if available."] + #[serde(rename = "stackTrace")] + #[serde(skip_serializing_if = "Option::is_none")] + pub stack_trace: Option, + #[doc = "If the scriptLanguage is WebAssembly, the code section offset in the module."] + #[serde(rename = "codeOffset")] + #[serde(skip_serializing_if = "Option::is_none")] + pub code_offset: Option, + #[doc = "The language of the script."] + #[serde(rename = "scriptLanguage")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub script_language: Option, + #[doc = "The name the embedder supplied for this script."] + #[serde(rename = "embedderName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub embedder_name: Option, + } + impl EventScriptFailedToParse { + pub const IDENTIFIER: &'static str = "Debugger.scriptFailedToParse"; + } + impl chromiumoxide_types::Method for EventScriptFailedToParse { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventScriptFailedToParse { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Fired when virtual machine parses script. This event is also fired for all known and uncollected\nscripts upon enabling debugger.\n[scriptParsed](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-scriptParsed)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventScriptParsed { + #[doc = "Identifier of the script parsed."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "URL or name of the script parsed (if any)."] + #[serde(rename = "url")] + pub url: String, + #[doc = "Line offset of the script within the resource with given URL (for script tags)."] + #[serde(rename = "startLine")] + pub start_line: i64, + #[doc = "Column offset of the script within the resource with given URL."] + #[serde(rename = "startColumn")] + pub start_column: i64, + #[doc = "Last line of the script."] + #[serde(rename = "endLine")] + pub end_line: i64, + #[doc = "Length of the last line of the script."] + #[serde(rename = "endColumn")] + pub end_column: i64, + #[doc = "Specifies script creation context."] + #[serde(rename = "executionContextId")] + pub execution_context_id: super::runtime::ExecutionContextId, + #[doc = "Content hash of the script, SHA-256."] + #[serde(rename = "hash")] + pub hash: String, + #[doc = "For Wasm modules, the content of the `build_id` custom section. For JavaScript the `debugId` magic comment."] + #[serde(rename = "buildId")] + pub build_id: String, + #[doc = "Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string}"] + #[serde(rename = "executionContextAuxData")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_aux_data: Option, + #[doc = "True, if this script is generated as a result of the live edit operation."] + #[serde(rename = "isLiveEdit")] + #[serde(skip_serializing_if = "Option::is_none")] + pub is_live_edit: Option, + #[doc = "URL of source map associated with script (if any)."] + #[serde(rename = "sourceMapURL")] + #[serde(skip_serializing_if = "Option::is_none")] + pub source_map_url: Option, + #[doc = "True, if this script has sourceURL."] + #[serde(rename = "hasSourceURL")] + #[serde(skip_serializing_if = "Option::is_none")] + pub has_source_url: Option, + #[doc = "True, if this script is ES6 module."] + #[serde(rename = "isModule")] + #[serde(skip_serializing_if = "Option::is_none")] + pub is_module: Option, + #[doc = "This script length."] + #[serde(rename = "length")] + #[serde(skip_serializing_if = "Option::is_none")] + pub length: Option, + #[doc = "JavaScript top stack frame of where the script parsed event was triggered if available."] + #[serde(rename = "stackTrace")] + #[serde(skip_serializing_if = "Option::is_none")] + pub stack_trace: Option, + #[doc = "If the scriptLanguage is WebAssembly, the code section offset in the module."] + #[serde(rename = "codeOffset")] + #[serde(skip_serializing_if = "Option::is_none")] + pub code_offset: Option, + #[doc = "The language of the script."] + #[serde(rename = "scriptLanguage")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub script_language: Option, + #[doc = "If the scriptLanguage is WebAssembly, the source of debug symbols for the module."] + #[serde(rename = "debugSymbols")] + #[serde(skip_serializing_if = "Option::is_none")] + pub debug_symbols: Option>, + #[doc = "The name the embedder supplied for this script."] + #[serde(rename = "embedderName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub embedder_name: Option, + #[doc = "The list of set breakpoints in this script if calls to `setBreakpointByUrl`\nmatches this script's URL or hash. Clients that use this list can ignore the\n`breakpointResolved` event. They are equivalent."] + #[serde(rename = "resolvedBreakpoints")] + #[serde(skip_serializing_if = "Option::is_none")] + pub resolved_breakpoints: Option>, + } + impl EventScriptParsed { + pub const IDENTIFIER: &'static str = "Debugger.scriptParsed"; + } + impl chromiumoxide_types::Method for EventScriptParsed { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventScriptParsed { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + } + pub mod heap_profiler { + use serde::{Deserialize, Serialize}; + #[doc = "Heap snapshot object id.\n[HeapSnapshotObjectId](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#type-HeapSnapshotObjectId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct HeapSnapshotObjectId(String); + impl HeapSnapshotObjectId { + pub fn new(val: impl Into) -> Self { + HeapSnapshotObjectId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for HeapSnapshotObjectId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: HeapSnapshotObjectId) -> String { + el.0 + } + } + impl From for HeapSnapshotObjectId { + fn from(expr: String) -> Self { + HeapSnapshotObjectId(expr) + } + } + impl std::borrow::Borrow for HeapSnapshotObjectId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl HeapSnapshotObjectId { + pub const IDENTIFIER: &'static str = "HeapProfiler.HeapSnapshotObjectId"; + } + #[doc = "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.\n[SamplingHeapProfileNode](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#type-SamplingHeapProfileNode)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SamplingHeapProfileNode { + #[doc = "Function location."] + #[serde(rename = "callFrame")] + pub call_frame: super::runtime::CallFrame, + #[doc = "Allocations size in bytes for the node excluding children."] + #[serde(rename = "selfSize")] + pub self_size: f64, + #[doc = "Node id. Ids are unique across all profiles collected between startSampling and stopSampling."] + #[serde(rename = "id")] + pub id: i64, + #[doc = "Child nodes."] + #[serde(rename = "children")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub children: Vec, + } + impl SamplingHeapProfileNode { + pub fn new( + call_frame: impl Into, + self_size: impl Into, + id: impl Into, + children: Vec, + ) -> Self { + Self { + call_frame: call_frame.into(), + self_size: self_size.into(), + id: id.into(), + children, + } + } + } + impl SamplingHeapProfileNode { + pub fn builder() -> SamplingHeapProfileNodeBuilder { + SamplingHeapProfileNodeBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SamplingHeapProfileNodeBuilder { + call_frame: Option, + self_size: Option, + id: Option, + children: Option>, + } + impl SamplingHeapProfileNodeBuilder { + pub fn call_frame(mut self, call_frame: impl Into) -> Self { + self.call_frame = Some(call_frame.into()); + self + } + pub fn self_size(mut self, self_size: impl Into) -> Self { + self.self_size = Some(self_size.into()); + self + } + pub fn id(mut self, id: impl Into) -> Self { + self.id = Some(id.into()); + self + } + pub fn children(mut self, children: impl Into) -> Self { + let v = self.children.get_or_insert(Vec::new()); + v.push(children.into()); + self + } + pub fn childrens(mut self, childrens: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.children.get_or_insert(Vec::new()); + for val in childrens { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(SamplingHeapProfileNode { + call_frame: self.call_frame.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frame)) + })?, + self_size: self.self_size.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(self_size)) + })?, + id: self + .id + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(id)))?, + children: self.children.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(children)) + })?, + }) + } + } + impl SamplingHeapProfileNode { + pub const IDENTIFIER: &'static str = "HeapProfiler.SamplingHeapProfileNode"; + } + #[doc = "A single sample from a sampling profile.\n[SamplingHeapProfileSample](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#type-SamplingHeapProfileSample)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SamplingHeapProfileSample { + #[doc = "Allocation size in bytes attributed to the sample."] + #[serde(rename = "size")] + pub size: f64, + #[doc = "Id of the corresponding profile tree node."] + #[serde(rename = "nodeId")] + pub node_id: i64, + #[doc = "Time-ordered sample ordinal number. It is unique across all profiles retrieved\nbetween startSampling and stopSampling."] + #[serde(rename = "ordinal")] + pub ordinal: f64, + } + impl SamplingHeapProfileSample { + pub fn new( + size: impl Into, + node_id: impl Into, + ordinal: impl Into, + ) -> Self { + Self { + size: size.into(), + node_id: node_id.into(), + ordinal: ordinal.into(), + } + } + } + impl SamplingHeapProfileSample { + pub fn builder() -> SamplingHeapProfileSampleBuilder { + SamplingHeapProfileSampleBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SamplingHeapProfileSampleBuilder { + size: Option, + node_id: Option, + ordinal: Option, + } + impl SamplingHeapProfileSampleBuilder { + pub fn size(mut self, size: impl Into) -> Self { + self.size = Some(size.into()); + self + } + pub fn node_id(mut self, node_id: impl Into) -> Self { + self.node_id = Some(node_id.into()); + self + } + pub fn ordinal(mut self, ordinal: impl Into) -> Self { + self.ordinal = Some(ordinal.into()); + self + } + pub fn build(self) -> Result { + Ok(SamplingHeapProfileSample { + size: self.size.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(size)) + })?, + node_id: self.node_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(node_id)) + })?, + ordinal: self.ordinal.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(ordinal)) + })?, + }) + } + } + impl SamplingHeapProfileSample { + pub const IDENTIFIER: &'static str = "HeapProfiler.SamplingHeapProfileSample"; + } + #[doc = "Sampling profile.\n[SamplingHeapProfile](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#type-SamplingHeapProfile)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SamplingHeapProfile { + #[serde(rename = "head")] + pub head: SamplingHeapProfileNode, + #[serde(rename = "samples")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub samples: Vec, + } + impl SamplingHeapProfile { + pub fn new( + head: impl Into, + samples: Vec, + ) -> Self { + Self { + head: head.into(), + samples, + } + } + } + impl SamplingHeapProfile { + pub fn builder() -> SamplingHeapProfileBuilder { + SamplingHeapProfileBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SamplingHeapProfileBuilder { + head: Option, + samples: Option>, + } + impl SamplingHeapProfileBuilder { + pub fn head(mut self, head: impl Into) -> Self { + self.head = Some(head.into()); + self + } + pub fn sample(mut self, sample: impl Into) -> Self { + let v = self.samples.get_or_insert(Vec::new()); + v.push(sample.into()); + self + } + pub fn samples(mut self, samples: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.samples.get_or_insert(Vec::new()); + for val in samples { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(SamplingHeapProfile { + head: self.head.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(head)) + })?, + samples: self.samples.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(samples)) + })?, + }) + } + } + impl SamplingHeapProfile { + pub const IDENTIFIER: &'static str = "HeapProfiler.SamplingHeapProfile"; + } + #[doc = "Enables console to refer to the node with given id via $x (see Command Line API for more details\n$x functions).\n[addInspectedHeapObject](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#method-addInspectedHeapObject)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct AddInspectedHeapObjectParams { + #[doc = "Heap snapshot object id to be accessible by means of $x command line API."] + #[serde(rename = "heapObjectId")] + pub heap_object_id: HeapSnapshotObjectId, + } + impl AddInspectedHeapObjectParams { + pub fn new(heap_object_id: impl Into) -> Self { + Self { + heap_object_id: heap_object_id.into(), + } + } + } + impl AddInspectedHeapObjectParams { + pub fn builder() -> AddInspectedHeapObjectParamsBuilder { + AddInspectedHeapObjectParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct AddInspectedHeapObjectParamsBuilder { + heap_object_id: Option, + } + impl AddInspectedHeapObjectParamsBuilder { + pub fn heap_object_id( + mut self, + heap_object_id: impl Into, + ) -> Self { + self.heap_object_id = Some(heap_object_id.into()); + self + } + pub fn build(self) -> Result { + Ok(AddInspectedHeapObjectParams { + heap_object_id: self.heap_object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(heap_object_id)) + })?, + }) + } + } + impl AddInspectedHeapObjectParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.addInspectedHeapObject"; + } + impl chromiumoxide_types::Method for AddInspectedHeapObjectParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for AddInspectedHeapObjectParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Enables console to refer to the node with given id via $x (see Command Line API for more details\n$x functions).\n[addInspectedHeapObject](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#method-addInspectedHeapObject)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct AddInspectedHeapObjectReturns {} + impl chromiumoxide_types::Command for AddInspectedHeapObjectParams { + type Response = AddInspectedHeapObjectReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct CollectGarbageParams {} + impl CollectGarbageParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.collectGarbage"; + } + impl chromiumoxide_types::Method for CollectGarbageParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for CollectGarbageParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct CollectGarbageReturns {} + impl chromiumoxide_types::Command for CollectGarbageParams { + type Response = CollectGarbageReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableParams {} + impl DisableParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.disable"; + } + impl chromiumoxide_types::Method for DisableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for DisableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableReturns {} + impl chromiumoxide_types::Command for DisableParams { + type Response = DisableReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableParams {} + impl EnableParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.enable"; + } + impl chromiumoxide_types::Method for EnableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EnableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableReturns {} + impl chromiumoxide_types::Command for EnableParams { + type Response = EnableReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetHeapObjectIdParams { + #[doc = "Identifier of the object to get heap object id for."] + #[serde(rename = "objectId")] + pub object_id: super::runtime::RemoteObjectId, + } + impl GetHeapObjectIdParams { + pub fn new(object_id: impl Into) -> Self { + Self { + object_id: object_id.into(), + } + } + } + impl GetHeapObjectIdParams { + pub fn builder() -> GetHeapObjectIdParamsBuilder { + GetHeapObjectIdParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetHeapObjectIdParamsBuilder { + object_id: Option, + } + impl GetHeapObjectIdParamsBuilder { + pub fn object_id( + mut self, + object_id: impl Into, + ) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn build(self) -> Result { + Ok(GetHeapObjectIdParams { + object_id: self.object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object_id)) + })?, + }) + } + } + impl GetHeapObjectIdParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.getHeapObjectId"; + } + impl chromiumoxide_types::Method for GetHeapObjectIdParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetHeapObjectIdParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetHeapObjectIdReturns { + #[doc = "Id of the heap snapshot object corresponding to the passed remote object id."] + #[serde(rename = "heapSnapshotObjectId")] + pub heap_snapshot_object_id: HeapSnapshotObjectId, + } + impl GetHeapObjectIdReturns { + pub fn new(heap_snapshot_object_id: impl Into) -> Self { + Self { + heap_snapshot_object_id: heap_snapshot_object_id.into(), + } + } + } + impl GetHeapObjectIdReturns { + pub fn builder() -> GetHeapObjectIdReturnsBuilder { + GetHeapObjectIdReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetHeapObjectIdReturnsBuilder { + heap_snapshot_object_id: Option, + } + impl GetHeapObjectIdReturnsBuilder { + pub fn heap_snapshot_object_id( + mut self, + heap_snapshot_object_id: impl Into, + ) -> Self { + self.heap_snapshot_object_id = Some(heap_snapshot_object_id.into()); + self + } + pub fn build(self) -> Result { + Ok(GetHeapObjectIdReturns { + heap_snapshot_object_id: self.heap_snapshot_object_id.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(heap_snapshot_object_id) + ) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetHeapObjectIdParams { + type Response = GetHeapObjectIdReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetObjectByHeapObjectIdParams { + #[serde(rename = "objectId")] + pub object_id: HeapSnapshotObjectId, + #[doc = "Symbolic group name that can be used to release multiple objects."] + #[serde(rename = "objectGroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + } + impl GetObjectByHeapObjectIdParams { + pub fn new(object_id: impl Into) -> Self { + Self { + object_id: object_id.into(), + object_group: None, + } + } + } + impl GetObjectByHeapObjectIdParams { + pub fn builder() -> GetObjectByHeapObjectIdParamsBuilder { + GetObjectByHeapObjectIdParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetObjectByHeapObjectIdParamsBuilder { + object_id: Option, + object_group: Option, + } + impl GetObjectByHeapObjectIdParamsBuilder { + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn build(self) -> Result { + Ok(GetObjectByHeapObjectIdParams { + object_id: self.object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object_id)) + })?, + object_group: self.object_group, + }) + } + } + impl GetObjectByHeapObjectIdParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.getObjectByHeapObjectId"; + } + impl chromiumoxide_types::Method for GetObjectByHeapObjectIdParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetObjectByHeapObjectIdParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetObjectByHeapObjectIdReturns { + #[doc = "Evaluation result."] + #[serde(rename = "result")] + pub result: super::runtime::RemoteObject, + } + impl GetObjectByHeapObjectIdReturns { + pub fn new(result: impl Into) -> Self { + Self { + result: result.into(), + } + } + } + impl GetObjectByHeapObjectIdReturns { + pub fn builder() -> GetObjectByHeapObjectIdReturnsBuilder { + GetObjectByHeapObjectIdReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetObjectByHeapObjectIdReturnsBuilder { + result: Option, + } + impl GetObjectByHeapObjectIdReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + self.result = Some(result.into()); + self + } + pub fn build(self) -> Result { + Ok(GetObjectByHeapObjectIdReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetObjectByHeapObjectIdParams { + type Response = GetObjectByHeapObjectIdReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct GetSamplingProfileParams {} + impl GetSamplingProfileParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.getSamplingProfile"; + } + impl chromiumoxide_types::Method for GetSamplingProfileParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetSamplingProfileParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetSamplingProfileReturns { + #[doc = "Return the sampling profile being collected."] + #[serde(rename = "profile")] + pub profile: SamplingHeapProfile, + } + impl GetSamplingProfileReturns { + pub fn new(profile: impl Into) -> Self { + Self { + profile: profile.into(), + } + } + } + impl GetSamplingProfileReturns { + pub fn builder() -> GetSamplingProfileReturnsBuilder { + GetSamplingProfileReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetSamplingProfileReturnsBuilder { + profile: Option, + } + impl GetSamplingProfileReturnsBuilder { + pub fn profile(mut self, profile: impl Into) -> Self { + self.profile = Some(profile.into()); + self + } + pub fn build(self) -> Result { + Ok(GetSamplingProfileReturns { + profile: self.profile.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(profile)) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetSamplingProfileParams { + type Response = GetSamplingProfileReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartSamplingParams { + #[doc = "Average sample interval in bytes. Poisson distribution is used for the intervals. The\ndefault value is 32768 bytes."] + #[serde(rename = "samplingInterval")] + #[serde(skip_serializing_if = "Option::is_none")] + pub sampling_interval: Option, + #[doc = "Maximum stack depth. The default value is 128."] + #[serde(rename = "stackDepth")] + #[serde(skip_serializing_if = "Option::is_none")] + pub stack_depth: Option, + #[doc = "By default, the sampling heap profiler reports only objects which are\nstill alive when the profile is returned via getSamplingProfile or\nstopSampling, which is useful for determining what functions contribute\nthe most to steady-state memory usage. This flag instructs the sampling\nheap profiler to also include information about objects discarded by\nmajor GC, which will show which functions cause large temporary memory\nusage or long GC pauses."] + #[serde(rename = "includeObjectsCollectedByMajorGC")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_objects_collected_by_major_gc: Option, + #[doc = "By default, the sampling heap profiler reports only objects which are\nstill alive when the profile is returned via getSamplingProfile or\nstopSampling, which is useful for determining what functions contribute\nthe most to steady-state memory usage. This flag instructs the sampling\nheap profiler to also include information about objects discarded by\nminor GC, which is useful when tuning a latency-sensitive application\nfor minimal GC activity."] + #[serde(rename = "includeObjectsCollectedByMinorGC")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_objects_collected_by_minor_gc: Option, + } + impl StartSamplingParams { + pub fn builder() -> StartSamplingParamsBuilder { + StartSamplingParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StartSamplingParamsBuilder { + sampling_interval: Option, + stack_depth: Option, + include_objects_collected_by_major_gc: Option, + include_objects_collected_by_minor_gc: Option, + } + impl StartSamplingParamsBuilder { + pub fn sampling_interval(mut self, sampling_interval: impl Into) -> Self { + self.sampling_interval = Some(sampling_interval.into()); + self + } + pub fn stack_depth(mut self, stack_depth: impl Into) -> Self { + self.stack_depth = Some(stack_depth.into()); + self + } + pub fn include_objects_collected_by_major_gc( + mut self, + include_objects_collected_by_major_gc: impl Into, + ) -> Self { + self.include_objects_collected_by_major_gc = + Some(include_objects_collected_by_major_gc.into()); + self + } + pub fn include_objects_collected_by_minor_gc( + mut self, + include_objects_collected_by_minor_gc: impl Into, + ) -> Self { + self.include_objects_collected_by_minor_gc = + Some(include_objects_collected_by_minor_gc.into()); + self + } + pub fn build(self) -> StartSamplingParams { + StartSamplingParams { + sampling_interval: self.sampling_interval, + stack_depth: self.stack_depth, + include_objects_collected_by_major_gc: self + .include_objects_collected_by_major_gc, + include_objects_collected_by_minor_gc: self + .include_objects_collected_by_minor_gc, + } + } + } + impl StartSamplingParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.startSampling"; + } + impl chromiumoxide_types::Method for StartSamplingParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StartSamplingParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartSamplingReturns {} + impl chromiumoxide_types::Command for StartSamplingParams { + type Response = StartSamplingReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartTrackingHeapObjectsParams { + #[serde(rename = "trackAllocations")] + #[serde(skip_serializing_if = "Option::is_none")] + pub track_allocations: Option, + } + impl StartTrackingHeapObjectsParams { + pub fn builder() -> StartTrackingHeapObjectsParamsBuilder { + StartTrackingHeapObjectsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StartTrackingHeapObjectsParamsBuilder { + track_allocations: Option, + } + impl StartTrackingHeapObjectsParamsBuilder { + pub fn track_allocations(mut self, track_allocations: impl Into) -> Self { + self.track_allocations = Some(track_allocations.into()); + self + } + pub fn build(self) -> StartTrackingHeapObjectsParams { + StartTrackingHeapObjectsParams { + track_allocations: self.track_allocations, + } + } + } + impl StartTrackingHeapObjectsParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.startTrackingHeapObjects"; + } + impl chromiumoxide_types::Method for StartTrackingHeapObjectsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StartTrackingHeapObjectsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartTrackingHeapObjectsReturns {} + impl chromiumoxide_types::Command for StartTrackingHeapObjectsParams { + type Response = StartTrackingHeapObjectsReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StopSamplingParams {} + impl StopSamplingParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.stopSampling"; + } + impl chromiumoxide_types::Method for StopSamplingParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StopSamplingParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct StopSamplingReturns { + #[doc = "Recorded sampling heap profile."] + #[serde(rename = "profile")] + pub profile: SamplingHeapProfile, + } + impl StopSamplingReturns { + pub fn new(profile: impl Into) -> Self { + Self { + profile: profile.into(), + } + } + } + impl StopSamplingReturns { + pub fn builder() -> StopSamplingReturnsBuilder { + StopSamplingReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StopSamplingReturnsBuilder { + profile: Option, + } + impl StopSamplingReturnsBuilder { + pub fn profile(mut self, profile: impl Into) -> Self { + self.profile = Some(profile.into()); + self + } + pub fn build(self) -> Result { + Ok(StopSamplingReturns { + profile: self.profile.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(profile)) + })?, + }) + } + } + impl chromiumoxide_types::Command for StopSamplingParams { + type Response = StopSamplingReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StopTrackingHeapObjectsParams { + #[doc = "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken\nwhen the tracking is stopped."] + #[serde(rename = "reportProgress")] + #[serde(skip_serializing_if = "Option::is_none")] + pub report_progress: Option, + #[doc = "If true, numerical values are included in the snapshot"] + #[serde(rename = "captureNumericValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub capture_numeric_value: Option, + #[doc = "If true, exposes internals of the snapshot."] + #[serde(rename = "exposeInternals")] + #[serde(skip_serializing_if = "Option::is_none")] + pub expose_internals: Option, + } + impl StopTrackingHeapObjectsParams { + pub fn builder() -> StopTrackingHeapObjectsParamsBuilder { + StopTrackingHeapObjectsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StopTrackingHeapObjectsParamsBuilder { + report_progress: Option, + capture_numeric_value: Option, + expose_internals: Option, + } + impl StopTrackingHeapObjectsParamsBuilder { + pub fn report_progress(mut self, report_progress: impl Into) -> Self { + self.report_progress = Some(report_progress.into()); + self + } + pub fn capture_numeric_value(mut self, capture_numeric_value: impl Into) -> Self { + self.capture_numeric_value = Some(capture_numeric_value.into()); + self + } + pub fn expose_internals(mut self, expose_internals: impl Into) -> Self { + self.expose_internals = Some(expose_internals.into()); + self + } + pub fn build(self) -> StopTrackingHeapObjectsParams { + StopTrackingHeapObjectsParams { + report_progress: self.report_progress, + capture_numeric_value: self.capture_numeric_value, + expose_internals: self.expose_internals, + } + } + } + impl StopTrackingHeapObjectsParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.stopTrackingHeapObjects"; + } + impl chromiumoxide_types::Method for StopTrackingHeapObjectsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StopTrackingHeapObjectsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StopTrackingHeapObjectsReturns {} + impl chromiumoxide_types::Command for StopTrackingHeapObjectsParams { + type Response = StopTrackingHeapObjectsReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct TakeHeapSnapshotParams { + #[doc = "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken."] + #[serde(rename = "reportProgress")] + #[serde(skip_serializing_if = "Option::is_none")] + pub report_progress: Option, + #[doc = "If true, numerical values are included in the snapshot"] + #[serde(rename = "captureNumericValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub capture_numeric_value: Option, + #[doc = "If true, exposes internals of the snapshot."] + #[serde(rename = "exposeInternals")] + #[serde(skip_serializing_if = "Option::is_none")] + pub expose_internals: Option, + } + impl TakeHeapSnapshotParams { + pub fn builder() -> TakeHeapSnapshotParamsBuilder { + TakeHeapSnapshotParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct TakeHeapSnapshotParamsBuilder { + report_progress: Option, + capture_numeric_value: Option, + expose_internals: Option, + } + impl TakeHeapSnapshotParamsBuilder { + pub fn report_progress(mut self, report_progress: impl Into) -> Self { + self.report_progress = Some(report_progress.into()); + self + } + pub fn capture_numeric_value(mut self, capture_numeric_value: impl Into) -> Self { + self.capture_numeric_value = Some(capture_numeric_value.into()); + self + } + pub fn expose_internals(mut self, expose_internals: impl Into) -> Self { + self.expose_internals = Some(expose_internals.into()); + self + } + pub fn build(self) -> TakeHeapSnapshotParams { + TakeHeapSnapshotParams { + report_progress: self.report_progress, + capture_numeric_value: self.capture_numeric_value, + expose_internals: self.expose_internals, + } + } + } + impl TakeHeapSnapshotParams { + pub const IDENTIFIER: &'static str = "HeapProfiler.takeHeapSnapshot"; + } + impl chromiumoxide_types::Method for TakeHeapSnapshotParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for TakeHeapSnapshotParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct TakeHeapSnapshotReturns {} + impl chromiumoxide_types::Command for TakeHeapSnapshotParams { + type Response = TakeHeapSnapshotReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventAddHeapSnapshotChunk { + #[serde(rename = "chunk")] + pub chunk: String, + } + impl EventAddHeapSnapshotChunk { + pub const IDENTIFIER: &'static str = "HeapProfiler.addHeapSnapshotChunk"; + } + impl chromiumoxide_types::Method for EventAddHeapSnapshotChunk { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventAddHeapSnapshotChunk { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "If heap objects tracking has been started then backend may send update for one or more fragments\n[heapStatsUpdate](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#event-heapStatsUpdate)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventHeapStatsUpdate { + #[doc = "An array of triplets. Each triplet describes a fragment. The first integer is the fragment\nindex, the second integer is a total count of objects for the fragment, the third integer is\na total size of the objects for the fragment."] + #[serde(rename = "statsUpdate")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub stats_update: Vec, + } + impl EventHeapStatsUpdate { + pub const IDENTIFIER: &'static str = "HeapProfiler.heapStatsUpdate"; + } + impl chromiumoxide_types::Method for EventHeapStatsUpdate { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventHeapStatsUpdate { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "If heap objects tracking has been started then backend regularly sends a current value for last\nseen object id and corresponding timestamp. If the were changes in the heap since last event\nthen one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.\n[lastSeenObjectId](https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#event-lastSeenObjectId)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventLastSeenObjectId { + #[serde(rename = "lastSeenObjectId")] + pub last_seen_object_id: i64, + #[serde(rename = "timestamp")] + pub timestamp: f64, + } + impl EventLastSeenObjectId { + pub const IDENTIFIER: &'static str = "HeapProfiler.lastSeenObjectId"; + } + impl chromiumoxide_types::Method for EventLastSeenObjectId { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventLastSeenObjectId { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventReportHeapSnapshotProgress { + #[serde(rename = "done")] + pub done: i64, + #[serde(rename = "total")] + pub total: i64, + #[serde(rename = "finished")] + #[serde(skip_serializing_if = "Option::is_none")] + pub finished: Option, + } + impl EventReportHeapSnapshotProgress { + pub const IDENTIFIER: &'static str = "HeapProfiler.reportHeapSnapshotProgress"; + } + impl chromiumoxide_types::Method for EventReportHeapSnapshotProgress { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventReportHeapSnapshotProgress { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EventResetProfiles {} + impl EventResetProfiles { + pub const IDENTIFIER: &'static str = "HeapProfiler.resetProfiles"; + } + impl chromiumoxide_types::Method for EventResetProfiles { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventResetProfiles { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + } + pub mod profiler { + use serde::{Deserialize, Serialize}; + #[doc = "Profile node. Holds callsite information, execution statistics and child nodes.\n[ProfileNode](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ProfileNode)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ProfileNode { + #[doc = "Unique id of the node."] + #[serde(rename = "id")] + pub id: i64, + #[doc = "Function location."] + #[serde(rename = "callFrame")] + pub call_frame: super::runtime::CallFrame, + #[doc = "Number of samples where this node was on top of the call stack."] + #[serde(rename = "hitCount")] + #[serde(skip_serializing_if = "Option::is_none")] + pub hit_count: Option, + #[doc = "Child node ids."] + #[serde(rename = "children")] + #[serde(skip_serializing_if = "Option::is_none")] + pub children: Option>, + #[doc = "The reason of being not optimized. The function may be deoptimized or marked as don't\noptimize."] + #[serde(rename = "deoptReason")] + #[serde(skip_serializing_if = "Option::is_none")] + pub deopt_reason: Option, + #[doc = "An array of source position ticks."] + #[serde(rename = "positionTicks")] + #[serde(skip_serializing_if = "Option::is_none")] + pub position_ticks: Option>, + } + impl ProfileNode { + pub fn new( + id: impl Into, + call_frame: impl Into, + ) -> Self { + Self { + id: id.into(), + call_frame: call_frame.into(), + hit_count: None, + children: None, + deopt_reason: None, + position_ticks: None, + } + } + } + impl ProfileNode { + pub fn builder() -> ProfileNodeBuilder { + ProfileNodeBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ProfileNodeBuilder { + id: Option, + call_frame: Option, + hit_count: Option, + children: Option>, + deopt_reason: Option, + position_ticks: Option>, + } + impl ProfileNodeBuilder { + pub fn id(mut self, id: impl Into) -> Self { + self.id = Some(id.into()); + self + } + pub fn call_frame(mut self, call_frame: impl Into) -> Self { + self.call_frame = Some(call_frame.into()); + self + } + pub fn hit_count(mut self, hit_count: impl Into) -> Self { + self.hit_count = Some(hit_count.into()); + self + } + pub fn children(mut self, children: impl Into) -> Self { + let v = self.children.get_or_insert(Vec::new()); + v.push(children.into()); + self + } + pub fn childrens(mut self, childrens: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.children.get_or_insert(Vec::new()); + for val in childrens { + v.push(val.into()); + } + self + } + pub fn deopt_reason(mut self, deopt_reason: impl Into) -> Self { + self.deopt_reason = Some(deopt_reason.into()); + self + } + pub fn position_tick(mut self, position_tick: impl Into) -> Self { + let v = self.position_ticks.get_or_insert(Vec::new()); + v.push(position_tick.into()); + self + } + pub fn position_ticks(mut self, position_ticks: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.position_ticks.get_or_insert(Vec::new()); + for val in position_ticks { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(ProfileNode { + id: self + .id + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(id)))?, + call_frame: self.call_frame.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frame)) + })?, + hit_count: self.hit_count, + children: self.children, + deopt_reason: self.deopt_reason, + position_ticks: self.position_ticks, + }) + } + } + impl ProfileNode { + pub const IDENTIFIER: &'static str = "Profiler.ProfileNode"; + } + #[doc = "Profile.\n[Profile](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-Profile)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct Profile { + #[doc = "The list of profile nodes. First item is the root node."] + #[serde(rename = "nodes")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub nodes: Vec, + #[doc = "Profiling start timestamp in microseconds."] + #[serde(rename = "startTime")] + pub start_time: f64, + #[doc = "Profiling end timestamp in microseconds."] + #[serde(rename = "endTime")] + pub end_time: f64, + #[doc = "Ids of samples top nodes."] + #[serde(rename = "samples")] + #[serde(skip_serializing_if = "Option::is_none")] + pub samples: Option>, + #[doc = "Time intervals between adjacent samples in microseconds. The first delta is relative to the\nprofile startTime."] + #[serde(rename = "timeDeltas")] + #[serde(skip_serializing_if = "Option::is_none")] + pub time_deltas: Option>, + } + impl Profile { + pub fn new( + nodes: Vec, + start_time: impl Into, + end_time: impl Into, + ) -> Self { + Self { + nodes, + start_time: start_time.into(), + end_time: end_time.into(), + samples: None, + time_deltas: None, + } + } + } + impl Profile { + pub fn builder() -> ProfileBuilder { + ProfileBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ProfileBuilder { + nodes: Option>, + start_time: Option, + end_time: Option, + samples: Option>, + time_deltas: Option>, + } + impl ProfileBuilder { + pub fn node(mut self, node: impl Into) -> Self { + let v = self.nodes.get_or_insert(Vec::new()); + v.push(node.into()); + self + } + pub fn nodes(mut self, nodes: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.nodes.get_or_insert(Vec::new()); + for val in nodes { + v.push(val.into()); + } + self + } + pub fn start_time(mut self, start_time: impl Into) -> Self { + self.start_time = Some(start_time.into()); + self + } + pub fn end_time(mut self, end_time: impl Into) -> Self { + self.end_time = Some(end_time.into()); + self + } + pub fn sample(mut self, sample: impl Into) -> Self { + let v = self.samples.get_or_insert(Vec::new()); + v.push(sample.into()); + self + } + pub fn samples(mut self, samples: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.samples.get_or_insert(Vec::new()); + for val in samples { + v.push(val.into()); + } + self + } + pub fn time_delta(mut self, time_delta: impl Into) -> Self { + let v = self.time_deltas.get_or_insert(Vec::new()); + v.push(time_delta.into()); + self + } + pub fn time_deltas(mut self, time_deltas: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.time_deltas.get_or_insert(Vec::new()); + for val in time_deltas { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(Profile { + nodes: self.nodes.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(nodes)) + })?, + start_time: self.start_time.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(start_time)) + })?, + end_time: self.end_time.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(end_time)) + })?, + samples: self.samples, + time_deltas: self.time_deltas, + }) + } + } + impl Profile { + pub const IDENTIFIER: &'static str = "Profiler.Profile"; + } + #[doc = "Specifies a number of samples attributed to a certain source position.\n[PositionTickInfo](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-PositionTickInfo)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct PositionTickInfo { + #[doc = "Source line number (1-based)."] + #[serde(rename = "line")] + pub line: i64, + #[doc = "Number of samples attributed to the source line."] + #[serde(rename = "ticks")] + pub ticks: i64, + } + impl PositionTickInfo { + pub fn new(line: impl Into, ticks: impl Into) -> Self { + Self { + line: line.into(), + ticks: ticks.into(), + } + } + } + impl PositionTickInfo { + pub fn builder() -> PositionTickInfoBuilder { + PositionTickInfoBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct PositionTickInfoBuilder { + line: Option, + ticks: Option, + } + impl PositionTickInfoBuilder { + pub fn line(mut self, line: impl Into) -> Self { + self.line = Some(line.into()); + self + } + pub fn ticks(mut self, ticks: impl Into) -> Self { + self.ticks = Some(ticks.into()); + self + } + pub fn build(self) -> Result { + Ok(PositionTickInfo { + line: self.line.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line)) + })?, + ticks: self.ticks.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(ticks)) + })?, + }) + } + } + impl PositionTickInfo { + pub const IDENTIFIER: &'static str = "Profiler.PositionTickInfo"; + } + #[doc = "Coverage data for a source range.\n[CoverageRange](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-CoverageRange)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CoverageRange { + #[doc = "JavaScript script source offset for the range start."] + #[serde(rename = "startOffset")] + pub start_offset: i64, + #[doc = "JavaScript script source offset for the range end."] + #[serde(rename = "endOffset")] + pub end_offset: i64, + #[doc = "Collected execution count of the source range."] + #[serde(rename = "count")] + pub count: i64, + } + impl CoverageRange { + pub fn new( + start_offset: impl Into, + end_offset: impl Into, + count: impl Into, + ) -> Self { + Self { + start_offset: start_offset.into(), + end_offset: end_offset.into(), + count: count.into(), + } + } + } + impl CoverageRange { + pub fn builder() -> CoverageRangeBuilder { + CoverageRangeBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CoverageRangeBuilder { + start_offset: Option, + end_offset: Option, + count: Option, + } + impl CoverageRangeBuilder { + pub fn start_offset(mut self, start_offset: impl Into) -> Self { + self.start_offset = Some(start_offset.into()); + self + } + pub fn end_offset(mut self, end_offset: impl Into) -> Self { + self.end_offset = Some(end_offset.into()); + self + } + pub fn count(mut self, count: impl Into) -> Self { + self.count = Some(count.into()); + self + } + pub fn build(self) -> Result { + Ok(CoverageRange { + start_offset: self.start_offset.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(start_offset)) + })?, + end_offset: self.end_offset.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(end_offset)) + })?, + count: self.count.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(count)) + })?, + }) + } + } + impl CoverageRange { + pub const IDENTIFIER: &'static str = "Profiler.CoverageRange"; + } + #[doc = "Coverage data for a JavaScript function.\n[FunctionCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-FunctionCoverage)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct FunctionCoverage { + #[doc = "JavaScript function name."] + #[serde(rename = "functionName")] + pub function_name: String, + #[doc = "Source ranges inside the function with coverage data."] + #[serde(rename = "ranges")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub ranges: Vec, + #[doc = "Whether coverage data for this function has block granularity."] + #[serde(rename = "isBlockCoverage")] + pub is_block_coverage: bool, + } + impl FunctionCoverage { + pub fn new( + function_name: impl Into, + ranges: Vec, + is_block_coverage: impl Into, + ) -> Self { + Self { + function_name: function_name.into(), + ranges, + is_block_coverage: is_block_coverage.into(), + } + } + } + impl FunctionCoverage { + pub fn builder() -> FunctionCoverageBuilder { + FunctionCoverageBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct FunctionCoverageBuilder { + function_name: Option, + ranges: Option>, + is_block_coverage: Option, + } + impl FunctionCoverageBuilder { + pub fn function_name(mut self, function_name: impl Into) -> Self { + self.function_name = Some(function_name.into()); + self + } + pub fn range(mut self, range: impl Into) -> Self { + let v = self.ranges.get_or_insert(Vec::new()); + v.push(range.into()); + self + } + pub fn ranges(mut self, ranges: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.ranges.get_or_insert(Vec::new()); + for val in ranges { + v.push(val.into()); + } + self + } + pub fn is_block_coverage(mut self, is_block_coverage: impl Into) -> Self { + self.is_block_coverage = Some(is_block_coverage.into()); + self + } + pub fn build(self) -> Result { + Ok(FunctionCoverage { + function_name: self.function_name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(function_name)) + })?, + ranges: self.ranges.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(ranges)) + })?, + is_block_coverage: self.is_block_coverage.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(is_block_coverage) + ) + })?, + }) + } + } + impl FunctionCoverage { + pub const IDENTIFIER: &'static str = "Profiler.FunctionCoverage"; + } + #[doc = "Coverage data for a JavaScript script.\n[ScriptCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-ScriptCoverage)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ScriptCoverage { + #[doc = "JavaScript script id."] + #[serde(rename = "scriptId")] + pub script_id: super::runtime::ScriptId, + #[doc = "JavaScript script name or url."] + #[serde(rename = "url")] + pub url: String, + #[doc = "Functions contained in the script that has coverage data."] + #[serde(rename = "functions")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub functions: Vec, + } + impl ScriptCoverage { + pub fn new( + script_id: impl Into, + url: impl Into, + functions: Vec, + ) -> Self { + Self { + script_id: script_id.into(), + url: url.into(), + functions, + } + } + } + impl ScriptCoverage { + pub fn builder() -> ScriptCoverageBuilder { + ScriptCoverageBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ScriptCoverageBuilder { + script_id: Option, + url: Option, + functions: Option>, + } + impl ScriptCoverageBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn url(mut self, url: impl Into) -> Self { + self.url = Some(url.into()); + self + } + pub fn function(mut self, function: impl Into) -> Self { + let v = self.functions.get_or_insert(Vec::new()); + v.push(function.into()); + self + } + pub fn functions(mut self, functions: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.functions.get_or_insert(Vec::new()); + for val in functions { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(ScriptCoverage { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + url: self + .url + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(url)))?, + functions: self.functions.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(functions)) + })?, + }) + } + } + impl ScriptCoverage { + pub const IDENTIFIER: &'static str = "Profiler.ScriptCoverage"; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableParams {} + impl DisableParams { + pub const IDENTIFIER: &'static str = "Profiler.disable"; + } + impl chromiumoxide_types::Method for DisableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for DisableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableReturns {} + impl chromiumoxide_types::Command for DisableParams { + type Response = DisableReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableParams {} + impl EnableParams { + pub const IDENTIFIER: &'static str = "Profiler.enable"; + } + impl chromiumoxide_types::Method for EnableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EnableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableReturns {} + impl chromiumoxide_types::Command for EnableParams { + type Response = EnableReturns; + } + #[doc = "Collect coverage data for the current isolate. The coverage data may be incomplete due to\ngarbage collection.\n[getBestEffortCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-getBestEffortCoverage)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct GetBestEffortCoverageParams {} + impl GetBestEffortCoverageParams { + pub const IDENTIFIER: &'static str = "Profiler.getBestEffortCoverage"; + } + impl chromiumoxide_types::Method for GetBestEffortCoverageParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetBestEffortCoverageParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Collect coverage data for the current isolate. The coverage data may be incomplete due to\ngarbage collection.\n[getBestEffortCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-getBestEffortCoverage)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetBestEffortCoverageReturns { + #[doc = "Coverage data for the current isolate."] + #[serde(rename = "result")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub result: Vec, + } + impl GetBestEffortCoverageReturns { + pub fn new(result: Vec) -> Self { + Self { result } + } + } + impl GetBestEffortCoverageReturns { + pub fn builder() -> GetBestEffortCoverageReturnsBuilder { + GetBestEffortCoverageReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetBestEffortCoverageReturnsBuilder { + result: Option>, + } + impl GetBestEffortCoverageReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + let v = self.result.get_or_insert(Vec::new()); + v.push(result.into()); + self + } + pub fn results(mut self, results: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.result.get_or_insert(Vec::new()); + for val in results { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(GetBestEffortCoverageReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetBestEffortCoverageParams { + type Response = GetBestEffortCoverageReturns; + } + #[doc = "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started.\n[setSamplingInterval](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-setSamplingInterval)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetSamplingIntervalParams { + #[doc = "New sampling interval in microseconds."] + #[serde(rename = "interval")] + pub interval: i64, + } + impl SetSamplingIntervalParams { + pub fn new(interval: impl Into) -> Self { + Self { + interval: interval.into(), + } + } + } + impl SetSamplingIntervalParams { + pub fn builder() -> SetSamplingIntervalParamsBuilder { + SetSamplingIntervalParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetSamplingIntervalParamsBuilder { + interval: Option, + } + impl SetSamplingIntervalParamsBuilder { + pub fn interval(mut self, interval: impl Into) -> Self { + self.interval = Some(interval.into()); + self + } + pub fn build(self) -> Result { + Ok(SetSamplingIntervalParams { + interval: self.interval.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(interval)) + })?, + }) + } + } + impl SetSamplingIntervalParams { + pub const IDENTIFIER: &'static str = "Profiler.setSamplingInterval"; + } + impl chromiumoxide_types::Method for SetSamplingIntervalParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetSamplingIntervalParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started.\n[setSamplingInterval](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-setSamplingInterval)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetSamplingIntervalReturns {} + impl chromiumoxide_types::Command for SetSamplingIntervalParams { + type Response = SetSamplingIntervalReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartParams {} + impl StartParams { + pub const IDENTIFIER: &'static str = "Profiler.start"; + } + impl chromiumoxide_types::Method for StartParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StartParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartReturns {} + impl chromiumoxide_types::Command for StartParams { + type Response = StartReturns; + } + #[doc = "Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code\ncoverage may be incomplete. Enabling prevents running optimized code and resets execution\ncounters.\n[startPreciseCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StartPreciseCoverageParams { + #[doc = "Collect accurate call counts beyond simple 'covered' or 'not covered'."] + #[serde(rename = "callCount")] + #[serde(skip_serializing_if = "Option::is_none")] + pub call_count: Option, + #[doc = "Collect block-based coverage."] + #[serde(rename = "detailed")] + #[serde(skip_serializing_if = "Option::is_none")] + pub detailed: Option, + #[doc = "Allow the backend to send updates on its own initiative"] + #[serde(rename = "allowTriggeredUpdates")] + #[serde(skip_serializing_if = "Option::is_none")] + pub allow_triggered_updates: Option, + } + impl StartPreciseCoverageParams { + pub fn builder() -> StartPreciseCoverageParamsBuilder { + StartPreciseCoverageParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StartPreciseCoverageParamsBuilder { + call_count: Option, + detailed: Option, + allow_triggered_updates: Option, + } + impl StartPreciseCoverageParamsBuilder { + pub fn call_count(mut self, call_count: impl Into) -> Self { + self.call_count = Some(call_count.into()); + self + } + pub fn detailed(mut self, detailed: impl Into) -> Self { + self.detailed = Some(detailed.into()); + self + } + pub fn allow_triggered_updates( + mut self, + allow_triggered_updates: impl Into, + ) -> Self { + self.allow_triggered_updates = Some(allow_triggered_updates.into()); + self + } + pub fn build(self) -> StartPreciseCoverageParams { + StartPreciseCoverageParams { + call_count: self.call_count, + detailed: self.detailed, + allow_triggered_updates: self.allow_triggered_updates, + } + } + } + impl StartPreciseCoverageParams { + pub const IDENTIFIER: &'static str = "Profiler.startPreciseCoverage"; + } + impl chromiumoxide_types::Method for StartPreciseCoverageParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StartPreciseCoverageParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code\ncoverage may be incomplete. Enabling prevents running optimized code and resets execution\ncounters.\n[startPreciseCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-startPreciseCoverage)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct StartPreciseCoverageReturns { + #[doc = "Monotonically increasing time (in seconds) when the coverage update was taken in the backend."] + #[serde(rename = "timestamp")] + pub timestamp: f64, + } + impl StartPreciseCoverageReturns { + pub fn new(timestamp: impl Into) -> Self { + Self { + timestamp: timestamp.into(), + } + } + } + impl StartPreciseCoverageReturns { + pub fn builder() -> StartPreciseCoverageReturnsBuilder { + StartPreciseCoverageReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StartPreciseCoverageReturnsBuilder { + timestamp: Option, + } + impl StartPreciseCoverageReturnsBuilder { + pub fn timestamp(mut self, timestamp: impl Into) -> Self { + self.timestamp = Some(timestamp.into()); + self + } + pub fn build(self) -> Result { + Ok(StartPreciseCoverageReturns { + timestamp: self.timestamp.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(timestamp)) + })?, + }) + } + } + impl chromiumoxide_types::Command for StartPreciseCoverageParams { + type Response = StartPreciseCoverageReturns; + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StopParams {} + impl StopParams { + pub const IDENTIFIER: &'static str = "Profiler.stop"; + } + impl chromiumoxide_types::Method for StopParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StopParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct StopReturns { + #[doc = "Recorded profile."] + #[serde(rename = "profile")] + pub profile: Profile, + } + impl StopReturns { + pub fn new(profile: impl Into) -> Self { + Self { + profile: profile.into(), + } + } + } + impl StopReturns { + pub fn builder() -> StopReturnsBuilder { + StopReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StopReturnsBuilder { + profile: Option, + } + impl StopReturnsBuilder { + pub fn profile(mut self, profile: impl Into) -> Self { + self.profile = Some(profile.into()); + self + } + pub fn build(self) -> Result { + Ok(StopReturns { + profile: self.profile.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(profile)) + })?, + }) + } + } + impl chromiumoxide_types::Command for StopParams { + type Response = StopReturns; + } + #[doc = "Disable precise code coverage. Disabling releases unnecessary execution count records and allows\nexecuting optimized code.\n[stopPreciseCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-stopPreciseCoverage)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StopPreciseCoverageParams {} + impl StopPreciseCoverageParams { + pub const IDENTIFIER: &'static str = "Profiler.stopPreciseCoverage"; + } + impl chromiumoxide_types::Method for StopPreciseCoverageParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for StopPreciseCoverageParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Disable precise code coverage. Disabling releases unnecessary execution count records and allows\nexecuting optimized code.\n[stopPreciseCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-stopPreciseCoverage)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct StopPreciseCoverageReturns {} + impl chromiumoxide_types::Command for StopPreciseCoverageParams { + type Response = StopPreciseCoverageReturns; + } + #[doc = "Collect coverage data for the current isolate, and resets execution counters. Precise code\ncoverage needs to have started.\n[takePreciseCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-takePreciseCoverage)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct TakePreciseCoverageParams {} + impl TakePreciseCoverageParams { + pub const IDENTIFIER: &'static str = "Profiler.takePreciseCoverage"; + } + impl chromiumoxide_types::Method for TakePreciseCoverageParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for TakePreciseCoverageParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Collect coverage data for the current isolate, and resets execution counters. Precise code\ncoverage needs to have started.\n[takePreciseCoverage](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-takePreciseCoverage)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct TakePreciseCoverageReturns { + #[doc = "Coverage data for the current isolate."] + #[serde(rename = "result")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub result: Vec, + #[doc = "Monotonically increasing time (in seconds) when the coverage update was taken in the backend."] + #[serde(rename = "timestamp")] + pub timestamp: f64, + } + impl TakePreciseCoverageReturns { + pub fn new(result: Vec, timestamp: impl Into) -> Self { + Self { + result, + timestamp: timestamp.into(), + } + } + } + impl TakePreciseCoverageReturns { + pub fn builder() -> TakePreciseCoverageReturnsBuilder { + TakePreciseCoverageReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct TakePreciseCoverageReturnsBuilder { + result: Option>, + timestamp: Option, + } + impl TakePreciseCoverageReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + let v = self.result.get_or_insert(Vec::new()); + v.push(result.into()); + self + } + pub fn results(mut self, results: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.result.get_or_insert(Vec::new()); + for val in results { + v.push(val.into()); + } + self + } + pub fn timestamp(mut self, timestamp: impl Into) -> Self { + self.timestamp = Some(timestamp.into()); + self + } + pub fn build(self) -> Result { + Ok(TakePreciseCoverageReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + timestamp: self.timestamp.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(timestamp)) + })?, + }) + } + } + impl chromiumoxide_types::Command for TakePreciseCoverageParams { + type Response = TakePreciseCoverageReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventConsoleProfileFinished { + #[serde(rename = "id")] + pub id: String, + #[doc = "Location of console.profileEnd()."] + #[serde(rename = "location")] + pub location: super::debugger::Location, + #[serde(rename = "profile")] + pub profile: Profile, + #[doc = "Profile title passed as an argument to console.profile()."] + #[serde(rename = "title")] + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + } + impl EventConsoleProfileFinished { + pub const IDENTIFIER: &'static str = "Profiler.consoleProfileFinished"; + } + impl chromiumoxide_types::Method for EventConsoleProfileFinished { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventConsoleProfileFinished { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Sent when new profile recording is started using console.profile() call.\n[consoleProfileStarted](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#event-consoleProfileStarted)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventConsoleProfileStarted { + #[serde(rename = "id")] + pub id: String, + #[doc = "Location of console.profile()."] + #[serde(rename = "location")] + pub location: super::debugger::Location, + #[doc = "Profile title passed as an argument to console.profile()."] + #[serde(rename = "title")] + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + } + impl EventConsoleProfileStarted { + pub const IDENTIFIER: &'static str = "Profiler.consoleProfileStarted"; + } + impl chromiumoxide_types::Method for EventConsoleProfileStarted { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventConsoleProfileStarted { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Reports coverage delta since the last poll (either from an event like this, or from\n`takePreciseCoverage` for the current isolate. May only be sent if precise code\ncoverage has been started. This event can be trigged by the embedder to, for example,\ntrigger collection of coverage data immediately at a certain point in time.\n[preciseCoverageDeltaUpdate](https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#event-preciseCoverageDeltaUpdate)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventPreciseCoverageDeltaUpdate { + #[doc = "Monotonically increasing time (in seconds) when the coverage update was taken in the backend."] + #[serde(rename = "timestamp")] + pub timestamp: f64, + #[doc = "Identifier for distinguishing coverage events."] + #[serde(rename = "occasion")] + pub occasion: String, + #[doc = "Coverage data for the current isolate."] + #[serde(rename = "result")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub result: Vec, + } + impl EventPreciseCoverageDeltaUpdate { + pub const IDENTIFIER: &'static str = "Profiler.preciseCoverageDeltaUpdate"; + } + impl chromiumoxide_types::Method for EventPreciseCoverageDeltaUpdate { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventPreciseCoverageDeltaUpdate { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + } + #[doc = "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects.\nEvaluation results are returned as mirror object that expose object type, string representation\nand unique identifier that can be used for further object reference. Original objects are\nmaintained in memory unless they are either explicitly released or are released along with the\nother objects in their object group."] + pub mod runtime { + use serde::{Deserialize, Serialize}; + #[doc = "Unique script identifier.\n[ScriptId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ScriptId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct ScriptId(String); + impl ScriptId { + pub fn new(val: impl Into) -> Self { + ScriptId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for ScriptId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: ScriptId) -> String { + el.0 + } + } + impl From for ScriptId { + fn from(expr: String) -> Self { + ScriptId(expr) + } + } + impl std::borrow::Borrow for ScriptId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl ScriptId { + pub const IDENTIFIER: &'static str = "Runtime.ScriptId"; + } + #[doc = "Represents options for serialization. Overrides `generatePreview` and `returnByValue`.\n[SerializationOptions](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-SerializationOptions)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SerializationOptions { + #[serde(rename = "serialization")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub serialization: SerializationOptionsSerialization, + #[doc = "Deep serialization depth. Default is full depth. Respected only in `deep` serialization mode."] + #[serde(rename = "maxDepth")] + #[serde(skip_serializing_if = "Option::is_none")] + pub max_depth: Option, + #[doc = "Embedder-specific parameters. For example if connected to V8 in Chrome these control DOM\nserialization via `maxNodeDepth: integer` and `includeShadowTree: \"none\" | \"open\" | \"all\"`.\nValues can be only of type string or integer."] + #[serde(rename = "additionalParameters")] + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_parameters: Option, + } + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum SerializationOptionsSerialization { + #[doc = "Whether the result should be deep-serialized. The result is put into\n`deepSerializedValue` and `ObjectId` is provided."] + #[serde(rename = "deep")] + Deep, + #[doc = "Whether the result is expected to be a JSON object which should be sent by value.\nThe result is put either into `value` or into `unserializableValue`. Synonym of\n`returnByValue: true`. Overrides `returnByValue`."] + #[serde(rename = "json")] + Json, + #[doc = "Only remote object id is put in the result. Same bahaviour as if no\n`serializationOptions`, `generatePreview` nor `returnByValue` are provided."] + #[serde(rename = "idOnly")] + IdOnly, + } + impl AsRef for SerializationOptionsSerialization { + fn as_ref(&self) -> &str { + match self { + SerializationOptionsSerialization::Deep => "deep", + SerializationOptionsSerialization::Json => "json", + SerializationOptionsSerialization::IdOnly => "idOnly", + } + } + } + impl ::std::str::FromStr for SerializationOptionsSerialization { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "deep" | "Deep" => Ok(SerializationOptionsSerialization::Deep), + "json" | "Json" => Ok(SerializationOptionsSerialization::Json), + "idOnly" | "IdOnly" | "idonly" => Ok(SerializationOptionsSerialization::IdOnly), + _ => Err(s.to_string()), + } + } + } + impl SerializationOptions { + pub fn new(serialization: impl Into) -> Self { + Self { + serialization: serialization.into(), + max_depth: None, + additional_parameters: None, + } + } + } + impl SerializationOptions { + pub fn builder() -> SerializationOptionsBuilder { + SerializationOptionsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SerializationOptionsBuilder { + serialization: Option, + max_depth: Option, + additional_parameters: Option, + } + impl SerializationOptionsBuilder { + pub fn serialization( + mut self, + serialization: impl Into, + ) -> Self { + self.serialization = Some(serialization.into()); + self + } + pub fn max_depth(mut self, max_depth: impl Into) -> Self { + self.max_depth = Some(max_depth.into()); + self + } + pub fn additional_parameters( + mut self, + additional_parameters: impl Into, + ) -> Self { + self.additional_parameters = Some(additional_parameters.into()); + self + } + pub fn build(self) -> Result { + Ok(SerializationOptions { + serialization: self.serialization.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(serialization)) + })?, + max_depth: self.max_depth, + additional_parameters: self.additional_parameters, + }) + } + } + impl SerializationOptions { + pub const IDENTIFIER: &'static str = "Runtime.SerializationOptions"; + } + #[doc = "Represents deep serialized value.\n[DeepSerializedValue](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-DeepSerializedValue)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct DeepSerializedValue { + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: DeepSerializedValueType, + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[serde(rename = "objectId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_id: Option, + #[doc = "Set if value reference met more then once during serialization. In such\ncase, value is provided only to one of the serialized values. Unique\nper value in the scope of one CDP call."] + #[serde(rename = "weakLocalObjectReference")] + #[serde(skip_serializing_if = "Option::is_none")] + pub weak_local_object_reference: Option, + } + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum DeepSerializedValueType { + #[serde(rename = "undefined")] + Undefined, + #[serde(rename = "null")] + Null, + #[serde(rename = "string")] + String, + #[serde(rename = "number")] + Number, + #[serde(rename = "boolean")] + Boolean, + #[serde(rename = "bigint")] + Bigint, + #[serde(rename = "regexp")] + Regexp, + #[serde(rename = "date")] + Date, + #[serde(rename = "symbol")] + Symbol, + #[serde(rename = "array")] + Array, + #[serde(rename = "object")] + Object, + #[serde(rename = "function")] + Function, + #[serde(rename = "map")] + Map, + #[serde(rename = "set")] + Set, + #[serde(rename = "weakmap")] + Weakmap, + #[serde(rename = "weakset")] + Weakset, + #[serde(rename = "error")] + Error, + #[serde(rename = "proxy")] + Proxy, + #[serde(rename = "promise")] + Promise, + #[serde(rename = "typedarray")] + Typedarray, + #[serde(rename = "arraybuffer")] + Arraybuffer, + #[serde(rename = "node")] + Node, + #[serde(rename = "window")] + Window, + #[serde(rename = "generator")] + Generator, + } + impl AsRef for DeepSerializedValueType { + fn as_ref(&self) -> &str { + match self { + DeepSerializedValueType::Undefined => "undefined", + DeepSerializedValueType::Null => "null", + DeepSerializedValueType::String => "string", + DeepSerializedValueType::Number => "number", + DeepSerializedValueType::Boolean => "boolean", + DeepSerializedValueType::Bigint => "bigint", + DeepSerializedValueType::Regexp => "regexp", + DeepSerializedValueType::Date => "date", + DeepSerializedValueType::Symbol => "symbol", + DeepSerializedValueType::Array => "array", + DeepSerializedValueType::Object => "object", + DeepSerializedValueType::Function => "function", + DeepSerializedValueType::Map => "map", + DeepSerializedValueType::Set => "set", + DeepSerializedValueType::Weakmap => "weakmap", + DeepSerializedValueType::Weakset => "weakset", + DeepSerializedValueType::Error => "error", + DeepSerializedValueType::Proxy => "proxy", + DeepSerializedValueType::Promise => "promise", + DeepSerializedValueType::Typedarray => "typedarray", + DeepSerializedValueType::Arraybuffer => "arraybuffer", + DeepSerializedValueType::Node => "node", + DeepSerializedValueType::Window => "window", + DeepSerializedValueType::Generator => "generator", + } + } + } + impl ::std::str::FromStr for DeepSerializedValueType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "undefined" | "Undefined" => Ok(DeepSerializedValueType::Undefined), + "null" | "Null" => Ok(DeepSerializedValueType::Null), + "string" | "String" => Ok(DeepSerializedValueType::String), + "number" | "Number" => Ok(DeepSerializedValueType::Number), + "boolean" | "Boolean" => Ok(DeepSerializedValueType::Boolean), + "bigint" | "Bigint" => Ok(DeepSerializedValueType::Bigint), + "regexp" | "Regexp" => Ok(DeepSerializedValueType::Regexp), + "date" | "Date" => Ok(DeepSerializedValueType::Date), + "symbol" | "Symbol" => Ok(DeepSerializedValueType::Symbol), + "array" | "Array" => Ok(DeepSerializedValueType::Array), + "object" | "Object" => Ok(DeepSerializedValueType::Object), + "function" | "Function" => Ok(DeepSerializedValueType::Function), + "map" | "Map" => Ok(DeepSerializedValueType::Map), + "set" | "Set" => Ok(DeepSerializedValueType::Set), + "weakmap" | "Weakmap" => Ok(DeepSerializedValueType::Weakmap), + "weakset" | "Weakset" => Ok(DeepSerializedValueType::Weakset), + "error" | "Error" => Ok(DeepSerializedValueType::Error), + "proxy" | "Proxy" => Ok(DeepSerializedValueType::Proxy), + "promise" | "Promise" => Ok(DeepSerializedValueType::Promise), + "typedarray" | "Typedarray" => Ok(DeepSerializedValueType::Typedarray), + "arraybuffer" | "Arraybuffer" => Ok(DeepSerializedValueType::Arraybuffer), + "node" | "Node" => Ok(DeepSerializedValueType::Node), + "window" | "Window" => Ok(DeepSerializedValueType::Window), + "generator" | "Generator" => Ok(DeepSerializedValueType::Generator), + _ => Err(s.to_string()), + } + } + } + impl DeepSerializedValue { + pub fn new(r#type: impl Into) -> Self { + Self { + r#type: r#type.into(), + value: None, + object_id: None, + weak_local_object_reference: None, + } + } + } + impl DeepSerializedValue { + pub fn builder() -> DeepSerializedValueBuilder { + DeepSerializedValueBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct DeepSerializedValueBuilder { + r#type: Option, + value: Option, + object_id: Option, + weak_local_object_reference: Option, + } + impl DeepSerializedValueBuilder { + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn weak_local_object_reference( + mut self, + weak_local_object_reference: impl Into, + ) -> Self { + self.weak_local_object_reference = Some(weak_local_object_reference.into()); + self + } + pub fn build(self) -> Result { + Ok(DeepSerializedValue { + r#type: self.r#type.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(r#type)) + })?, + value: self.value, + object_id: self.object_id, + weak_local_object_reference: self.weak_local_object_reference, + }) + } + } + impl DeepSerializedValue { + pub const IDENTIFIER: &'static str = "Runtime.DeepSerializedValue"; + } + #[doc = "Unique object identifier.\n[RemoteObjectId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObjectId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct RemoteObjectId(String); + impl RemoteObjectId { + pub fn new(val: impl Into) -> Self { + RemoteObjectId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for RemoteObjectId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: RemoteObjectId) -> String { + el.0 + } + } + impl From for RemoteObjectId { + fn from(expr: String) -> Self { + RemoteObjectId(expr) + } + } + impl std::borrow::Borrow for RemoteObjectId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl RemoteObjectId { + pub const IDENTIFIER: &'static str = "Runtime.RemoteObjectId"; + } + #[doc = "Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`,\n`-Infinity`, and bigint literals.\n[UnserializableValue](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-UnserializableValue)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct UnserializableValue(String); + impl UnserializableValue { + pub fn new(val: impl Into) -> Self { + UnserializableValue(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for UnserializableValue { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: UnserializableValue) -> String { + el.0 + } + } + impl From for UnserializableValue { + fn from(expr: String) -> Self { + UnserializableValue(expr) + } + } + impl UnserializableValue { + pub const IDENTIFIER: &'static str = "Runtime.UnserializableValue"; + } + #[doc = "Mirror object referencing original JavaScript object.\n[RemoteObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct RemoteObject { + #[doc = "Object type."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: RemoteObjectType, + #[doc = "Object subtype hint. Specified for `object` type values only.\nNOTE: If you change anything here, make sure to also update\n`subtype` in `ObjectPreview` and `PropertyPreview` below."] + #[serde(rename = "subtype")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub subtype: Option, + #[doc = "Object class (constructor) name. Specified for `object` type values only."] + #[serde(rename = "className")] + #[serde(skip_serializing_if = "Option::is_none")] + pub class_name: Option, + #[doc = "Remote object value in case of primitive values or JSON values (if it was requested)."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[doc = "Primitive value which can not be JSON-stringified does not have `value`, but gets this\nproperty."] + #[serde(rename = "unserializableValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub unserializable_value: Option, + #[doc = "String representation of the object."] + #[serde(rename = "description")] + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[doc = "Deep serialized value."] + #[serde(rename = "deepSerializedValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub deep_serialized_value: Option, + #[doc = "Unique object identifier (for non-primitive values)."] + #[serde(rename = "objectId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_id: Option, + #[doc = "Preview containing abbreviated property values. Specified for `object` type values only."] + #[serde(rename = "preview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub preview: Option, + #[serde(rename = "customPreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub custom_preview: Option, + } + #[doc = "Object type."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum RemoteObjectType { + #[serde(rename = "object")] + Object, + #[serde(rename = "function")] + Function, + #[serde(rename = "undefined")] + Undefined, + #[serde(rename = "string")] + String, + #[serde(rename = "number")] + Number, + #[serde(rename = "boolean")] + Boolean, + #[serde(rename = "symbol")] + Symbol, + #[serde(rename = "bigint")] + Bigint, + } + impl AsRef for RemoteObjectType { + fn as_ref(&self) -> &str { + match self { + RemoteObjectType::Object => "object", + RemoteObjectType::Function => "function", + RemoteObjectType::Undefined => "undefined", + RemoteObjectType::String => "string", + RemoteObjectType::Number => "number", + RemoteObjectType::Boolean => "boolean", + RemoteObjectType::Symbol => "symbol", + RemoteObjectType::Bigint => "bigint", + } + } + } + impl ::std::str::FromStr for RemoteObjectType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "object" | "Object" => Ok(RemoteObjectType::Object), + "function" | "Function" => Ok(RemoteObjectType::Function), + "undefined" | "Undefined" => Ok(RemoteObjectType::Undefined), + "string" | "String" => Ok(RemoteObjectType::String), + "number" | "Number" => Ok(RemoteObjectType::Number), + "boolean" | "Boolean" => Ok(RemoteObjectType::Boolean), + "symbol" | "Symbol" => Ok(RemoteObjectType::Symbol), + "bigint" | "Bigint" => Ok(RemoteObjectType::Bigint), + _ => Err(s.to_string()), + } + } + } + #[doc = "Object subtype hint. Specified for `object` type values only.\nNOTE: If you change anything here, make sure to also update\n`subtype` in `ObjectPreview` and `PropertyPreview` below."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum RemoteObjectSubtype { + #[serde(rename = "array")] + Array, + #[serde(rename = "null")] + Null, + #[serde(rename = "node")] + Node, + #[serde(rename = "regexp")] + Regexp, + #[serde(rename = "date")] + Date, + #[serde(rename = "map")] + Map, + #[serde(rename = "set")] + Set, + #[serde(rename = "weakmap")] + Weakmap, + #[serde(rename = "weakset")] + Weakset, + #[serde(rename = "iterator")] + Iterator, + #[serde(rename = "generator")] + Generator, + #[serde(rename = "error")] + Error, + #[serde(rename = "proxy")] + Proxy, + #[serde(rename = "promise")] + Promise, + #[serde(rename = "typedarray")] + Typedarray, + #[serde(rename = "arraybuffer")] + Arraybuffer, + #[serde(rename = "dataview")] + Dataview, + #[serde(rename = "webassemblymemory")] + Webassemblymemory, + #[serde(rename = "wasmvalue")] + Wasmvalue, + #[doc = "blink's subtypes."] + #[serde(rename = "trustedtype")] + Trustedtype, + } + impl AsRef for RemoteObjectSubtype { + fn as_ref(&self) -> &str { + match self { + RemoteObjectSubtype::Array => "array", + RemoteObjectSubtype::Null => "null", + RemoteObjectSubtype::Node => "node", + RemoteObjectSubtype::Regexp => "regexp", + RemoteObjectSubtype::Date => "date", + RemoteObjectSubtype::Map => "map", + RemoteObjectSubtype::Set => "set", + RemoteObjectSubtype::Weakmap => "weakmap", + RemoteObjectSubtype::Weakset => "weakset", + RemoteObjectSubtype::Iterator => "iterator", + RemoteObjectSubtype::Generator => "generator", + RemoteObjectSubtype::Error => "error", + RemoteObjectSubtype::Proxy => "proxy", + RemoteObjectSubtype::Promise => "promise", + RemoteObjectSubtype::Typedarray => "typedarray", + RemoteObjectSubtype::Arraybuffer => "arraybuffer", + RemoteObjectSubtype::Dataview => "dataview", + RemoteObjectSubtype::Webassemblymemory => "webassemblymemory", + RemoteObjectSubtype::Wasmvalue => "wasmvalue", + RemoteObjectSubtype::Trustedtype => "trustedtype", + } + } + } + impl ::std::str::FromStr for RemoteObjectSubtype { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "array" | "Array" => Ok(RemoteObjectSubtype::Array), + "null" | "Null" => Ok(RemoteObjectSubtype::Null), + "node" | "Node" => Ok(RemoteObjectSubtype::Node), + "regexp" | "Regexp" => Ok(RemoteObjectSubtype::Regexp), + "date" | "Date" => Ok(RemoteObjectSubtype::Date), + "map" | "Map" => Ok(RemoteObjectSubtype::Map), + "set" | "Set" => Ok(RemoteObjectSubtype::Set), + "weakmap" | "Weakmap" => Ok(RemoteObjectSubtype::Weakmap), + "weakset" | "Weakset" => Ok(RemoteObjectSubtype::Weakset), + "iterator" | "Iterator" => Ok(RemoteObjectSubtype::Iterator), + "generator" | "Generator" => Ok(RemoteObjectSubtype::Generator), + "error" | "Error" => Ok(RemoteObjectSubtype::Error), + "proxy" | "Proxy" => Ok(RemoteObjectSubtype::Proxy), + "promise" | "Promise" => Ok(RemoteObjectSubtype::Promise), + "typedarray" | "Typedarray" => Ok(RemoteObjectSubtype::Typedarray), + "arraybuffer" | "Arraybuffer" => Ok(RemoteObjectSubtype::Arraybuffer), + "dataview" | "Dataview" => Ok(RemoteObjectSubtype::Dataview), + "webassemblymemory" | "Webassemblymemory" => { + Ok(RemoteObjectSubtype::Webassemblymemory) + } + "wasmvalue" | "Wasmvalue" => Ok(RemoteObjectSubtype::Wasmvalue), + "trustedtype" | "Trustedtype" => Ok(RemoteObjectSubtype::Trustedtype), + _ => Err(s.to_string()), + } + } + } + impl RemoteObject { + pub fn new(r#type: impl Into) -> Self { + Self { + r#type: r#type.into(), + subtype: None, + class_name: None, + value: None, + unserializable_value: None, + description: None, + deep_serialized_value: None, + object_id: None, + preview: None, + custom_preview: None, + } + } + } + impl RemoteObject { + pub fn builder() -> RemoteObjectBuilder { + RemoteObjectBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct RemoteObjectBuilder { + r#type: Option, + subtype: Option, + class_name: Option, + value: Option, + unserializable_value: Option, + description: Option, + deep_serialized_value: Option, + object_id: Option, + preview: Option, + custom_preview: Option, + } + impl RemoteObjectBuilder { + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn subtype(mut self, subtype: impl Into) -> Self { + self.subtype = Some(subtype.into()); + self + } + pub fn class_name(mut self, class_name: impl Into) -> Self { + self.class_name = Some(class_name.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn unserializable_value( + mut self, + unserializable_value: impl Into, + ) -> Self { + self.unserializable_value = Some(unserializable_value.into()); + self + } + pub fn description(mut self, description: impl Into) -> Self { + self.description = Some(description.into()); + self + } + pub fn deep_serialized_value( + mut self, + deep_serialized_value: impl Into, + ) -> Self { + self.deep_serialized_value = Some(deep_serialized_value.into()); + self + } + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn preview(mut self, preview: impl Into) -> Self { + self.preview = Some(preview.into()); + self + } + pub fn custom_preview(mut self, custom_preview: impl Into) -> Self { + self.custom_preview = Some(custom_preview.into()); + self + } + pub fn build(self) -> Result { + Ok(RemoteObject { + r#type: self.r#type.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(r#type)) + })?, + subtype: self.subtype, + class_name: self.class_name, + value: self.value, + unserializable_value: self.unserializable_value, + description: self.description, + deep_serialized_value: self.deep_serialized_value, + object_id: self.object_id, + preview: self.preview, + custom_preview: self.custom_preview, + }) + } + } + impl RemoteObject { + pub const IDENTIFIER: &'static str = "Runtime.RemoteObject"; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CustomPreview { + #[doc = "The JSON-stringified result of formatter.header(object, config) call.\nIt contains json ML array that represents RemoteObject."] + #[serde(rename = "header")] + pub header: String, + #[doc = "If formatter returns true as a result of formatter.hasBody call then bodyGetterId will\ncontain RemoteObjectId for the function that returns result of formatter.body(object, config) call.\nThe result value is json ML array."] + #[serde(rename = "bodyGetterId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub body_getter_id: Option, + } + impl CustomPreview { + pub fn new(header: impl Into) -> Self { + Self { + header: header.into(), + body_getter_id: None, + } + } + } + impl> From for CustomPreview { + fn from(url: T) -> Self { + CustomPreview::new(url) + } + } + impl CustomPreview { + pub fn builder() -> CustomPreviewBuilder { + CustomPreviewBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CustomPreviewBuilder { + header: Option, + body_getter_id: Option, + } + impl CustomPreviewBuilder { + pub fn header(mut self, header: impl Into) -> Self { + self.header = Some(header.into()); + self + } + pub fn body_getter_id(mut self, body_getter_id: impl Into) -> Self { + self.body_getter_id = Some(body_getter_id.into()); + self + } + pub fn build(self) -> Result { + Ok(CustomPreview { + header: self.header.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(header)) + })?, + body_getter_id: self.body_getter_id, + }) + } + } + impl CustomPreview { + pub const IDENTIFIER: &'static str = "Runtime.CustomPreview"; + } + #[doc = "Object containing abbreviated remote object value.\n[ObjectPreview](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ObjectPreview)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ObjectPreview { + #[doc = "Object type."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: ObjectPreviewType, + #[doc = "Object subtype hint. Specified for `object` type values only."] + #[serde(rename = "subtype")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub subtype: Option, + #[doc = "String representation of the object."] + #[serde(rename = "description")] + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[doc = "True iff some of the properties or entries of the original object did not fit."] + #[serde(rename = "overflow")] + pub overflow: bool, + #[doc = "List of the properties."] + #[serde(rename = "properties")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub properties: Vec, + #[doc = "List of the entries. Specified for `map` and `set` subtype values only."] + #[serde(rename = "entries")] + #[serde(skip_serializing_if = "Option::is_none")] + pub entries: Option>, + } + #[doc = "Object type."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum ObjectPreviewType { + #[serde(rename = "object")] + Object, + #[serde(rename = "function")] + Function, + #[serde(rename = "undefined")] + Undefined, + #[serde(rename = "string")] + String, + #[serde(rename = "number")] + Number, + #[serde(rename = "boolean")] + Boolean, + #[serde(rename = "symbol")] + Symbol, + #[serde(rename = "bigint")] + Bigint, + } + impl AsRef for ObjectPreviewType { + fn as_ref(&self) -> &str { + match self { + ObjectPreviewType::Object => "object", + ObjectPreviewType::Function => "function", + ObjectPreviewType::Undefined => "undefined", + ObjectPreviewType::String => "string", + ObjectPreviewType::Number => "number", + ObjectPreviewType::Boolean => "boolean", + ObjectPreviewType::Symbol => "symbol", + ObjectPreviewType::Bigint => "bigint", + } + } + } + impl ::std::str::FromStr for ObjectPreviewType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "object" | "Object" => Ok(ObjectPreviewType::Object), + "function" | "Function" => Ok(ObjectPreviewType::Function), + "undefined" | "Undefined" => Ok(ObjectPreviewType::Undefined), + "string" | "String" => Ok(ObjectPreviewType::String), + "number" | "Number" => Ok(ObjectPreviewType::Number), + "boolean" | "Boolean" => Ok(ObjectPreviewType::Boolean), + "symbol" | "Symbol" => Ok(ObjectPreviewType::Symbol), + "bigint" | "Bigint" => Ok(ObjectPreviewType::Bigint), + _ => Err(s.to_string()), + } + } + } + #[doc = "Object subtype hint. Specified for `object` type values only."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum ObjectPreviewSubtype { + #[serde(rename = "array")] + Array, + #[serde(rename = "null")] + Null, + #[serde(rename = "node")] + Node, + #[serde(rename = "regexp")] + Regexp, + #[serde(rename = "date")] + Date, + #[serde(rename = "map")] + Map, + #[serde(rename = "set")] + Set, + #[serde(rename = "weakmap")] + Weakmap, + #[serde(rename = "weakset")] + Weakset, + #[serde(rename = "iterator")] + Iterator, + #[serde(rename = "generator")] + Generator, + #[serde(rename = "error")] + Error, + #[serde(rename = "proxy")] + Proxy, + #[serde(rename = "promise")] + Promise, + #[serde(rename = "typedarray")] + Typedarray, + #[serde(rename = "arraybuffer")] + Arraybuffer, + #[serde(rename = "dataview")] + Dataview, + #[serde(rename = "webassemblymemory")] + Webassemblymemory, + #[serde(rename = "wasmvalue")] + Wasmvalue, + #[doc = "blink's subtypes."] + #[serde(rename = "trustedtype")] + Trustedtype, + } + impl AsRef for ObjectPreviewSubtype { + fn as_ref(&self) -> &str { + match self { + ObjectPreviewSubtype::Array => "array", + ObjectPreviewSubtype::Null => "null", + ObjectPreviewSubtype::Node => "node", + ObjectPreviewSubtype::Regexp => "regexp", + ObjectPreviewSubtype::Date => "date", + ObjectPreviewSubtype::Map => "map", + ObjectPreviewSubtype::Set => "set", + ObjectPreviewSubtype::Weakmap => "weakmap", + ObjectPreviewSubtype::Weakset => "weakset", + ObjectPreviewSubtype::Iterator => "iterator", + ObjectPreviewSubtype::Generator => "generator", + ObjectPreviewSubtype::Error => "error", + ObjectPreviewSubtype::Proxy => "proxy", + ObjectPreviewSubtype::Promise => "promise", + ObjectPreviewSubtype::Typedarray => "typedarray", + ObjectPreviewSubtype::Arraybuffer => "arraybuffer", + ObjectPreviewSubtype::Dataview => "dataview", + ObjectPreviewSubtype::Webassemblymemory => "webassemblymemory", + ObjectPreviewSubtype::Wasmvalue => "wasmvalue", + ObjectPreviewSubtype::Trustedtype => "trustedtype", + } + } + } + impl ::std::str::FromStr for ObjectPreviewSubtype { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "array" | "Array" => Ok(ObjectPreviewSubtype::Array), + "null" | "Null" => Ok(ObjectPreviewSubtype::Null), + "node" | "Node" => Ok(ObjectPreviewSubtype::Node), + "regexp" | "Regexp" => Ok(ObjectPreviewSubtype::Regexp), + "date" | "Date" => Ok(ObjectPreviewSubtype::Date), + "map" | "Map" => Ok(ObjectPreviewSubtype::Map), + "set" | "Set" => Ok(ObjectPreviewSubtype::Set), + "weakmap" | "Weakmap" => Ok(ObjectPreviewSubtype::Weakmap), + "weakset" | "Weakset" => Ok(ObjectPreviewSubtype::Weakset), + "iterator" | "Iterator" => Ok(ObjectPreviewSubtype::Iterator), + "generator" | "Generator" => Ok(ObjectPreviewSubtype::Generator), + "error" | "Error" => Ok(ObjectPreviewSubtype::Error), + "proxy" | "Proxy" => Ok(ObjectPreviewSubtype::Proxy), + "promise" | "Promise" => Ok(ObjectPreviewSubtype::Promise), + "typedarray" | "Typedarray" => Ok(ObjectPreviewSubtype::Typedarray), + "arraybuffer" | "Arraybuffer" => Ok(ObjectPreviewSubtype::Arraybuffer), + "dataview" | "Dataview" => Ok(ObjectPreviewSubtype::Dataview), + "webassemblymemory" | "Webassemblymemory" => { + Ok(ObjectPreviewSubtype::Webassemblymemory) + } + "wasmvalue" | "Wasmvalue" => Ok(ObjectPreviewSubtype::Wasmvalue), + "trustedtype" | "Trustedtype" => Ok(ObjectPreviewSubtype::Trustedtype), + _ => Err(s.to_string()), + } + } + } + impl ObjectPreview { + pub fn new( + r#type: impl Into, + overflow: impl Into, + properties: Vec, + ) -> Self { + Self { + r#type: r#type.into(), + overflow: overflow.into(), + properties, + subtype: None, + description: None, + entries: None, + } + } + } + impl ObjectPreview { + pub fn builder() -> ObjectPreviewBuilder { + ObjectPreviewBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ObjectPreviewBuilder { + r#type: Option, + subtype: Option, + description: Option, + overflow: Option, + properties: Option>, + entries: Option>, + } + impl ObjectPreviewBuilder { + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn subtype(mut self, subtype: impl Into) -> Self { + self.subtype = Some(subtype.into()); + self + } + pub fn description(mut self, description: impl Into) -> Self { + self.description = Some(description.into()); + self + } + pub fn overflow(mut self, overflow: impl Into) -> Self { + self.overflow = Some(overflow.into()); + self + } + pub fn propertie(mut self, propertie: impl Into) -> Self { + let v = self.properties.get_or_insert(Vec::new()); + v.push(propertie.into()); + self + } + pub fn properties(mut self, properties: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.properties.get_or_insert(Vec::new()); + for val in properties { + v.push(val.into()); + } + self + } + pub fn entrie(mut self, entrie: impl Into) -> Self { + let v = self.entries.get_or_insert(Vec::new()); + v.push(entrie.into()); + self + } + pub fn entries(mut self, entries: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.entries.get_or_insert(Vec::new()); + for val in entries { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(ObjectPreview { + r#type: self.r#type.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(r#type)) + })?, + subtype: self.subtype, + description: self.description, + overflow: self.overflow.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(overflow)) + })?, + properties: self.properties.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(properties)) + })?, + entries: self.entries, + }) + } + } + impl ObjectPreview { + pub const IDENTIFIER: &'static str = "Runtime.ObjectPreview"; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct PropertyPreview { + #[doc = "Property name."] + #[serde(rename = "name")] + pub name: String, + #[doc = "Object type. Accessor means that the property itself is an accessor property."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: PropertyPreviewType, + #[doc = "User-friendly property value string."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[doc = "Nested value preview."] + #[serde(rename = "valuePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value_preview: Option, + #[doc = "Object subtype hint. Specified for `object` type values only."] + #[serde(rename = "subtype")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(deserialize_with = "super::super::de::deserialize_from_str_optional")] + pub subtype: Option, + } + #[doc = "Object type. Accessor means that the property itself is an accessor property."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum PropertyPreviewType { + #[serde(rename = "object")] + Object, + #[serde(rename = "function")] + Function, + #[serde(rename = "undefined")] + Undefined, + #[serde(rename = "string")] + String, + #[serde(rename = "number")] + Number, + #[serde(rename = "boolean")] + Boolean, + #[serde(rename = "symbol")] + Symbol, + #[serde(rename = "accessor")] + Accessor, + #[serde(rename = "bigint")] + Bigint, + } + impl AsRef for PropertyPreviewType { + fn as_ref(&self) -> &str { + match self { + PropertyPreviewType::Object => "object", + PropertyPreviewType::Function => "function", + PropertyPreviewType::Undefined => "undefined", + PropertyPreviewType::String => "string", + PropertyPreviewType::Number => "number", + PropertyPreviewType::Boolean => "boolean", + PropertyPreviewType::Symbol => "symbol", + PropertyPreviewType::Accessor => "accessor", + PropertyPreviewType::Bigint => "bigint", + } + } + } + impl ::std::str::FromStr for PropertyPreviewType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "object" | "Object" => Ok(PropertyPreviewType::Object), + "function" | "Function" => Ok(PropertyPreviewType::Function), + "undefined" | "Undefined" => Ok(PropertyPreviewType::Undefined), + "string" | "String" => Ok(PropertyPreviewType::String), + "number" | "Number" => Ok(PropertyPreviewType::Number), + "boolean" | "Boolean" => Ok(PropertyPreviewType::Boolean), + "symbol" | "Symbol" => Ok(PropertyPreviewType::Symbol), + "accessor" | "Accessor" => Ok(PropertyPreviewType::Accessor), + "bigint" | "Bigint" => Ok(PropertyPreviewType::Bigint), + _ => Err(s.to_string()), + } + } + } + #[doc = "Object subtype hint. Specified for `object` type values only."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum PropertyPreviewSubtype { + #[serde(rename = "array")] + Array, + #[serde(rename = "null")] + Null, + #[serde(rename = "node")] + Node, + #[serde(rename = "regexp")] + Regexp, + #[serde(rename = "date")] + Date, + #[serde(rename = "map")] + Map, + #[serde(rename = "set")] + Set, + #[serde(rename = "weakmap")] + Weakmap, + #[serde(rename = "weakset")] + Weakset, + #[serde(rename = "iterator")] + Iterator, + #[serde(rename = "generator")] + Generator, + #[serde(rename = "error")] + Error, + #[serde(rename = "proxy")] + Proxy, + #[serde(rename = "promise")] + Promise, + #[serde(rename = "typedarray")] + Typedarray, + #[serde(rename = "arraybuffer")] + Arraybuffer, + #[serde(rename = "dataview")] + Dataview, + #[serde(rename = "webassemblymemory")] + Webassemblymemory, + #[serde(rename = "wasmvalue")] + Wasmvalue, + #[doc = "blink's subtypes."] + #[serde(rename = "trustedtype")] + Trustedtype, + } + impl AsRef for PropertyPreviewSubtype { + fn as_ref(&self) -> &str { + match self { + PropertyPreviewSubtype::Array => "array", + PropertyPreviewSubtype::Null => "null", + PropertyPreviewSubtype::Node => "node", + PropertyPreviewSubtype::Regexp => "regexp", + PropertyPreviewSubtype::Date => "date", + PropertyPreviewSubtype::Map => "map", + PropertyPreviewSubtype::Set => "set", + PropertyPreviewSubtype::Weakmap => "weakmap", + PropertyPreviewSubtype::Weakset => "weakset", + PropertyPreviewSubtype::Iterator => "iterator", + PropertyPreviewSubtype::Generator => "generator", + PropertyPreviewSubtype::Error => "error", + PropertyPreviewSubtype::Proxy => "proxy", + PropertyPreviewSubtype::Promise => "promise", + PropertyPreviewSubtype::Typedarray => "typedarray", + PropertyPreviewSubtype::Arraybuffer => "arraybuffer", + PropertyPreviewSubtype::Dataview => "dataview", + PropertyPreviewSubtype::Webassemblymemory => "webassemblymemory", + PropertyPreviewSubtype::Wasmvalue => "wasmvalue", + PropertyPreviewSubtype::Trustedtype => "trustedtype", + } + } + } + impl ::std::str::FromStr for PropertyPreviewSubtype { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "array" | "Array" => Ok(PropertyPreviewSubtype::Array), + "null" | "Null" => Ok(PropertyPreviewSubtype::Null), + "node" | "Node" => Ok(PropertyPreviewSubtype::Node), + "regexp" | "Regexp" => Ok(PropertyPreviewSubtype::Regexp), + "date" | "Date" => Ok(PropertyPreviewSubtype::Date), + "map" | "Map" => Ok(PropertyPreviewSubtype::Map), + "set" | "Set" => Ok(PropertyPreviewSubtype::Set), + "weakmap" | "Weakmap" => Ok(PropertyPreviewSubtype::Weakmap), + "weakset" | "Weakset" => Ok(PropertyPreviewSubtype::Weakset), + "iterator" | "Iterator" => Ok(PropertyPreviewSubtype::Iterator), + "generator" | "Generator" => Ok(PropertyPreviewSubtype::Generator), + "error" | "Error" => Ok(PropertyPreviewSubtype::Error), + "proxy" | "Proxy" => Ok(PropertyPreviewSubtype::Proxy), + "promise" | "Promise" => Ok(PropertyPreviewSubtype::Promise), + "typedarray" | "Typedarray" => Ok(PropertyPreviewSubtype::Typedarray), + "arraybuffer" | "Arraybuffer" => Ok(PropertyPreviewSubtype::Arraybuffer), + "dataview" | "Dataview" => Ok(PropertyPreviewSubtype::Dataview), + "webassemblymemory" | "Webassemblymemory" => { + Ok(PropertyPreviewSubtype::Webassemblymemory) + } + "wasmvalue" | "Wasmvalue" => Ok(PropertyPreviewSubtype::Wasmvalue), + "trustedtype" | "Trustedtype" => Ok(PropertyPreviewSubtype::Trustedtype), + _ => Err(s.to_string()), + } + } + } + impl PropertyPreview { + pub fn new(name: impl Into, r#type: impl Into) -> Self { + Self { + name: name.into(), + r#type: r#type.into(), + value: None, + value_preview: None, + subtype: None, + } + } + } + impl PropertyPreview { + pub fn builder() -> PropertyPreviewBuilder { + PropertyPreviewBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct PropertyPreviewBuilder { + name: Option, + r#type: Option, + value: Option, + value_preview: Option, + subtype: Option, + } + impl PropertyPreviewBuilder { + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn r#type(mut self, r#type: impl Into) -> Self { + self.r#type = Some(r#type.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn value_preview(mut self, value_preview: impl Into) -> Self { + self.value_preview = Some(value_preview.into()); + self + } + pub fn subtype(mut self, subtype: impl Into) -> Self { + self.subtype = Some(subtype.into()); + self + } + pub fn build(self) -> Result { + Ok(PropertyPreview { + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + r#type: self.r#type.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(r#type)) + })?, + value: self.value, + value_preview: self.value_preview, + subtype: self.subtype, + }) + } + } + impl PropertyPreview { + pub const IDENTIFIER: &'static str = "Runtime.PropertyPreview"; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EntryPreview { + #[doc = "Preview of the key. Specified for map-like collection entries."] + #[serde(rename = "key")] + #[serde(skip_serializing_if = "Option::is_none")] + pub key: Option, + #[doc = "Preview of the value."] + #[serde(rename = "value")] + pub value: ObjectPreview, + } + impl EntryPreview { + pub fn new(value: impl Into) -> Self { + Self { + value: value.into(), + key: None, + } + } + } + impl EntryPreview { + pub fn builder() -> EntryPreviewBuilder { + EntryPreviewBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EntryPreviewBuilder { + key: Option, + value: Option, + } + impl EntryPreviewBuilder { + pub fn key(mut self, key: impl Into) -> Self { + self.key = Some(key.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn build(self) -> Result { + Ok(EntryPreview { + key: self.key, + value: self.value.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(value)) + })?, + }) + } + } + impl EntryPreview { + pub const IDENTIFIER: &'static str = "Runtime.EntryPreview"; + } + #[doc = "Object property descriptor.\n[PropertyDescriptor](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-PropertyDescriptor)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct PropertyDescriptor { + #[doc = "Property name or symbol description."] + #[serde(rename = "name")] + pub name: String, + #[doc = "The value associated with the property."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[doc = "True if the value associated with the property may be changed (data descriptors only)."] + #[serde(rename = "writable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub writable: Option, + #[doc = "A function which serves as a getter for the property, or `undefined` if there is no getter\n(accessor descriptors only)."] + #[serde(rename = "get")] + #[serde(skip_serializing_if = "Option::is_none")] + pub get: Option, + #[doc = "A function which serves as a setter for the property, or `undefined` if there is no setter\n(accessor descriptors only)."] + #[serde(rename = "set")] + #[serde(skip_serializing_if = "Option::is_none")] + pub set: Option, + #[doc = "True if the type of this property descriptor may be changed and if the property may be\ndeleted from the corresponding object."] + #[serde(rename = "configurable")] + pub configurable: bool, + #[doc = "True if this property shows up during enumeration of the properties on the corresponding\nobject."] + #[serde(rename = "enumerable")] + pub enumerable: bool, + #[doc = "True if the result was thrown during the evaluation."] + #[serde(rename = "wasThrown")] + #[serde(skip_serializing_if = "Option::is_none")] + pub was_thrown: Option, + #[doc = "True if the property is owned for the object."] + #[serde(rename = "isOwn")] + #[serde(skip_serializing_if = "Option::is_none")] + pub is_own: Option, + #[doc = "Property symbol object, if the property is of the `symbol` type."] + #[serde(rename = "symbol")] + #[serde(skip_serializing_if = "Option::is_none")] + pub symbol: Option, + } + impl PropertyDescriptor { + pub fn new( + name: impl Into, + configurable: impl Into, + enumerable: impl Into, + ) -> Self { + Self { + name: name.into(), + configurable: configurable.into(), + enumerable: enumerable.into(), + value: None, + writable: None, + get: None, + set: None, + was_thrown: None, + is_own: None, + symbol: None, + } + } + } + impl PropertyDescriptor { + pub fn builder() -> PropertyDescriptorBuilder { + PropertyDescriptorBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct PropertyDescriptorBuilder { + name: Option, + value: Option, + writable: Option, + get: Option, + set: Option, + configurable: Option, + enumerable: Option, + was_thrown: Option, + is_own: Option, + symbol: Option, + } + impl PropertyDescriptorBuilder { + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn writable(mut self, writable: impl Into) -> Self { + self.writable = Some(writable.into()); + self + } + pub fn get(mut self, get: impl Into) -> Self { + self.get = Some(get.into()); + self + } + pub fn set(mut self, set: impl Into) -> Self { + self.set = Some(set.into()); + self + } + pub fn configurable(mut self, configurable: impl Into) -> Self { + self.configurable = Some(configurable.into()); + self + } + pub fn enumerable(mut self, enumerable: impl Into) -> Self { + self.enumerable = Some(enumerable.into()); + self + } + pub fn was_thrown(mut self, was_thrown: impl Into) -> Self { + self.was_thrown = Some(was_thrown.into()); + self + } + pub fn is_own(mut self, is_own: impl Into) -> Self { + self.is_own = Some(is_own.into()); + self + } + pub fn symbol(mut self, symbol: impl Into) -> Self { + self.symbol = Some(symbol.into()); + self + } + pub fn build(self) -> Result { + Ok(PropertyDescriptor { + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + value: self.value, + writable: self.writable, + get: self.get, + set: self.set, + configurable: self.configurable.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(configurable)) + })?, + enumerable: self.enumerable.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(enumerable)) + })?, + was_thrown: self.was_thrown, + is_own: self.is_own, + symbol: self.symbol, + }) + } + } + impl PropertyDescriptor { + pub const IDENTIFIER: &'static str = "Runtime.PropertyDescriptor"; + } + #[doc = "Object internal property descriptor. This property isn't normally visible in JavaScript code.\n[InternalPropertyDescriptor](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-InternalPropertyDescriptor)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct InternalPropertyDescriptor { + #[doc = "Conventional property name."] + #[serde(rename = "name")] + pub name: String, + #[doc = "The value associated with the property."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + } + impl InternalPropertyDescriptor { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + value: None, + } + } + } + impl> From for InternalPropertyDescriptor { + fn from(url: T) -> Self { + InternalPropertyDescriptor::new(url) + } + } + impl InternalPropertyDescriptor { + pub fn builder() -> InternalPropertyDescriptorBuilder { + InternalPropertyDescriptorBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct InternalPropertyDescriptorBuilder { + name: Option, + value: Option, + } + impl InternalPropertyDescriptorBuilder { + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn build(self) -> Result { + Ok(InternalPropertyDescriptor { + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + value: self.value, + }) + } + } + impl InternalPropertyDescriptor { + pub const IDENTIFIER: &'static str = "Runtime.InternalPropertyDescriptor"; + } + #[doc = "Object private field descriptor.\n[PrivatePropertyDescriptor](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-PrivatePropertyDescriptor)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct PrivatePropertyDescriptor { + #[doc = "Private property name."] + #[serde(rename = "name")] + pub name: String, + #[doc = "The value associated with the private property."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[doc = "A function which serves as a getter for the private property,\nor `undefined` if there is no getter (accessor descriptors only)."] + #[serde(rename = "get")] + #[serde(skip_serializing_if = "Option::is_none")] + pub get: Option, + #[doc = "A function which serves as a setter for the private property,\nor `undefined` if there is no setter (accessor descriptors only)."] + #[serde(rename = "set")] + #[serde(skip_serializing_if = "Option::is_none")] + pub set: Option, + } + impl PrivatePropertyDescriptor { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + value: None, + get: None, + set: None, + } + } + } + impl> From for PrivatePropertyDescriptor { + fn from(url: T) -> Self { + PrivatePropertyDescriptor::new(url) + } + } + impl PrivatePropertyDescriptor { + pub fn builder() -> PrivatePropertyDescriptorBuilder { + PrivatePropertyDescriptorBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct PrivatePropertyDescriptorBuilder { + name: Option, + value: Option, + get: Option, + set: Option, + } + impl PrivatePropertyDescriptorBuilder { + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn get(mut self, get: impl Into) -> Self { + self.get = Some(get.into()); + self + } + pub fn set(mut self, set: impl Into) -> Self { + self.set = Some(set.into()); + self + } + pub fn build(self) -> Result { + Ok(PrivatePropertyDescriptor { + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + value: self.value, + get: self.get, + set: self.set, + }) + } + } + impl PrivatePropertyDescriptor { + pub const IDENTIFIER: &'static str = "Runtime.PrivatePropertyDescriptor"; + } + #[doc = "Represents function call argument. Either remote object id `objectId`, primitive `value`,\nunserializable primitive value or neither of (for undefined) them should be specified.\n[CallArgument](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-CallArgument)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct CallArgument { + #[doc = "Primitive value or serializable javascript object."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[doc = "Primitive value which can not be JSON-stringified."] + #[serde(rename = "unserializableValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub unserializable_value: Option, + #[doc = "Remote object handle."] + #[serde(rename = "objectId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_id: Option, + } + impl CallArgument { + pub fn builder() -> CallArgumentBuilder { + CallArgumentBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CallArgumentBuilder { + value: Option, + unserializable_value: Option, + object_id: Option, + } + impl CallArgumentBuilder { + pub fn value(mut self, value: impl Into) -> Self { + self.value = Some(value.into()); + self + } + pub fn unserializable_value( + mut self, + unserializable_value: impl Into, + ) -> Self { + self.unserializable_value = Some(unserializable_value.into()); + self + } + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn build(self) -> CallArgument { + CallArgument { + value: self.value, + unserializable_value: self.unserializable_value, + object_id: self.object_id, + } + } + } + impl CallArgument { + pub const IDENTIFIER: &'static str = "Runtime.CallArgument"; + } + #[doc = "Id of an execution context.\n[ExecutionContextId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExecutionContextId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Copy, Hash)] + pub struct ExecutionContextId(i64); + impl ExecutionContextId { + pub fn new(val: impl Into) -> Self { + ExecutionContextId(val.into()) + } + pub fn inner(&self) -> &i64 { + &self.0 + } + } + impl ExecutionContextId { + pub const IDENTIFIER: &'static str = "Runtime.ExecutionContextId"; + } + #[doc = "Description of an isolated world.\n[ExecutionContextDescription](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExecutionContextDescription)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ExecutionContextDescription { + #[doc = "Unique id of the execution context. It can be used to specify in which execution context\nscript evaluation should be performed."] + #[serde(rename = "id")] + pub id: ExecutionContextId, + #[doc = "Execution context origin."] + #[serde(rename = "origin")] + pub origin: String, + #[doc = "Human readable name describing given context."] + #[serde(rename = "name")] + pub name: String, + #[doc = "A system-unique execution context identifier. Unlike the id, this is unique across\nmultiple processes, so can be reliably used to identify specific context while backend\nperforms a cross-process navigation."] + #[serde(rename = "uniqueId")] + pub unique_id: String, + #[doc = "Embedder-specific auxiliary data likely matching {isDefault: boolean, type: 'default'|'isolated'|'worker', frameId: string}"] + #[serde(rename = "auxData")] + #[serde(skip_serializing_if = "Option::is_none")] + pub aux_data: Option, + } + impl ExecutionContextDescription { + pub fn new( + id: impl Into, + origin: impl Into, + name: impl Into, + unique_id: impl Into, + ) -> Self { + Self { + id: id.into(), + origin: origin.into(), + name: name.into(), + unique_id: unique_id.into(), + aux_data: None, + } + } + } + impl ExecutionContextDescription { + pub fn builder() -> ExecutionContextDescriptionBuilder { + ExecutionContextDescriptionBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ExecutionContextDescriptionBuilder { + id: Option, + origin: Option, + name: Option, + unique_id: Option, + aux_data: Option, + } + impl ExecutionContextDescriptionBuilder { + pub fn id(mut self, id: impl Into) -> Self { + self.id = Some(id.into()); + self + } + pub fn origin(mut self, origin: impl Into) -> Self { + self.origin = Some(origin.into()); + self + } + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn unique_id(mut self, unique_id: impl Into) -> Self { + self.unique_id = Some(unique_id.into()); + self + } + pub fn aux_data(mut self, aux_data: impl Into) -> Self { + self.aux_data = Some(aux_data.into()); + self + } + pub fn build(self) -> Result { + Ok(ExecutionContextDescription { + id: self + .id + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(id)))?, + origin: self.origin.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(origin)) + })?, + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + unique_id: self.unique_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(unique_id)) + })?, + aux_data: self.aux_data, + }) + } + } + impl ExecutionContextDescription { + pub const IDENTIFIER: &'static str = "Runtime.ExecutionContextDescription"; + } + #[doc = "Detailed information about exception (or error) that was thrown during script compilation or\nexecution.\n[ExceptionDetails](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExceptionDetails)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ExceptionDetails { + #[doc = "Exception id."] + #[serde(rename = "exceptionId")] + pub exception_id: i64, + #[doc = "Exception text, which should be used together with exception object when available."] + #[serde(rename = "text")] + pub text: String, + #[doc = "Line number of the exception location (0-based)."] + #[serde(rename = "lineNumber")] + pub line_number: i64, + #[doc = "Column number of the exception location (0-based)."] + #[serde(rename = "columnNumber")] + pub column_number: i64, + #[doc = "Script ID of the exception location."] + #[serde(rename = "scriptId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub script_id: Option, + #[doc = "URL of the exception location, to be used when the script was not reported."] + #[serde(rename = "url")] + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + #[doc = "JavaScript stack trace if available."] + #[serde(rename = "stackTrace")] + #[serde(skip_serializing_if = "Option::is_none")] + pub stack_trace: Option, + #[doc = "Exception object if available."] + #[serde(rename = "exception")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception: Option, + #[doc = "Identifier of the context where exception happened."] + #[serde(rename = "executionContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + #[doc = "Dictionary with entries of meta data that the client associated\nwith this exception, such as information about associated network\nrequests, etc."] + #[serde(rename = "exceptionMetaData")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_meta_data: Option, + } + impl ExceptionDetails { + pub fn new( + exception_id: impl Into, + text: impl Into, + line_number: impl Into, + column_number: impl Into, + ) -> Self { + Self { + exception_id: exception_id.into(), + text: text.into(), + line_number: line_number.into(), + column_number: column_number.into(), + script_id: None, + url: None, + stack_trace: None, + exception: None, + execution_context_id: None, + exception_meta_data: None, + } + } + } + impl ExceptionDetails { + pub fn builder() -> ExceptionDetailsBuilder { + ExceptionDetailsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ExceptionDetailsBuilder { + exception_id: Option, + text: Option, + line_number: Option, + column_number: Option, + script_id: Option, + url: Option, + stack_trace: Option, + exception: Option, + execution_context_id: Option, + exception_meta_data: Option, + } + impl ExceptionDetailsBuilder { + pub fn exception_id(mut self, exception_id: impl Into) -> Self { + self.exception_id = Some(exception_id.into()); + self + } + pub fn text(mut self, text: impl Into) -> Self { + self.text = Some(text.into()); + self + } + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn column_number(mut self, column_number: impl Into) -> Self { + self.column_number = Some(column_number.into()); + self + } + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn url(mut self, url: impl Into) -> Self { + self.url = Some(url.into()); + self + } + pub fn stack_trace(mut self, stack_trace: impl Into) -> Self { + self.stack_trace = Some(stack_trace.into()); + self + } + pub fn exception(mut self, exception: impl Into) -> Self { + self.exception = Some(exception.into()); + self + } + pub fn execution_context_id( + mut self, + execution_context_id: impl Into, + ) -> Self { + self.execution_context_id = Some(execution_context_id.into()); + self + } + pub fn exception_meta_data( + mut self, + exception_meta_data: impl Into, + ) -> Self { + self.exception_meta_data = Some(exception_meta_data.into()); + self + } + pub fn build(self) -> Result { + Ok(ExceptionDetails { + exception_id: self.exception_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(exception_id)) + })?, + text: self.text.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(text)) + })?, + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + column_number: self.column_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(column_number)) + })?, + script_id: self.script_id, + url: self.url, + stack_trace: self.stack_trace, + exception: self.exception, + execution_context_id: self.execution_context_id, + exception_meta_data: self.exception_meta_data, + }) + } + } + impl ExceptionDetails { + pub const IDENTIFIER: &'static str = "Runtime.ExceptionDetails"; + } + #[doc = "Number of milliseconds since epoch.\n[Timestamp](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-Timestamp)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct Timestamp(f64); + impl Timestamp { + pub fn new(val: impl Into) -> Self { + Timestamp(val.into()) + } + pub fn inner(&self) -> &f64 { + &self.0 + } + } + impl Timestamp { + pub const IDENTIFIER: &'static str = "Runtime.Timestamp"; + } + #[doc = "Number of milliseconds.\n[TimeDelta](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-TimeDelta)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct TimeDelta(f64); + impl TimeDelta { + pub fn new(val: impl Into) -> Self { + TimeDelta(val.into()) + } + pub fn inner(&self) -> &f64 { + &self.0 + } + } + impl TimeDelta { + pub const IDENTIFIER: &'static str = "Runtime.TimeDelta"; + } + #[doc = "Stack entry for runtime errors and assertions.\n[CallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-CallFrame)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CallFrame { + #[doc = "JavaScript function name."] + #[serde(rename = "functionName")] + pub function_name: String, + #[doc = "JavaScript script id."] + #[serde(rename = "scriptId")] + pub script_id: ScriptId, + #[doc = "JavaScript script name or url."] + #[serde(rename = "url")] + pub url: String, + #[doc = "JavaScript script line number (0-based)."] + #[serde(rename = "lineNumber")] + pub line_number: i64, + #[doc = "JavaScript script column number (0-based)."] + #[serde(rename = "columnNumber")] + pub column_number: i64, + } + impl CallFrame { + pub fn builder() -> CallFrameBuilder { + CallFrameBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CallFrameBuilder { + function_name: Option, + script_id: Option, + url: Option, + line_number: Option, + column_number: Option, + } + impl CallFrameBuilder { + pub fn function_name(mut self, function_name: impl Into) -> Self { + self.function_name = Some(function_name.into()); + self + } + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn url(mut self, url: impl Into) -> Self { + self.url = Some(url.into()); + self + } + pub fn line_number(mut self, line_number: impl Into) -> Self { + self.line_number = Some(line_number.into()); + self + } + pub fn column_number(mut self, column_number: impl Into) -> Self { + self.column_number = Some(column_number.into()); + self + } + pub fn build(self) -> Result { + Ok(CallFrame { + function_name: self.function_name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(function_name)) + })?, + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + url: self + .url + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(url)))?, + line_number: self.line_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(line_number)) + })?, + column_number: self.column_number.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(column_number)) + })?, + }) + } + } + impl CallFrame { + pub const IDENTIFIER: &'static str = "Runtime.CallFrame"; + } + #[doc = "Call frames for assertions or error messages.\n[StackTrace](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-StackTrace)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct StackTrace { + #[doc = "String label of this stack trace. For async traces this may be a name of the function that\ninitiated the async call."] + #[serde(rename = "description")] + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[doc = "JavaScript function name."] + #[serde(rename = "callFrames")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub call_frames: Vec, + #[doc = "Asynchronous JavaScript stack trace that preceded this stack, if available."] + #[serde(rename = "parent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub parent: Option>, + #[doc = "Asynchronous JavaScript stack trace that preceded this stack, if available."] + #[serde(rename = "parentId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub parent_id: Option, + } + impl StackTrace { + pub fn new(call_frames: Vec) -> Self { + Self { + call_frames, + description: None, + parent: None, + parent_id: None, + } + } + } + impl StackTrace { + pub fn builder() -> StackTraceBuilder { + StackTraceBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StackTraceBuilder { + description: Option, + call_frames: Option>, + parent: Option, + parent_id: Option, + } + impl StackTraceBuilder { + pub fn description(mut self, description: impl Into) -> Self { + self.description = Some(description.into()); + self + } + pub fn call_frame(mut self, call_frame: impl Into) -> Self { + let v = self.call_frames.get_or_insert(Vec::new()); + v.push(call_frame.into()); + self + } + pub fn call_frames(mut self, call_frames: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.call_frames.get_or_insert(Vec::new()); + for val in call_frames { + v.push(val.into()); + } + self + } + pub fn parent(mut self, parent: impl Into) -> Self { + self.parent = Some(parent.into()); + self + } + pub fn parent_id(mut self, parent_id: impl Into) -> Self { + self.parent_id = Some(parent_id.into()); + self + } + pub fn build(self) -> Result { + Ok(StackTrace { + description: self.description, + call_frames: self.call_frames.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(call_frames)) + })?, + parent: self.parent.map(Box::new), + parent_id: self.parent_id, + }) + } + } + impl StackTrace { + pub const IDENTIFIER: &'static str = "Runtime.StackTrace"; + } + #[doc = "Unique identifier of current debugger.\n[UniqueDebuggerId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-UniqueDebuggerId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct UniqueDebuggerId(String); + impl UniqueDebuggerId { + pub fn new(val: impl Into) -> Self { + UniqueDebuggerId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for UniqueDebuggerId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: UniqueDebuggerId) -> String { + el.0 + } + } + impl From for UniqueDebuggerId { + fn from(expr: String) -> Self { + UniqueDebuggerId(expr) + } + } + impl std::borrow::Borrow for UniqueDebuggerId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl UniqueDebuggerId { + pub const IDENTIFIER: &'static str = "Runtime.UniqueDebuggerId"; + } + #[doc = "If `debuggerId` is set stack trace comes from another debugger and can be resolved there. This\nallows to track cross-debugger calls. See `Runtime.StackTrace` and `Debugger.paused` for usages.\n[StackTraceId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-StackTraceId)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct StackTraceId { + #[serde(rename = "id")] + pub id: String, + #[serde(rename = "debuggerId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub debugger_id: Option, + } + impl StackTraceId { + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + debugger_id: None, + } + } + } + impl> From for StackTraceId { + fn from(url: T) -> Self { + StackTraceId::new(url) + } + } + impl StackTraceId { + pub fn builder() -> StackTraceIdBuilder { + StackTraceIdBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct StackTraceIdBuilder { + id: Option, + debugger_id: Option, + } + impl StackTraceIdBuilder { + pub fn id(mut self, id: impl Into) -> Self { + self.id = Some(id.into()); + self + } + pub fn debugger_id(mut self, debugger_id: impl Into) -> Self { + self.debugger_id = Some(debugger_id.into()); + self + } + pub fn build(self) -> Result { + Ok(StackTraceId { + id: self + .id + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(id)))?, + debugger_id: self.debugger_id, + }) + } + } + impl StackTraceId { + pub const IDENTIFIER: &'static str = "Runtime.StackTraceId"; + } + #[doc = "Add handler to promise with given promise object id.\n[awaitPromise](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-awaitPromise)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct AwaitPromiseParams { + #[doc = "Identifier of the promise."] + #[serde(rename = "promiseObjectId")] + pub promise_object_id: RemoteObjectId, + #[doc = "Whether the result is expected to be a JSON object that should be sent by value."] + #[serde(rename = "returnByValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[doc = "Whether preview should be generated for the result."] + #[serde(rename = "generatePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + } + impl AwaitPromiseParams { + pub fn new(promise_object_id: impl Into) -> Self { + Self { + promise_object_id: promise_object_id.into(), + return_by_value: None, + generate_preview: None, + } + } + } + impl AwaitPromiseParams { + pub fn builder() -> AwaitPromiseParamsBuilder { + AwaitPromiseParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct AwaitPromiseParamsBuilder { + promise_object_id: Option, + return_by_value: Option, + generate_preview: Option, + } + impl AwaitPromiseParamsBuilder { + pub fn promise_object_id( + mut self, + promise_object_id: impl Into, + ) -> Self { + self.promise_object_id = Some(promise_object_id.into()); + self + } + pub fn return_by_value(mut self, return_by_value: impl Into) -> Self { + self.return_by_value = Some(return_by_value.into()); + self + } + pub fn generate_preview(mut self, generate_preview: impl Into) -> Self { + self.generate_preview = Some(generate_preview.into()); + self + } + pub fn build(self) -> Result { + Ok(AwaitPromiseParams { + promise_object_id: self.promise_object_id.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(promise_object_id) + ) + })?, + return_by_value: self.return_by_value, + generate_preview: self.generate_preview, + }) + } + } + impl AwaitPromiseParams { + pub const IDENTIFIER: &'static str = "Runtime.awaitPromise"; + } + impl chromiumoxide_types::Method for AwaitPromiseParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for AwaitPromiseParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Add handler to promise with given promise object id.\n[awaitPromise](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-awaitPromise)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct AwaitPromiseReturns { + #[doc = "Promise result. Will contain rejected value if promise was rejected."] + #[serde(rename = "result")] + pub result: RemoteObject, + #[doc = "Exception details if stack strace is available."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl AwaitPromiseReturns { + pub fn new(result: impl Into) -> Self { + Self { + result: result.into(), + exception_details: None, + } + } + } + impl AwaitPromiseReturns { + pub fn builder() -> AwaitPromiseReturnsBuilder { + AwaitPromiseReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct AwaitPromiseReturnsBuilder { + result: Option, + exception_details: Option, + } + impl AwaitPromiseReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + self.result = Some(result.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> Result { + Ok(AwaitPromiseReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + exception_details: self.exception_details, + }) + } + } + impl chromiumoxide_types::Command for AwaitPromiseParams { + type Response = AwaitPromiseReturns; + } + #[doc = "Calls function with given declaration on the given object. Object group of the result is\ninherited from the target object.\n[callFunctionOn](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-callFunctionOn)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CallFunctionOnParams { + #[doc = "Declaration of the function to call."] + #[serde(rename = "functionDeclaration")] + pub function_declaration: String, + #[doc = "Identifier of the object to call function on. Either objectId or executionContextId should\nbe specified."] + #[serde(rename = "objectId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_id: Option, + #[doc = "Call arguments. All call arguments must belong to the same JavaScript world as the target\nobject."] + #[serde(rename = "arguments")] + #[serde(skip_serializing_if = "Option::is_none")] + pub arguments: Option>, + #[doc = "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state."] + #[serde(rename = "silent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[doc = "Whether the result is expected to be a JSON object which should be sent by value.\nCan be overriden by `serializationOptions`."] + #[serde(rename = "returnByValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[doc = "Whether preview should be generated for the result."] + #[serde(rename = "generatePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[doc = "Whether execution should be treated as initiated by user in the UI."] + #[serde(rename = "userGesture")] + #[serde(skip_serializing_if = "Option::is_none")] + pub user_gesture: Option, + #[doc = "Whether execution should `await` for resulting value and return once awaited promise is\nresolved."] + #[serde(rename = "awaitPromise")] + #[serde(skip_serializing_if = "Option::is_none")] + pub await_promise: Option, + #[doc = "Specifies execution context which global object will be used to call function on. Either\nexecutionContextId or objectId should be specified."] + #[serde(rename = "executionContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + #[doc = "Symbolic group name that can be used to release multiple objects. If objectGroup is not\nspecified and objectId is, objectGroup will be inherited from object."] + #[serde(rename = "objectGroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[doc = "Whether to throw an exception if side effect cannot be ruled out during evaluation."] + #[serde(rename = "throwOnSideEffect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub throw_on_side_effect: Option, + #[doc = "An alternative way to specify the execution context to call function on.\nCompared to contextId that may be reused across processes, this is guaranteed to be\nsystem-unique, so it can be used to prevent accidental function call\nin context different than intended (e.g. as a result of navigation across process\nboundaries).\nThis is mutually exclusive with `executionContextId`."] + #[serde(rename = "uniqueContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub unique_context_id: Option, + #[doc = "Specifies the result serialization. If provided, overrides\n`generatePreview` and `returnByValue`."] + #[serde(rename = "serializationOptions")] + #[serde(skip_serializing_if = "Option::is_none")] + pub serialization_options: Option, + } + impl CallFunctionOnParams { + pub fn new(function_declaration: impl Into) -> Self { + Self { + function_declaration: function_declaration.into(), + object_id: None, + arguments: None, + silent: None, + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + execution_context_id: None, + object_group: None, + throw_on_side_effect: None, + unique_context_id: None, + serialization_options: None, + } + } + } + impl> From for CallFunctionOnParams { + fn from(url: T) -> Self { + CallFunctionOnParams::new(url) + } + } + impl CallFunctionOnParams { + pub fn builder() -> CallFunctionOnParamsBuilder { + CallFunctionOnParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CallFunctionOnParamsBuilder { + function_declaration: Option, + object_id: Option, + arguments: Option>, + silent: Option, + return_by_value: Option, + generate_preview: Option, + user_gesture: Option, + await_promise: Option, + execution_context_id: Option, + object_group: Option, + throw_on_side_effect: Option, + unique_context_id: Option, + serialization_options: Option, + } + impl CallFunctionOnParamsBuilder { + pub fn function_declaration(mut self, function_declaration: impl Into) -> Self { + self.function_declaration = Some(function_declaration.into()); + self + } + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn argument(mut self, argument: impl Into) -> Self { + let v = self.arguments.get_or_insert(Vec::new()); + v.push(argument.into()); + self + } + pub fn arguments(mut self, arguments: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.arguments.get_or_insert(Vec::new()); + for val in arguments { + v.push(val.into()); + } + self + } + pub fn silent(mut self, silent: impl Into) -> Self { + self.silent = Some(silent.into()); + self + } + pub fn return_by_value(mut self, return_by_value: impl Into) -> Self { + self.return_by_value = Some(return_by_value.into()); + self + } + pub fn generate_preview(mut self, generate_preview: impl Into) -> Self { + self.generate_preview = Some(generate_preview.into()); + self + } + pub fn user_gesture(mut self, user_gesture: impl Into) -> Self { + self.user_gesture = Some(user_gesture.into()); + self + } + pub fn await_promise(mut self, await_promise: impl Into) -> Self { + self.await_promise = Some(await_promise.into()); + self + } + pub fn execution_context_id( + mut self, + execution_context_id: impl Into, + ) -> Self { + self.execution_context_id = Some(execution_context_id.into()); + self + } + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn throw_on_side_effect(mut self, throw_on_side_effect: impl Into) -> Self { + self.throw_on_side_effect = Some(throw_on_side_effect.into()); + self + } + pub fn unique_context_id(mut self, unique_context_id: impl Into) -> Self { + self.unique_context_id = Some(unique_context_id.into()); + self + } + pub fn serialization_options( + mut self, + serialization_options: impl Into, + ) -> Self { + self.serialization_options = Some(serialization_options.into()); + self + } + pub fn build(self) -> Result { + Ok(CallFunctionOnParams { + function_declaration: self.function_declaration.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(function_declaration) + ) + })?, + object_id: self.object_id, + arguments: self.arguments, + silent: self.silent, + return_by_value: self.return_by_value, + generate_preview: self.generate_preview, + user_gesture: self.user_gesture, + await_promise: self.await_promise, + execution_context_id: self.execution_context_id, + object_group: self.object_group, + throw_on_side_effect: self.throw_on_side_effect, + unique_context_id: self.unique_context_id, + serialization_options: self.serialization_options, + }) + } + } + impl CallFunctionOnParams { + pub const IDENTIFIER: &'static str = "Runtime.callFunctionOn"; + } + impl chromiumoxide_types::Method for CallFunctionOnParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for CallFunctionOnParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Calls function with given declaration on the given object. Object group of the result is\ninherited from the target object.\n[callFunctionOn](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-callFunctionOn)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CallFunctionOnReturns { + #[doc = "Call result."] + #[serde(rename = "result")] + pub result: RemoteObject, + #[doc = "Exception details."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl CallFunctionOnReturns { + pub fn new(result: impl Into) -> Self { + Self { + result: result.into(), + exception_details: None, + } + } + } + impl CallFunctionOnReturns { + pub fn builder() -> CallFunctionOnReturnsBuilder { + CallFunctionOnReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CallFunctionOnReturnsBuilder { + result: Option, + exception_details: Option, + } + impl CallFunctionOnReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + self.result = Some(result.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> Result { + Ok(CallFunctionOnReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + exception_details: self.exception_details, + }) + } + } + impl chromiumoxide_types::Command for CallFunctionOnParams { + type Response = CallFunctionOnReturns; + } + #[doc = "Compiles expression.\n[compileScript](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-compileScript)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct CompileScriptParams { + #[doc = "Expression to compile."] + #[serde(rename = "expression")] + pub expression: String, + #[doc = "Source url to be set for the script."] + #[serde(rename = "sourceURL")] + pub source_url: String, + #[doc = "Specifies whether the compiled script should be persisted."] + #[serde(rename = "persistScript")] + pub persist_script: bool, + #[doc = "Specifies in which execution context to perform script run. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page."] + #[serde(rename = "executionContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + } + impl CompileScriptParams { + pub fn new( + expression: impl Into, + source_url: impl Into, + persist_script: impl Into, + ) -> Self { + Self { + expression: expression.into(), + source_url: source_url.into(), + persist_script: persist_script.into(), + execution_context_id: None, + } + } + } + impl CompileScriptParams { + pub fn builder() -> CompileScriptParamsBuilder { + CompileScriptParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CompileScriptParamsBuilder { + expression: Option, + source_url: Option, + persist_script: Option, + execution_context_id: Option, + } + impl CompileScriptParamsBuilder { + pub fn expression(mut self, expression: impl Into) -> Self { + self.expression = Some(expression.into()); + self + } + pub fn source_url(mut self, source_url: impl Into) -> Self { + self.source_url = Some(source_url.into()); + self + } + pub fn persist_script(mut self, persist_script: impl Into) -> Self { + self.persist_script = Some(persist_script.into()); + self + } + pub fn execution_context_id( + mut self, + execution_context_id: impl Into, + ) -> Self { + self.execution_context_id = Some(execution_context_id.into()); + self + } + pub fn build(self) -> Result { + Ok(CompileScriptParams { + expression: self.expression.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(expression)) + })?, + source_url: self.source_url.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(source_url)) + })?, + persist_script: self.persist_script.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(persist_script)) + })?, + execution_context_id: self.execution_context_id, + }) + } + } + impl CompileScriptParams { + pub const IDENTIFIER: &'static str = "Runtime.compileScript"; + } + impl chromiumoxide_types::Method for CompileScriptParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for CompileScriptParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Compiles expression.\n[compileScript](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-compileScript)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct CompileScriptReturns { + #[doc = "Id of the script."] + #[serde(rename = "scriptId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub script_id: Option, + #[doc = "Exception details."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl CompileScriptReturns { + pub fn builder() -> CompileScriptReturnsBuilder { + CompileScriptReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct CompileScriptReturnsBuilder { + script_id: Option, + exception_details: Option, + } + impl CompileScriptReturnsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> CompileScriptReturns { + CompileScriptReturns { + script_id: self.script_id, + exception_details: self.exception_details, + } + } + } + impl chromiumoxide_types::Command for CompileScriptParams { + type Response = CompileScriptReturns; + } + #[doc = "Disables reporting of execution contexts creation.\n[disable](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-disable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableParams {} + impl DisableParams { + pub const IDENTIFIER: &'static str = "Runtime.disable"; + } + impl chromiumoxide_types::Method for DisableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for DisableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Disables reporting of execution contexts creation.\n[disable](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-disable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DisableReturns {} + impl chromiumoxide_types::Command for DisableParams { + type Response = DisableReturns; + } + #[doc = "Discards collected exceptions and console API calls.\n[discardConsoleEntries](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-discardConsoleEntries)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DiscardConsoleEntriesParams {} + impl DiscardConsoleEntriesParams { + pub const IDENTIFIER: &'static str = "Runtime.discardConsoleEntries"; + } + impl chromiumoxide_types::Method for DiscardConsoleEntriesParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for DiscardConsoleEntriesParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Discards collected exceptions and console API calls.\n[discardConsoleEntries](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-discardConsoleEntries)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct DiscardConsoleEntriesReturns {} + impl chromiumoxide_types::Command for DiscardConsoleEntriesParams { + type Response = DiscardConsoleEntriesReturns; + } + #[doc = "Enables reporting of execution contexts creation by means of `executionContextCreated` event.\nWhen the reporting gets enabled the event will be sent immediately for each existing execution\ncontext.\n[enable](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-enable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableParams {} + impl EnableParams { + pub const IDENTIFIER: &'static str = "Runtime.enable"; + } + impl chromiumoxide_types::Method for EnableParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EnableParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Enables reporting of execution contexts creation by means of `executionContextCreated` event.\nWhen the reporting gets enabled the event will be sent immediately for each existing execution\ncontext.\n[enable](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-enable)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EnableReturns {} + impl chromiumoxide_types::Command for EnableParams { + type Response = EnableReturns; + } + #[doc = "Evaluates expression on global object.\n[evaluate](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EvaluateParams { + #[doc = "Expression to evaluate."] + #[serde(rename = "expression")] + pub expression: String, + #[doc = "Symbolic group name that can be used to release multiple objects."] + #[serde(rename = "objectGroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[doc = "Determines whether Command Line API should be available during the evaluation."] + #[serde(rename = "includeCommandLineAPI")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_command_line_api: Option, + #[doc = "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state."] + #[serde(rename = "silent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[doc = "Specifies in which execution context to perform evaluation. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page.\nThis is mutually exclusive with `uniqueContextId`, which offers an\nalternative way to identify the execution context that is more reliable\nin a multi-process environment."] + #[serde(rename = "contextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub context_id: Option, + #[doc = "Whether the result is expected to be a JSON object that should be sent by value."] + #[serde(rename = "returnByValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[doc = "Whether preview should be generated for the result."] + #[serde(rename = "generatePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[doc = "Whether execution should be treated as initiated by user in the UI."] + #[serde(rename = "userGesture")] + #[serde(skip_serializing_if = "Option::is_none")] + pub user_gesture: Option, + #[doc = "Whether execution should `await` for resulting value and return once awaited promise is\nresolved."] + #[serde(rename = "awaitPromise")] + #[serde(skip_serializing_if = "Option::is_none")] + pub await_promise: Option, + #[doc = "Whether to throw an exception if side effect cannot be ruled out during evaluation.\nThis implies `disableBreaks` below."] + #[serde(rename = "throwOnSideEffect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub throw_on_side_effect: Option, + #[doc = "Terminate execution after timing out (number of milliseconds)."] + #[serde(rename = "timeout")] + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout: Option, + #[doc = "Disable breakpoints during execution."] + #[serde(rename = "disableBreaks")] + #[serde(skip_serializing_if = "Option::is_none")] + pub disable_breaks: Option, + #[doc = "Setting this flag to true enables `let` re-declaration and top-level `await`.\nNote that `let` variables can only be re-declared if they originate from\n`replMode` themselves."] + #[serde(rename = "replMode")] + #[serde(skip_serializing_if = "Option::is_none")] + pub repl_mode: Option, + #[doc = "The Content Security Policy (CSP) for the target might block 'unsafe-eval'\nwhich includes eval(), Function(), setTimeout() and setInterval()\nwhen called with non-callable arguments. This flag bypasses CSP for this\nevaluation and allows unsafe-eval. Defaults to true."] + #[serde(rename = "allowUnsafeEvalBlockedByCSP")] + #[serde(skip_serializing_if = "Option::is_none")] + pub allow_unsafe_eval_blocked_by_csp: Option, + #[doc = "An alternative way to specify the execution context to evaluate in.\nCompared to contextId that may be reused across processes, this is guaranteed to be\nsystem-unique, so it can be used to prevent accidental evaluation of the expression\nin context different than intended (e.g. as a result of navigation across process\nboundaries).\nThis is mutually exclusive with `contextId`."] + #[serde(rename = "uniqueContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub unique_context_id: Option, + #[doc = "Specifies the result serialization. If provided, overrides\n`generatePreview` and `returnByValue`."] + #[serde(rename = "serializationOptions")] + #[serde(skip_serializing_if = "Option::is_none")] + pub serialization_options: Option, + #[doc = r" This is a manually added field that is not part of the protocol definition, hence ignored during serde operations."] + #[doc = r""] + #[doc = r" If set to true, this field indicates, that if the command resulted in a response value of type `function` this, `EvaluateParams` command should be executed as a `CallFunctionOnParams` instead."] + #[serde(skip)] + pub eval_as_function_fallback: Option, + } + impl EvaluateParams { + pub fn new(expression: impl Into) -> Self { + Self { + expression: expression.into(), + object_group: None, + include_command_line_api: None, + silent: None, + context_id: None, + return_by_value: None, + generate_preview: None, + user_gesture: None, + await_promise: None, + throw_on_side_effect: None, + timeout: None, + disable_breaks: None, + repl_mode: None, + allow_unsafe_eval_blocked_by_csp: None, + unique_context_id: None, + serialization_options: None, + eval_as_function_fallback: None, + } + } + } + impl> From for EvaluateParams { + fn from(url: T) -> Self { + EvaluateParams::new(url) + } + } + impl EvaluateParams { + pub fn builder() -> EvaluateParamsBuilder { + EvaluateParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EvaluateParamsBuilder { + expression: Option, + object_group: Option, + include_command_line_api: Option, + silent: Option, + context_id: Option, + return_by_value: Option, + generate_preview: Option, + user_gesture: Option, + await_promise: Option, + throw_on_side_effect: Option, + timeout: Option, + disable_breaks: Option, + repl_mode: Option, + allow_unsafe_eval_blocked_by_csp: Option, + unique_context_id: Option, + serialization_options: Option, + eval_as_function_fallback: Option, + } + impl EvaluateParamsBuilder { + pub fn expression(mut self, expression: impl Into) -> Self { + self.expression = Some(expression.into()); + self + } + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn include_command_line_api( + mut self, + include_command_line_api: impl Into, + ) -> Self { + self.include_command_line_api = Some(include_command_line_api.into()); + self + } + pub fn silent(mut self, silent: impl Into) -> Self { + self.silent = Some(silent.into()); + self + } + pub fn context_id(mut self, context_id: impl Into) -> Self { + self.context_id = Some(context_id.into()); + self + } + pub fn return_by_value(mut self, return_by_value: impl Into) -> Self { + self.return_by_value = Some(return_by_value.into()); + self + } + pub fn generate_preview(mut self, generate_preview: impl Into) -> Self { + self.generate_preview = Some(generate_preview.into()); + self + } + pub fn user_gesture(mut self, user_gesture: impl Into) -> Self { + self.user_gesture = Some(user_gesture.into()); + self + } + pub fn await_promise(mut self, await_promise: impl Into) -> Self { + self.await_promise = Some(await_promise.into()); + self + } + pub fn throw_on_side_effect(mut self, throw_on_side_effect: impl Into) -> Self { + self.throw_on_side_effect = Some(throw_on_side_effect.into()); + self + } + pub fn timeout(mut self, timeout: impl Into) -> Self { + self.timeout = Some(timeout.into()); + self + } + pub fn disable_breaks(mut self, disable_breaks: impl Into) -> Self { + self.disable_breaks = Some(disable_breaks.into()); + self + } + pub fn repl_mode(mut self, repl_mode: impl Into) -> Self { + self.repl_mode = Some(repl_mode.into()); + self + } + pub fn allow_unsafe_eval_blocked_by_csp( + mut self, + allow_unsafe_eval_blocked_by_csp: impl Into, + ) -> Self { + self.allow_unsafe_eval_blocked_by_csp = + Some(allow_unsafe_eval_blocked_by_csp.into()); + self + } + pub fn unique_context_id(mut self, unique_context_id: impl Into) -> Self { + self.unique_context_id = Some(unique_context_id.into()); + self + } + pub fn serialization_options( + mut self, + serialization_options: impl Into, + ) -> Self { + self.serialization_options = Some(serialization_options.into()); + self + } + pub fn eval_as_function_fallback( + mut self, + eval_as_function_fallback: impl Into, + ) -> Self { + self.eval_as_function_fallback = Some(eval_as_function_fallback.into()); + self + } + pub fn build(self) -> Result { + Ok(EvaluateParams { + expression: self.expression.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(expression)) + })?, + object_group: self.object_group, + include_command_line_api: self.include_command_line_api, + silent: self.silent, + context_id: self.context_id, + return_by_value: self.return_by_value, + generate_preview: self.generate_preview, + user_gesture: self.user_gesture, + await_promise: self.await_promise, + throw_on_side_effect: self.throw_on_side_effect, + timeout: self.timeout, + disable_breaks: self.disable_breaks, + repl_mode: self.repl_mode, + allow_unsafe_eval_blocked_by_csp: self.allow_unsafe_eval_blocked_by_csp, + unique_context_id: self.unique_context_id, + serialization_options: self.serialization_options, + eval_as_function_fallback: self.eval_as_function_fallback, + }) + } + } + impl EvaluateParams { + pub const IDENTIFIER: &'static str = "Runtime.evaluate"; + } + impl chromiumoxide_types::Method for EvaluateParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EvaluateParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Evaluates expression on global object.\n[evaluate](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EvaluateReturns { + #[doc = "Evaluation result."] + #[serde(rename = "result")] + pub result: RemoteObject, + #[doc = "Exception details."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + #[doc = r" This is a manually added field that is not part of the protocol definition, hence ignored during serde operations."] + #[doc = r""] + #[doc = r" If set to true, this field indicates, that if the command resulted in a response value of type `function` this, `EvaluateParams` command should be executed as a `CallFunctionOnParams` instead."] + #[serde(skip)] + pub eval_as_function_fallback: Option, + } + impl EvaluateReturns { + pub fn new(result: impl Into) -> Self { + Self { + result: result.into(), + exception_details: None, + eval_as_function_fallback: None, + } + } + } + impl EvaluateReturns { + pub fn builder() -> EvaluateReturnsBuilder { + EvaluateReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct EvaluateReturnsBuilder { + result: Option, + exception_details: Option, + eval_as_function_fallback: Option, + } + impl EvaluateReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + self.result = Some(result.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn eval_as_function_fallback( + mut self, + eval_as_function_fallback: impl Into, + ) -> Self { + self.eval_as_function_fallback = Some(eval_as_function_fallback.into()); + self + } + pub fn build(self) -> Result { + Ok(EvaluateReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + exception_details: self.exception_details, + eval_as_function_fallback: self.eval_as_function_fallback, + }) + } + } + impl chromiumoxide_types::Command for EvaluateParams { + type Response = EvaluateReturns; + } + #[doc = "Returns the isolate id.\n[getIsolateId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getIsolateId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct GetIsolateIdParams {} + impl GetIsolateIdParams { + pub const IDENTIFIER: &'static str = "Runtime.getIsolateId"; + } + impl chromiumoxide_types::Method for GetIsolateIdParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetIsolateIdParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns the isolate id.\n[getIsolateId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getIsolateId)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetIsolateIdReturns { + #[doc = "The isolate id."] + #[serde(rename = "id")] + pub id: String, + } + impl GetIsolateIdReturns { + pub fn new(id: impl Into) -> Self { + Self { id: id.into() } + } + } + impl> From for GetIsolateIdReturns { + fn from(url: T) -> Self { + GetIsolateIdReturns::new(url) + } + } + impl GetIsolateIdReturns { + pub fn builder() -> GetIsolateIdReturnsBuilder { + GetIsolateIdReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetIsolateIdReturnsBuilder { + id: Option, + } + impl GetIsolateIdReturnsBuilder { + pub fn id(mut self, id: impl Into) -> Self { + self.id = Some(id.into()); + self + } + pub fn build(self) -> Result { + Ok(GetIsolateIdReturns { + id: self + .id + .ok_or_else(|| format!("Field `{}` is mandatory.", std::stringify!(id)))?, + }) + } + } + impl chromiumoxide_types::Command for GetIsolateIdParams { + type Response = GetIsolateIdReturns; + } + #[doc = "Returns the JavaScript heap usage.\nIt is the total usage of the corresponding isolate not scoped to a particular Runtime.\n[getHeapUsage](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getHeapUsage)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct GetHeapUsageParams {} + impl GetHeapUsageParams { + pub const IDENTIFIER: &'static str = "Runtime.getHeapUsage"; + } + impl chromiumoxide_types::Method for GetHeapUsageParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetHeapUsageParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns the JavaScript heap usage.\nIt is the total usage of the corresponding isolate not scoped to a particular Runtime.\n[getHeapUsage](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getHeapUsage)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetHeapUsageReturns { + #[doc = "Used JavaScript heap size in bytes."] + #[serde(rename = "usedSize")] + pub used_size: f64, + #[doc = "Allocated JavaScript heap size in bytes."] + #[serde(rename = "totalSize")] + pub total_size: f64, + #[doc = "Used size in bytes in the embedder's garbage-collected heap."] + #[serde(rename = "embedderHeapUsedSize")] + pub embedder_heap_used_size: f64, + #[doc = "Size in bytes of backing storage for array buffers and external strings."] + #[serde(rename = "backingStorageSize")] + pub backing_storage_size: f64, + } + impl GetHeapUsageReturns { + pub fn new( + used_size: impl Into, + total_size: impl Into, + embedder_heap_used_size: impl Into, + backing_storage_size: impl Into, + ) -> Self { + Self { + used_size: used_size.into(), + total_size: total_size.into(), + embedder_heap_used_size: embedder_heap_used_size.into(), + backing_storage_size: backing_storage_size.into(), + } + } + } + impl GetHeapUsageReturns { + pub fn builder() -> GetHeapUsageReturnsBuilder { + GetHeapUsageReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetHeapUsageReturnsBuilder { + used_size: Option, + total_size: Option, + embedder_heap_used_size: Option, + backing_storage_size: Option, + } + impl GetHeapUsageReturnsBuilder { + pub fn used_size(mut self, used_size: impl Into) -> Self { + self.used_size = Some(used_size.into()); + self + } + pub fn total_size(mut self, total_size: impl Into) -> Self { + self.total_size = Some(total_size.into()); + self + } + pub fn embedder_heap_used_size( + mut self, + embedder_heap_used_size: impl Into, + ) -> Self { + self.embedder_heap_used_size = Some(embedder_heap_used_size.into()); + self + } + pub fn backing_storage_size(mut self, backing_storage_size: impl Into) -> Self { + self.backing_storage_size = Some(backing_storage_size.into()); + self + } + pub fn build(self) -> Result { + Ok(GetHeapUsageReturns { + used_size: self.used_size.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(used_size)) + })?, + total_size: self.total_size.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(total_size)) + })?, + embedder_heap_used_size: self.embedder_heap_used_size.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(embedder_heap_used_size) + ) + })?, + backing_storage_size: self.backing_storage_size.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(backing_storage_size) + ) + })?, + }) + } + } + impl chromiumoxide_types::Command for GetHeapUsageParams { + type Response = GetHeapUsageReturns; + } + #[doc = "Returns properties of a given object. Object group of the result is inherited from the target\nobject.\n[getProperties](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getProperties)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetPropertiesParams { + #[doc = "Identifier of the object to return properties for."] + #[serde(rename = "objectId")] + pub object_id: RemoteObjectId, + #[doc = "If true, returns properties belonging only to the element itself, not to its prototype\nchain."] + #[serde(rename = "ownProperties")] + #[serde(skip_serializing_if = "Option::is_none")] + pub own_properties: Option, + #[doc = "If true, returns accessor properties (with getter/setter) only; internal properties are not\nreturned either."] + #[serde(rename = "accessorPropertiesOnly")] + #[serde(skip_serializing_if = "Option::is_none")] + pub accessor_properties_only: Option, + #[doc = "Whether preview should be generated for the results."] + #[serde(rename = "generatePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[doc = "If true, returns non-indexed properties only."] + #[serde(rename = "nonIndexedPropertiesOnly")] + #[serde(skip_serializing_if = "Option::is_none")] + pub non_indexed_properties_only: Option, + } + impl GetPropertiesParams { + pub fn new(object_id: impl Into) -> Self { + Self { + object_id: object_id.into(), + own_properties: None, + accessor_properties_only: None, + generate_preview: None, + non_indexed_properties_only: None, + } + } + } + impl GetPropertiesParams { + pub fn builder() -> GetPropertiesParamsBuilder { + GetPropertiesParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetPropertiesParamsBuilder { + object_id: Option, + own_properties: Option, + accessor_properties_only: Option, + generate_preview: Option, + non_indexed_properties_only: Option, + } + impl GetPropertiesParamsBuilder { + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn own_properties(mut self, own_properties: impl Into) -> Self { + self.own_properties = Some(own_properties.into()); + self + } + pub fn accessor_properties_only( + mut self, + accessor_properties_only: impl Into, + ) -> Self { + self.accessor_properties_only = Some(accessor_properties_only.into()); + self + } + pub fn generate_preview(mut self, generate_preview: impl Into) -> Self { + self.generate_preview = Some(generate_preview.into()); + self + } + pub fn non_indexed_properties_only( + mut self, + non_indexed_properties_only: impl Into, + ) -> Self { + self.non_indexed_properties_only = Some(non_indexed_properties_only.into()); + self + } + pub fn build(self) -> Result { + Ok(GetPropertiesParams { + object_id: self.object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object_id)) + })?, + own_properties: self.own_properties, + accessor_properties_only: self.accessor_properties_only, + generate_preview: self.generate_preview, + non_indexed_properties_only: self.non_indexed_properties_only, + }) + } + } + impl GetPropertiesParams { + pub const IDENTIFIER: &'static str = "Runtime.getProperties"; + } + impl chromiumoxide_types::Method for GetPropertiesParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetPropertiesParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns properties of a given object. Object group of the result is inherited from the target\nobject.\n[getProperties](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getProperties)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetPropertiesReturns { + #[doc = "Object properties."] + #[serde(rename = "result")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub result: Vec, + #[doc = "Internal object properties (only of the element itself)."] + #[serde(rename = "internalProperties")] + #[serde(skip_serializing_if = "Option::is_none")] + pub internal_properties: Option>, + #[doc = "Object private properties."] + #[serde(rename = "privateProperties")] + #[serde(skip_serializing_if = "Option::is_none")] + pub private_properties: Option>, + #[doc = "Exception details."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl GetPropertiesReturns { + pub fn new(result: Vec) -> Self { + Self { + result, + internal_properties: None, + private_properties: None, + exception_details: None, + } + } + } + impl GetPropertiesReturns { + pub fn builder() -> GetPropertiesReturnsBuilder { + GetPropertiesReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetPropertiesReturnsBuilder { + result: Option>, + internal_properties: Option>, + private_properties: Option>, + exception_details: Option, + } + impl GetPropertiesReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + let v = self.result.get_or_insert(Vec::new()); + v.push(result.into()); + self + } + pub fn results(mut self, results: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.result.get_or_insert(Vec::new()); + for val in results { + v.push(val.into()); + } + self + } + pub fn internal_propertie( + mut self, + internal_propertie: impl Into, + ) -> Self { + let v = self.internal_properties.get_or_insert(Vec::new()); + v.push(internal_propertie.into()); + self + } + pub fn internal_properties(mut self, internal_properties: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.internal_properties.get_or_insert(Vec::new()); + for val in internal_properties { + v.push(val.into()); + } + self + } + pub fn private_propertie( + mut self, + private_propertie: impl Into, + ) -> Self { + let v = self.private_properties.get_or_insert(Vec::new()); + v.push(private_propertie.into()); + self + } + pub fn private_properties(mut self, private_properties: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.private_properties.get_or_insert(Vec::new()); + for val in private_properties { + v.push(val.into()); + } + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> Result { + Ok(GetPropertiesReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + internal_properties: self.internal_properties, + private_properties: self.private_properties, + exception_details: self.exception_details, + }) + } + } + impl chromiumoxide_types::Command for GetPropertiesParams { + type Response = GetPropertiesReturns; + } + #[doc = "Returns all let, const and class variables from global scope.\n[globalLexicalScopeNames](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-globalLexicalScopeNames)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct GlobalLexicalScopeNamesParams { + #[doc = "Specifies in which execution context to lookup global scope variables."] + #[serde(rename = "executionContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + } + impl GlobalLexicalScopeNamesParams { + pub fn builder() -> GlobalLexicalScopeNamesParamsBuilder { + GlobalLexicalScopeNamesParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GlobalLexicalScopeNamesParamsBuilder { + execution_context_id: Option, + } + impl GlobalLexicalScopeNamesParamsBuilder { + pub fn execution_context_id( + mut self, + execution_context_id: impl Into, + ) -> Self { + self.execution_context_id = Some(execution_context_id.into()); + self + } + pub fn build(self) -> GlobalLexicalScopeNamesParams { + GlobalLexicalScopeNamesParams { + execution_context_id: self.execution_context_id, + } + } + } + impl GlobalLexicalScopeNamesParams { + pub const IDENTIFIER: &'static str = "Runtime.globalLexicalScopeNames"; + } + impl chromiumoxide_types::Method for GlobalLexicalScopeNamesParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GlobalLexicalScopeNamesParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Returns all let, const and class variables from global scope.\n[globalLexicalScopeNames](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-globalLexicalScopeNames)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GlobalLexicalScopeNamesReturns { + #[serde(rename = "names")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub names: Vec, + } + impl GlobalLexicalScopeNamesReturns { + pub fn new(names: Vec) -> Self { + Self { names } + } + } + impl GlobalLexicalScopeNamesReturns { + pub fn builder() -> GlobalLexicalScopeNamesReturnsBuilder { + GlobalLexicalScopeNamesReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GlobalLexicalScopeNamesReturnsBuilder { + names: Option>, + } + impl GlobalLexicalScopeNamesReturnsBuilder { + pub fn name(mut self, name: impl Into) -> Self { + let v = self.names.get_or_insert(Vec::new()); + v.push(name.into()); + self + } + pub fn names(mut self, names: I) -> Self + where + I: IntoIterator, + S: Into, + { + let v = self.names.get_or_insert(Vec::new()); + for val in names { + v.push(val.into()); + } + self + } + pub fn build(self) -> Result { + Ok(GlobalLexicalScopeNamesReturns { + names: self.names.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(names)) + })?, + }) + } + } + impl chromiumoxide_types::Command for GlobalLexicalScopeNamesParams { + type Response = GlobalLexicalScopeNamesReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct QueryObjectsParams { + #[doc = "Identifier of the prototype to return objects for."] + #[serde(rename = "prototypeObjectId")] + pub prototype_object_id: RemoteObjectId, + #[doc = "Symbolic group name that can be used to release the results."] + #[serde(rename = "objectGroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + } + impl QueryObjectsParams { + pub fn new(prototype_object_id: impl Into) -> Self { + Self { + prototype_object_id: prototype_object_id.into(), + object_group: None, + } + } + } + impl QueryObjectsParams { + pub fn builder() -> QueryObjectsParamsBuilder { + QueryObjectsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct QueryObjectsParamsBuilder { + prototype_object_id: Option, + object_group: Option, + } + impl QueryObjectsParamsBuilder { + pub fn prototype_object_id( + mut self, + prototype_object_id: impl Into, + ) -> Self { + self.prototype_object_id = Some(prototype_object_id.into()); + self + } + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn build(self) -> Result { + Ok(QueryObjectsParams { + prototype_object_id: self.prototype_object_id.ok_or_else(|| { + format!( + "Field `{}` is mandatory.", + std::stringify!(prototype_object_id) + ) + })?, + object_group: self.object_group, + }) + } + } + impl QueryObjectsParams { + pub const IDENTIFIER: &'static str = "Runtime.queryObjects"; + } + impl chromiumoxide_types::Method for QueryObjectsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for QueryObjectsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct QueryObjectsReturns { + #[doc = "Array with objects."] + #[serde(rename = "objects")] + pub objects: RemoteObject, + } + impl QueryObjectsReturns { + pub fn new(objects: impl Into) -> Self { + Self { + objects: objects.into(), + } + } + } + impl QueryObjectsReturns { + pub fn builder() -> QueryObjectsReturnsBuilder { + QueryObjectsReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct QueryObjectsReturnsBuilder { + objects: Option, + } + impl QueryObjectsReturnsBuilder { + pub fn objects(mut self, objects: impl Into) -> Self { + self.objects = Some(objects.into()); + self + } + pub fn build(self) -> Result { + Ok(QueryObjectsReturns { + objects: self.objects.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(objects)) + })?, + }) + } + } + impl chromiumoxide_types::Command for QueryObjectsParams { + type Response = QueryObjectsReturns; + } + #[doc = "Releases remote object with given id.\n[releaseObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-releaseObject)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ReleaseObjectParams { + #[doc = "Identifier of the object to release."] + #[serde(rename = "objectId")] + pub object_id: RemoteObjectId, + } + impl ReleaseObjectParams { + pub fn new(object_id: impl Into) -> Self { + Self { + object_id: object_id.into(), + } + } + } + impl ReleaseObjectParams { + pub fn builder() -> ReleaseObjectParamsBuilder { + ReleaseObjectParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ReleaseObjectParamsBuilder { + object_id: Option, + } + impl ReleaseObjectParamsBuilder { + pub fn object_id(mut self, object_id: impl Into) -> Self { + self.object_id = Some(object_id.into()); + self + } + pub fn build(self) -> Result { + Ok(ReleaseObjectParams { + object_id: self.object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object_id)) + })?, + }) + } + } + impl ReleaseObjectParams { + pub const IDENTIFIER: &'static str = "Runtime.releaseObject"; + } + impl chromiumoxide_types::Method for ReleaseObjectParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for ReleaseObjectParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Releases remote object with given id.\n[releaseObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-releaseObject)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct ReleaseObjectReturns {} + impl chromiumoxide_types::Command for ReleaseObjectParams { + type Response = ReleaseObjectReturns; + } + #[doc = "Releases all remote objects that belong to a given group.\n[releaseObjectGroup](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-releaseObjectGroup)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct ReleaseObjectGroupParams { + #[doc = "Symbolic object group name."] + #[serde(rename = "objectGroup")] + pub object_group: String, + } + impl ReleaseObjectGroupParams { + pub fn new(object_group: impl Into) -> Self { + Self { + object_group: object_group.into(), + } + } + } + impl> From for ReleaseObjectGroupParams { + fn from(url: T) -> Self { + ReleaseObjectGroupParams::new(url) + } + } + impl ReleaseObjectGroupParams { + pub fn builder() -> ReleaseObjectGroupParamsBuilder { + ReleaseObjectGroupParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct ReleaseObjectGroupParamsBuilder { + object_group: Option, + } + impl ReleaseObjectGroupParamsBuilder { + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn build(self) -> Result { + Ok(ReleaseObjectGroupParams { + object_group: self.object_group.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(object_group)) + })?, + }) + } + } + impl ReleaseObjectGroupParams { + pub const IDENTIFIER: &'static str = "Runtime.releaseObjectGroup"; + } + impl chromiumoxide_types::Method for ReleaseObjectGroupParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for ReleaseObjectGroupParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Releases all remote objects that belong to a given group.\n[releaseObjectGroup](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-releaseObjectGroup)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct ReleaseObjectGroupReturns {} + impl chromiumoxide_types::Command for ReleaseObjectGroupParams { + type Response = ReleaseObjectGroupReturns; + } + #[doc = "Tells inspected instance to run if it was waiting for debugger to attach.\n[runIfWaitingForDebugger](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runIfWaitingForDebugger)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct RunIfWaitingForDebuggerParams {} + impl RunIfWaitingForDebuggerParams { + pub const IDENTIFIER: &'static str = "Runtime.runIfWaitingForDebugger"; + } + impl chromiumoxide_types::Method for RunIfWaitingForDebuggerParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for RunIfWaitingForDebuggerParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Tells inspected instance to run if it was waiting for debugger to attach.\n[runIfWaitingForDebugger](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runIfWaitingForDebugger)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct RunIfWaitingForDebuggerReturns {} + impl chromiumoxide_types::Command for RunIfWaitingForDebuggerParams { + type Response = RunIfWaitingForDebuggerReturns; + } + #[doc = "Runs script with given id in a given context.\n[runScript](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runScript)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct RunScriptParams { + #[doc = "Id of the script to run."] + #[serde(rename = "scriptId")] + pub script_id: ScriptId, + #[doc = "Specifies in which execution context to perform script run. If the parameter is omitted the\nevaluation will be performed in the context of the inspected page."] + #[serde(rename = "executionContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + #[doc = "Symbolic group name that can be used to release multiple objects."] + #[serde(rename = "objectGroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub object_group: Option, + #[doc = "In silent mode exceptions thrown during evaluation are not reported and do not pause\nexecution. Overrides `setPauseOnException` state."] + #[serde(rename = "silent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + #[doc = "Determines whether Command Line API should be available during the evaluation."] + #[serde(rename = "includeCommandLineAPI")] + #[serde(skip_serializing_if = "Option::is_none")] + pub include_command_line_api: Option, + #[doc = "Whether the result is expected to be a JSON object which should be sent by value."] + #[serde(rename = "returnByValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub return_by_value: Option, + #[doc = "Whether preview should be generated for the result."] + #[serde(rename = "generatePreview")] + #[serde(skip_serializing_if = "Option::is_none")] + pub generate_preview: Option, + #[doc = "Whether execution should `await` for resulting value and return once awaited promise is\nresolved."] + #[serde(rename = "awaitPromise")] + #[serde(skip_serializing_if = "Option::is_none")] + pub await_promise: Option, + } + impl RunScriptParams { + pub fn new(script_id: impl Into) -> Self { + Self { + script_id: script_id.into(), + execution_context_id: None, + object_group: None, + silent: None, + include_command_line_api: None, + return_by_value: None, + generate_preview: None, + await_promise: None, + } + } + } + impl RunScriptParams { + pub fn builder() -> RunScriptParamsBuilder { + RunScriptParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct RunScriptParamsBuilder { + script_id: Option, + execution_context_id: Option, + object_group: Option, + silent: Option, + include_command_line_api: Option, + return_by_value: Option, + generate_preview: Option, + await_promise: Option, + } + impl RunScriptParamsBuilder { + pub fn script_id(mut self, script_id: impl Into) -> Self { + self.script_id = Some(script_id.into()); + self + } + pub fn execution_context_id( + mut self, + execution_context_id: impl Into, + ) -> Self { + self.execution_context_id = Some(execution_context_id.into()); + self + } + pub fn object_group(mut self, object_group: impl Into) -> Self { + self.object_group = Some(object_group.into()); + self + } + pub fn silent(mut self, silent: impl Into) -> Self { + self.silent = Some(silent.into()); + self + } + pub fn include_command_line_api( + mut self, + include_command_line_api: impl Into, + ) -> Self { + self.include_command_line_api = Some(include_command_line_api.into()); + self + } + pub fn return_by_value(mut self, return_by_value: impl Into) -> Self { + self.return_by_value = Some(return_by_value.into()); + self + } + pub fn generate_preview(mut self, generate_preview: impl Into) -> Self { + self.generate_preview = Some(generate_preview.into()); + self + } + pub fn await_promise(mut self, await_promise: impl Into) -> Self { + self.await_promise = Some(await_promise.into()); + self + } + pub fn build(self) -> Result { + Ok(RunScriptParams { + script_id: self.script_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(script_id)) + })?, + execution_context_id: self.execution_context_id, + object_group: self.object_group, + silent: self.silent, + include_command_line_api: self.include_command_line_api, + return_by_value: self.return_by_value, + generate_preview: self.generate_preview, + await_promise: self.await_promise, + }) + } + } + impl RunScriptParams { + pub const IDENTIFIER: &'static str = "Runtime.runScript"; + } + impl chromiumoxide_types::Method for RunScriptParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for RunScriptParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Runs script with given id in a given context.\n[runScript](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runScript)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct RunScriptReturns { + #[doc = "Run result."] + #[serde(rename = "result")] + pub result: RemoteObject, + #[doc = "Exception details."] + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl RunScriptReturns { + pub fn new(result: impl Into) -> Self { + Self { + result: result.into(), + exception_details: None, + } + } + } + impl RunScriptReturns { + pub fn builder() -> RunScriptReturnsBuilder { + RunScriptReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct RunScriptReturnsBuilder { + result: Option, + exception_details: Option, + } + impl RunScriptReturnsBuilder { + pub fn result(mut self, result: impl Into) -> Self { + self.result = Some(result.into()); + self + } + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> Result { + Ok(RunScriptReturns { + result: self.result.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(result)) + })?, + exception_details: self.exception_details, + }) + } + } + impl chromiumoxide_types::Command for RunScriptParams { + type Response = RunScriptReturns; + } + #[doc = "Enables or disables async call stacks tracking.\n[setAsyncCallStackDepth](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-setAsyncCallStackDepth)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetAsyncCallStackDepthParams { + #[doc = "Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async\ncall stacks (default)."] + #[serde(rename = "maxDepth")] + pub max_depth: i64, + } + impl SetAsyncCallStackDepthParams { + pub fn new(max_depth: impl Into) -> Self { + Self { + max_depth: max_depth.into(), + } + } + } + impl SetAsyncCallStackDepthParams { + pub fn builder() -> SetAsyncCallStackDepthParamsBuilder { + SetAsyncCallStackDepthParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetAsyncCallStackDepthParamsBuilder { + max_depth: Option, + } + impl SetAsyncCallStackDepthParamsBuilder { + pub fn max_depth(mut self, max_depth: impl Into) -> Self { + self.max_depth = Some(max_depth.into()); + self + } + pub fn build(self) -> Result { + Ok(SetAsyncCallStackDepthParams { + max_depth: self.max_depth.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(max_depth)) + })?, + }) + } + } + impl SetAsyncCallStackDepthParams { + pub const IDENTIFIER: &'static str = "Runtime.setAsyncCallStackDepth"; + } + impl chromiumoxide_types::Method for SetAsyncCallStackDepthParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetAsyncCallStackDepthParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Enables or disables async call stacks tracking.\n[setAsyncCallStackDepth](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-setAsyncCallStackDepth)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetAsyncCallStackDepthReturns {} + impl chromiumoxide_types::Command for SetAsyncCallStackDepthParams { + type Response = SetAsyncCallStackDepthReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetCustomObjectFormatterEnabledParams { + #[serde(rename = "enabled")] + pub enabled: bool, + } + impl SetCustomObjectFormatterEnabledParams { + pub fn new(enabled: impl Into) -> Self { + Self { + enabled: enabled.into(), + } + } + } + impl SetCustomObjectFormatterEnabledParams { + pub fn builder() -> SetCustomObjectFormatterEnabledParamsBuilder { + SetCustomObjectFormatterEnabledParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetCustomObjectFormatterEnabledParamsBuilder { + enabled: Option, + } + impl SetCustomObjectFormatterEnabledParamsBuilder { + pub fn enabled(mut self, enabled: impl Into) -> Self { + self.enabled = Some(enabled.into()); + self + } + pub fn build(self) -> Result { + Ok(SetCustomObjectFormatterEnabledParams { + enabled: self.enabled.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(enabled)) + })?, + }) + } + } + impl SetCustomObjectFormatterEnabledParams { + pub const IDENTIFIER: &'static str = "Runtime.setCustomObjectFormatterEnabled"; + } + impl chromiumoxide_types::Method for SetCustomObjectFormatterEnabledParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetCustomObjectFormatterEnabledParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetCustomObjectFormatterEnabledReturns {} + impl chromiumoxide_types::Command for SetCustomObjectFormatterEnabledParams { + type Response = SetCustomObjectFormatterEnabledReturns; + } + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct SetMaxCallStackSizeToCaptureParams { + #[serde(rename = "size")] + pub size: i64, + } + impl SetMaxCallStackSizeToCaptureParams { + pub fn new(size: impl Into) -> Self { + Self { size: size.into() } + } + } + impl SetMaxCallStackSizeToCaptureParams { + pub fn builder() -> SetMaxCallStackSizeToCaptureParamsBuilder { + SetMaxCallStackSizeToCaptureParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct SetMaxCallStackSizeToCaptureParamsBuilder { + size: Option, + } + impl SetMaxCallStackSizeToCaptureParamsBuilder { + pub fn size(mut self, size: impl Into) -> Self { + self.size = Some(size.into()); + self + } + pub fn build(self) -> Result { + Ok(SetMaxCallStackSizeToCaptureParams { + size: self.size.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(size)) + })?, + }) + } + } + impl SetMaxCallStackSizeToCaptureParams { + pub const IDENTIFIER: &'static str = "Runtime.setMaxCallStackSizeToCapture"; + } + impl chromiumoxide_types::Method for SetMaxCallStackSizeToCaptureParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for SetMaxCallStackSizeToCaptureParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct SetMaxCallStackSizeToCaptureReturns {} + impl chromiumoxide_types::Command for SetMaxCallStackSizeToCaptureParams { + type Response = SetMaxCallStackSizeToCaptureReturns; + } + #[doc = "Terminate current or next JavaScript execution.\nWill cancel the termination when the outer-most script execution ends.\n[terminateExecution](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-terminateExecution)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct TerminateExecutionParams {} + impl TerminateExecutionParams { + pub const IDENTIFIER: &'static str = "Runtime.terminateExecution"; + } + impl chromiumoxide_types::Method for TerminateExecutionParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for TerminateExecutionParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Terminate current or next JavaScript execution.\nWill cancel the termination when the outer-most script execution ends.\n[terminateExecution](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-terminateExecution)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct TerminateExecutionReturns {} + impl chromiumoxide_types::Command for TerminateExecutionParams { + type Response = TerminateExecutionReturns; + } + #[doc = "If executionContextId is empty, adds binding with the given name on the\nglobal objects of all inspected contexts, including those created later,\nbindings survive reloads.\nBinding function takes exactly one argument, this argument should be string,\nin case of any other input, function throws an exception.\nEach binding function call produces Runtime.bindingCalled notification.\n[addBinding](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-addBinding)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct AddBindingParams { + #[serde(rename = "name")] + pub name: String, + #[doc = "If specified, the binding is exposed to the executionContext with\nmatching name, even for contexts created after the binding is added.\nSee also `ExecutionContext.name` and `worldName` parameter to\n`Page.addScriptToEvaluateOnNewDocument`.\nThis parameter is mutually exclusive with `executionContextId`."] + #[serde(rename = "executionContextName")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_name: Option, + } + impl AddBindingParams { + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + execution_context_name: None, + } + } + } + impl> From for AddBindingParams { + fn from(url: T) -> Self { + AddBindingParams::new(url) + } + } + impl AddBindingParams { + pub fn builder() -> AddBindingParamsBuilder { + AddBindingParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct AddBindingParamsBuilder { + name: Option, + execution_context_name: Option, + } + impl AddBindingParamsBuilder { + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn execution_context_name( + mut self, + execution_context_name: impl Into, + ) -> Self { + self.execution_context_name = Some(execution_context_name.into()); + self + } + pub fn build(self) -> Result { + Ok(AddBindingParams { + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + execution_context_name: self.execution_context_name, + }) + } + } + impl AddBindingParams { + pub const IDENTIFIER: &'static str = "Runtime.addBinding"; + } + impl chromiumoxide_types::Method for AddBindingParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for AddBindingParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "If executionContextId is empty, adds binding with the given name on the\nglobal objects of all inspected contexts, including those created later,\nbindings survive reloads.\nBinding function takes exactly one argument, this argument should be string,\nin case of any other input, function throws an exception.\nEach binding function call produces Runtime.bindingCalled notification.\n[addBinding](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-addBinding)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct AddBindingReturns {} + impl chromiumoxide_types::Command for AddBindingParams { + type Response = AddBindingReturns; + } + #[doc = "This method does not remove binding function from global object but\nunsubscribes current runtime agent from Runtime.bindingCalled notifications.\n[removeBinding](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-removeBinding)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct RemoveBindingParams { + #[serde(rename = "name")] + pub name: String, + } + impl RemoveBindingParams { + pub fn new(name: impl Into) -> Self { + Self { name: name.into() } + } + } + impl> From for RemoveBindingParams { + fn from(url: T) -> Self { + RemoveBindingParams::new(url) + } + } + impl RemoveBindingParams { + pub fn builder() -> RemoveBindingParamsBuilder { + RemoveBindingParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct RemoveBindingParamsBuilder { + name: Option, + } + impl RemoveBindingParamsBuilder { + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + pub fn build(self) -> Result { + Ok(RemoveBindingParams { + name: self.name.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(name)) + })?, + }) + } + } + impl RemoveBindingParams { + pub const IDENTIFIER: &'static str = "Runtime.removeBinding"; + } + impl chromiumoxide_types::Method for RemoveBindingParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for RemoveBindingParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "This method does not remove binding function from global object but\nunsubscribes current runtime agent from Runtime.bindingCalled notifications.\n[removeBinding](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-removeBinding)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct RemoveBindingReturns {} + impl chromiumoxide_types::Command for RemoveBindingParams { + type Response = RemoveBindingReturns; + } + #[doc = "This method tries to lookup and populate exception details for a\nJavaScript Error object.\nNote that the stackTrace portion of the resulting exceptionDetails will\nonly be populated if the Runtime domain was enabled at the time when the\nError was thrown.\n[getExceptionDetails](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getExceptionDetails)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct GetExceptionDetailsParams { + #[doc = "The error object for which to resolve the exception details."] + #[serde(rename = "errorObjectId")] + pub error_object_id: RemoteObjectId, + } + impl GetExceptionDetailsParams { + pub fn new(error_object_id: impl Into) -> Self { + Self { + error_object_id: error_object_id.into(), + } + } + } + impl GetExceptionDetailsParams { + pub fn builder() -> GetExceptionDetailsParamsBuilder { + GetExceptionDetailsParamsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetExceptionDetailsParamsBuilder { + error_object_id: Option, + } + impl GetExceptionDetailsParamsBuilder { + pub fn error_object_id(mut self, error_object_id: impl Into) -> Self { + self.error_object_id = Some(error_object_id.into()); + self + } + pub fn build(self) -> Result { + Ok(GetExceptionDetailsParams { + error_object_id: self.error_object_id.ok_or_else(|| { + format!("Field `{}` is mandatory.", std::stringify!(error_object_id)) + })?, + }) + } + } + impl GetExceptionDetailsParams { + pub const IDENTIFIER: &'static str = "Runtime.getExceptionDetails"; + } + impl chromiumoxide_types::Method for GetExceptionDetailsParams { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for GetExceptionDetailsParams { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "This method tries to lookup and populate exception details for a\nJavaScript Error object.\nNote that the stackTrace portion of the resulting exceptionDetails will\nonly be populated if the Runtime domain was enabled at the time when the\nError was thrown.\n[getExceptionDetails](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getExceptionDetails)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct GetExceptionDetailsReturns { + #[serde(rename = "exceptionDetails")] + #[serde(skip_serializing_if = "Option::is_none")] + pub exception_details: Option, + } + impl GetExceptionDetailsReturns { + pub fn builder() -> GetExceptionDetailsReturnsBuilder { + GetExceptionDetailsReturnsBuilder::default() + } + } + #[derive(Default, Clone)] + pub struct GetExceptionDetailsReturnsBuilder { + exception_details: Option, + } + impl GetExceptionDetailsReturnsBuilder { + pub fn exception_details( + mut self, + exception_details: impl Into, + ) -> Self { + self.exception_details = Some(exception_details.into()); + self + } + pub fn build(self) -> GetExceptionDetailsReturns { + GetExceptionDetailsReturns { + exception_details: self.exception_details, + } + } + } + impl chromiumoxide_types::Command for GetExceptionDetailsParams { + type Response = GetExceptionDetailsReturns; + } + #[doc = "Notification is issued every time when binding is called.\n[bindingCalled](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-bindingCalled)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventBindingCalled { + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "payload")] + pub payload: String, + #[doc = "Identifier of the context where the call was made."] + #[serde(rename = "executionContextId")] + pub execution_context_id: ExecutionContextId, + } + impl EventBindingCalled { + pub const IDENTIFIER: &'static str = "Runtime.bindingCalled"; + } + impl chromiumoxide_types::Method for EventBindingCalled { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventBindingCalled { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when console API was called.\n[consoleAPICalled](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-consoleAPICalled)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventConsoleApiCalled { + #[doc = "Type of the call."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: ConsoleApiCalledType, + #[doc = "Call arguments."] + #[serde(rename = "args")] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub args: Vec, + #[doc = "Identifier of the context where the call was made."] + #[serde(rename = "executionContextId")] + pub execution_context_id: ExecutionContextId, + #[doc = "Call timestamp."] + #[serde(rename = "timestamp")] + pub timestamp: Timestamp, + #[doc = "Stack trace captured when the call was made. The async stack chain is automatically reported for\nthe following call types: `assert`, `error`, `trace`, `warning`. For other types the async call\nchain can be retrieved using `Debugger.getStackTrace` and `stackTrace.parentId` field."] + #[serde(rename = "stackTrace")] + #[serde(skip_serializing_if = "Option::is_none")] + pub stack_trace: Option, + #[doc = "Console context descriptor for calls on non-default console context (not console.*):\n'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call\non named context."] + #[serde(rename = "context")] + #[serde(skip_serializing_if = "Option::is_none")] + pub context: Option, + } + #[doc = "Type of the call."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum ConsoleApiCalledType { + #[serde(rename = "log")] + Log, + #[serde(rename = "debug")] + Debug, + #[serde(rename = "info")] + Info, + #[serde(rename = "error")] + Error, + #[serde(rename = "warning")] + Warning, + #[serde(rename = "dir")] + Dir, + #[serde(rename = "dirxml")] + Dirxml, + #[serde(rename = "table")] + Table, + #[serde(rename = "trace")] + Trace, + #[serde(rename = "clear")] + Clear, + #[serde(rename = "startGroup")] + StartGroup, + #[serde(rename = "startGroupCollapsed")] + StartGroupCollapsed, + #[serde(rename = "endGroup")] + EndGroup, + #[serde(rename = "assert")] + Assert, + #[serde(rename = "profile")] + Profile, + #[serde(rename = "profileEnd")] + ProfileEnd, + #[serde(rename = "count")] + Count, + #[serde(rename = "timeEnd")] + TimeEnd, + } + impl AsRef for ConsoleApiCalledType { + fn as_ref(&self) -> &str { + match self { + ConsoleApiCalledType::Log => "log", + ConsoleApiCalledType::Debug => "debug", + ConsoleApiCalledType::Info => "info", + ConsoleApiCalledType::Error => "error", + ConsoleApiCalledType::Warning => "warning", + ConsoleApiCalledType::Dir => "dir", + ConsoleApiCalledType::Dirxml => "dirxml", + ConsoleApiCalledType::Table => "table", + ConsoleApiCalledType::Trace => "trace", + ConsoleApiCalledType::Clear => "clear", + ConsoleApiCalledType::StartGroup => "startGroup", + ConsoleApiCalledType::StartGroupCollapsed => "startGroupCollapsed", + ConsoleApiCalledType::EndGroup => "endGroup", + ConsoleApiCalledType::Assert => "assert", + ConsoleApiCalledType::Profile => "profile", + ConsoleApiCalledType::ProfileEnd => "profileEnd", + ConsoleApiCalledType::Count => "count", + ConsoleApiCalledType::TimeEnd => "timeEnd", + } + } + } + impl ::std::str::FromStr for ConsoleApiCalledType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "log" | "Log" => Ok(ConsoleApiCalledType::Log), + "debug" | "Debug" => Ok(ConsoleApiCalledType::Debug), + "info" | "Info" => Ok(ConsoleApiCalledType::Info), + "error" | "Error" => Ok(ConsoleApiCalledType::Error), + "warning" | "Warning" => Ok(ConsoleApiCalledType::Warning), + "dir" | "Dir" => Ok(ConsoleApiCalledType::Dir), + "dirxml" | "Dirxml" => Ok(ConsoleApiCalledType::Dirxml), + "table" | "Table" => Ok(ConsoleApiCalledType::Table), + "trace" | "Trace" => Ok(ConsoleApiCalledType::Trace), + "clear" | "Clear" => Ok(ConsoleApiCalledType::Clear), + "startGroup" | "StartGroup" | "startgroup" => { + Ok(ConsoleApiCalledType::StartGroup) + } + "startGroupCollapsed" | "StartGroupCollapsed" | "startgroupcollapsed" => { + Ok(ConsoleApiCalledType::StartGroupCollapsed) + } + "endGroup" | "EndGroup" | "endgroup" => Ok(ConsoleApiCalledType::EndGroup), + "assert" | "Assert" => Ok(ConsoleApiCalledType::Assert), + "profile" | "Profile" => Ok(ConsoleApiCalledType::Profile), + "profileEnd" | "ProfileEnd" | "profileend" => { + Ok(ConsoleApiCalledType::ProfileEnd) + } + "count" | "Count" => Ok(ConsoleApiCalledType::Count), + "timeEnd" | "TimeEnd" | "timeend" => Ok(ConsoleApiCalledType::TimeEnd), + _ => Err(s.to_string()), + } + } + } + impl EventConsoleApiCalled { + pub const IDENTIFIER: &'static str = "Runtime.consoleAPICalled"; + } + impl chromiumoxide_types::Method for EventConsoleApiCalled { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventConsoleApiCalled { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when unhandled exception was revoked.\n[exceptionRevoked](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-exceptionRevoked)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventExceptionRevoked { + #[doc = "Reason describing why exception was revoked."] + #[serde(rename = "reason")] + pub reason: String, + #[doc = "The id of revoked exception, as reported in `exceptionThrown`."] + #[serde(rename = "exceptionId")] + pub exception_id: i64, + } + impl EventExceptionRevoked { + pub const IDENTIFIER: &'static str = "Runtime.exceptionRevoked"; + } + impl chromiumoxide_types::Method for EventExceptionRevoked { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventExceptionRevoked { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when exception was thrown and unhandled.\n[exceptionThrown](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-exceptionThrown)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventExceptionThrown { + #[doc = "Timestamp of the exception."] + #[serde(rename = "timestamp")] + pub timestamp: Timestamp, + #[serde(rename = "exceptionDetails")] + pub exception_details: ExceptionDetails, + } + impl EventExceptionThrown { + pub const IDENTIFIER: &'static str = "Runtime.exceptionThrown"; + } + impl chromiumoxide_types::Method for EventExceptionThrown { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventExceptionThrown { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when new execution context is created.\n[executionContextCreated](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-executionContextCreated)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventExecutionContextCreated { + #[doc = "A newly created execution context."] + #[serde(rename = "context")] + pub context: ExecutionContextDescription, + } + impl EventExecutionContextCreated { + pub const IDENTIFIER: &'static str = "Runtime.executionContextCreated"; + } + impl chromiumoxide_types::Method for EventExecutionContextCreated { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventExecutionContextCreated { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when execution context is destroyed.\n[executionContextDestroyed](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-executionContextDestroyed)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventExecutionContextDestroyed { + #[doc = "Unique Id of the destroyed context"] + #[serde(rename = "executionContextUniqueId")] + pub execution_context_unique_id: String, + } + impl EventExecutionContextDestroyed { + pub const IDENTIFIER: &'static str = "Runtime.executionContextDestroyed"; + } + impl chromiumoxide_types::Method for EventExecutionContextDestroyed { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventExecutionContextDestroyed { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when all executionContexts were cleared in browser\n[executionContextsCleared](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-executionContextsCleared)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] + pub struct EventExecutionContextsCleared {} + impl EventExecutionContextsCleared { + pub const IDENTIFIER: &'static str = "Runtime.executionContextsCleared"; + } + impl chromiumoxide_types::Method for EventExecutionContextsCleared { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventExecutionContextsCleared { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + #[doc = "Issued when object should be inspected (for example, as a result of inspect() command line API\ncall).\n[inspectRequested](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-inspectRequested)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct EventInspectRequested { + #[serde(rename = "object")] + pub object: RemoteObject, + #[serde(rename = "hints")] + pub hints: serde_json::Value, + #[doc = "Identifier of the context where the call was made."] + #[serde(rename = "executionContextId")] + #[serde(skip_serializing_if = "Option::is_none")] + pub execution_context_id: Option, + } + impl EventInspectRequested { + pub const IDENTIFIER: &'static str = "Runtime.inspectRequested"; + } + impl chromiumoxide_types::Method for EventInspectRequested { + fn identifier(&self) -> chromiumoxide_types::MethodId { + Self::IDENTIFIER.into() + } + } + impl chromiumoxide_types::MethodType for EventInspectRequested { + fn method_id() -> chromiumoxide_types::MethodId + where + Self: Sized, + { + Self::IDENTIFIER.into() + } + } + } +} +#[allow(clippy::wrong_self_convention)] +pub mod browser_protocol { + #[doc = r" The version of this protocol definition"] + pub const VERSION: &str = "1.3"; + pub mod accessibility { + use serde::{Deserialize, Serialize}; + #[doc = "Unique accessibility node identifier.\n[AXNodeId](https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/#type-AXNodeId)"] + #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)] + pub struct AxNodeId(String); + impl AxNodeId { + pub fn new(val: impl Into) -> Self { + AxNodeId(val.into()) + } + pub fn inner(&self) -> &String { + &self.0 + } + } + impl AsRef for AxNodeId { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + impl From for String { + fn from(el: AxNodeId) -> String { + el.0 + } + } + impl From for AxNodeId { + fn from(expr: String) -> Self { + AxNodeId(expr) + } + } + impl std::borrow::Borrow for AxNodeId { + fn borrow(&self) -> &str { + &self.0 + } + } + impl AxNodeId { + pub const IDENTIFIER: &'static str = "Accessibility.AXNodeId"; + } + #[doc = "Enum of possible property types."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum AxValueType { + #[serde(rename = "boolean")] + Boolean, + #[serde(rename = "tristate")] + Tristate, + #[serde(rename = "booleanOrUndefined")] + BooleanOrUndefined, + #[serde(rename = "idref")] + Idref, + #[serde(rename = "idrefList")] + IdrefList, + #[serde(rename = "integer")] + Integer, + #[serde(rename = "node")] + Node, + #[serde(rename = "nodeList")] + NodeList, + #[serde(rename = "number")] + Number, + #[serde(rename = "string")] + String, + #[serde(rename = "computedString")] + ComputedString, + #[serde(rename = "token")] + Token, + #[serde(rename = "tokenList")] + TokenList, + #[serde(rename = "domRelation")] + DomRelation, + #[serde(rename = "role")] + Role, + #[serde(rename = "internalRole")] + InternalRole, + #[serde(rename = "valueUndefined")] + ValueUndefined, + } + impl AsRef for AxValueType { + fn as_ref(&self) -> &str { + match self { + AxValueType::Boolean => "boolean", + AxValueType::Tristate => "tristate", + AxValueType::BooleanOrUndefined => "booleanOrUndefined", + AxValueType::Idref => "idref", + AxValueType::IdrefList => "idrefList", + AxValueType::Integer => "integer", + AxValueType::Node => "node", + AxValueType::NodeList => "nodeList", + AxValueType::Number => "number", + AxValueType::String => "string", + AxValueType::ComputedString => "computedString", + AxValueType::Token => "token", + AxValueType::TokenList => "tokenList", + AxValueType::DomRelation => "domRelation", + AxValueType::Role => "role", + AxValueType::InternalRole => "internalRole", + AxValueType::ValueUndefined => "valueUndefined", + } + } + } + impl ::std::str::FromStr for AxValueType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "boolean" | "Boolean" => Ok(AxValueType::Boolean), + "tristate" | "Tristate" => Ok(AxValueType::Tristate), + "booleanOrUndefined" | "BooleanOrUndefined" | "booleanorundefined" => { + Ok(AxValueType::BooleanOrUndefined) + } + "idref" | "Idref" => Ok(AxValueType::Idref), + "idrefList" | "IdrefList" | "idreflist" => Ok(AxValueType::IdrefList), + "integer" | "Integer" => Ok(AxValueType::Integer), + "node" | "Node" => Ok(AxValueType::Node), + "nodeList" | "NodeList" | "nodelist" => Ok(AxValueType::NodeList), + "number" | "Number" => Ok(AxValueType::Number), + "string" | "String" => Ok(AxValueType::String), + "computedString" | "ComputedString" | "computedstring" => { + Ok(AxValueType::ComputedString) + } + "token" | "Token" => Ok(AxValueType::Token), + "tokenList" | "TokenList" | "tokenlist" => Ok(AxValueType::TokenList), + "domRelation" | "DomRelation" | "domrelation" => Ok(AxValueType::DomRelation), + "role" | "Role" => Ok(AxValueType::Role), + "internalRole" | "InternalRole" | "internalrole" => { + Ok(AxValueType::InternalRole) + } + "valueUndefined" | "ValueUndefined" | "valueundefined" => { + Ok(AxValueType::ValueUndefined) + } + _ => Err(s.to_string()), + } + } + } + #[doc = "Enum of possible property sources."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum AxValueSourceType { + #[serde(rename = "attribute")] + Attribute, + #[serde(rename = "implicit")] + Implicit, + #[serde(rename = "style")] + Style, + #[serde(rename = "contents")] + Contents, + #[serde(rename = "placeholder")] + Placeholder, + #[serde(rename = "relatedElement")] + RelatedElement, + } + impl AsRef for AxValueSourceType { + fn as_ref(&self) -> &str { + match self { + AxValueSourceType::Attribute => "attribute", + AxValueSourceType::Implicit => "implicit", + AxValueSourceType::Style => "style", + AxValueSourceType::Contents => "contents", + AxValueSourceType::Placeholder => "placeholder", + AxValueSourceType::RelatedElement => "relatedElement", + } + } + } + impl ::std::str::FromStr for AxValueSourceType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "attribute" | "Attribute" => Ok(AxValueSourceType::Attribute), + "implicit" | "Implicit" => Ok(AxValueSourceType::Implicit), + "style" | "Style" => Ok(AxValueSourceType::Style), + "contents" | "Contents" => Ok(AxValueSourceType::Contents), + "placeholder" | "Placeholder" => Ok(AxValueSourceType::Placeholder), + "relatedElement" | "RelatedElement" | "relatedelement" => { + Ok(AxValueSourceType::RelatedElement) + } + _ => Err(s.to_string()), + } + } + } + #[doc = "Enum of possible native property sources (as a subtype of a particular AXValueSourceType)."] + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] + pub enum AxValueNativeSourceType { + #[serde(rename = "description")] + Description, + #[serde(rename = "figcaption")] + Figcaption, + #[serde(rename = "label")] + Label, + #[serde(rename = "labelfor")] + Labelfor, + #[serde(rename = "labelwrapped")] + Labelwrapped, + #[serde(rename = "legend")] + Legend, + #[serde(rename = "rubyannotation")] + Rubyannotation, + #[serde(rename = "tablecaption")] + Tablecaption, + #[serde(rename = "title")] + Title, + #[serde(rename = "other")] + Other, + } + impl AsRef for AxValueNativeSourceType { + fn as_ref(&self) -> &str { + match self { + AxValueNativeSourceType::Description => "description", + AxValueNativeSourceType::Figcaption => "figcaption", + AxValueNativeSourceType::Label => "label", + AxValueNativeSourceType::Labelfor => "labelfor", + AxValueNativeSourceType::Labelwrapped => "labelwrapped", + AxValueNativeSourceType::Legend => "legend", + AxValueNativeSourceType::Rubyannotation => "rubyannotation", + AxValueNativeSourceType::Tablecaption => "tablecaption", + AxValueNativeSourceType::Title => "title", + AxValueNativeSourceType::Other => "other", + } + } + } + impl ::std::str::FromStr for AxValueNativeSourceType { + type Err = String; + fn from_str(s: &str) -> Result { + match s { + "description" | "Description" => Ok(AxValueNativeSourceType::Description), + "figcaption" | "Figcaption" => Ok(AxValueNativeSourceType::Figcaption), + "label" | "Label" => Ok(AxValueNativeSourceType::Label), + "labelfor" | "Labelfor" => Ok(AxValueNativeSourceType::Labelfor), + "labelwrapped" | "Labelwrapped" => Ok(AxValueNativeSourceType::Labelwrapped), + "legend" | "Legend" => Ok(AxValueNativeSourceType::Legend), + "rubyannotation" | "Rubyannotation" => { + Ok(AxValueNativeSourceType::Rubyannotation) + } + "tablecaption" | "Tablecaption" => Ok(AxValueNativeSourceType::Tablecaption), + "title" | "Title" => Ok(AxValueNativeSourceType::Title), + "other" | "Other" => Ok(AxValueNativeSourceType::Other), + _ => Err(s.to_string()), + } + } + } + #[doc = "A single source for a computed AX property.\n[AXValueSource](https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/#type-AXValueSource)"] + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] + pub struct AxValueSource { + #[doc = "What type of source this is."] + #[serde(rename = "type")] + #[serde(deserialize_with = "super::super::de::deserialize_from_str")] + pub r#type: AxValueSourceType, + #[doc = "The value of this property source."] + #[serde(rename = "value")] + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + #[doc = "The name of the relevant attribute, if any."] + #[serde(rename = "attribute")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attribute: Option, + #[doc = "The value of the relevant attribute, if any."] + #[serde(rename = "attributeValue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attribute_value: Option, + #[doc = "Whether this source is superseded by a higher priority source."] + #[serde(rename = "superseded")] + #[serde(skip_serializing_if = "Option::is_none")] + pub superseded: Option, + #[doc = "The native markup source for this value, e.g. a `