From a4a68354d506aba9b5afb114c091236347db5145 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 14:17:04 +0200 Subject: [PATCH 01/42] cleanup --- 2026-04-16-cloudflare-driver.md | 535 ------------------------------- 2026-04-17_build-macos-binary.md | 287 ----------------- 2 files changed, 822 deletions(-) delete mode 100644 2026-04-16-cloudflare-driver.md delete mode 100644 2026-04-17_build-macos-binary.md 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 From 615d9aadef04a3d8933d8168cbcce5b6759fbc8f Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:06:55 +0200 Subject: [PATCH 02/42] build(browser): add aws-config, aws-sdk-devicefarm, thirtyfour deps --- Cargo.lock | 899 +++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 + browser/Cargo.toml | 3 + 3 files changed, 867 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebf7fac..32e8055 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,310 @@ 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 0.3.27", + "h2 0.4.14", + "http 0.2.12", + "http 1.4.0", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.9.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.9", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.40", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "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 +546,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 +635,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" @@ -430,6 +801,8 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" name = "client-simulator-browser" version = "0.3.4" dependencies = [ + "aws-config", + "aws-sdk-devicefarm", "chromiumoxide", "chrono", "client-simulator-config", @@ -441,6 +814,7 @@ dependencies = [ "serde", "serde_json", "strum 0.28.0", + "thirtyfour", "tokio", "tokio-util", "tracing", @@ -526,6 +900,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 +996,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 +1108,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 +1192,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 +1304,7 @@ dependencies = [ "block-buffer 0.12.0", "const-oid", "crypto-common 0.2.1", + "ctutils", ] [[package]] @@ -896,6 +1316,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 +1445,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 +1462,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 +1490,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 +1528,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" @@ -1228,6 +1695,25 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.14" @@ -1239,7 +1725,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -1294,6 +1780,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 +1810,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 +1828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -1322,8 +1839,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1333,6 +1850,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "human-panic" version = "2.0.8" @@ -1358,6 +1881,30 @@ dependencies = [ "typenum", ] +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.9.0" @@ -1368,9 +1915,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", - "http-body", + "h2 0.4.14", + "http 1.4.0", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -1401,18 +1948,34 @@ dependencies = [ "url", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http", - "hyper", + "http 1.4.0", + "hyper 1.9.0", "hyper-util", - "rustls", + "rustls 0.23.40", + "rustls-native-certs", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tower-service", ] @@ -1426,14 +1989,14 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.9.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.3", "system-configuration", "tokio", "tower-service", @@ -1780,6 +2343,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 +2519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2022,6 +2601,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 +2706,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 +2741,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 +2799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -2260,6 +2860,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 +2967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e349eed84b9a1a6a5dbe478d335e3df73d32a93c5eefe571c9b8cb298aab5d" dependencies = [ "heck", - "http", + "http 1.4.0", "indexmap", "openapiv3", "proc-macro2", @@ -2422,8 +3028,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls", - "socket2", + "rustls 0.23.40", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -2443,7 +3049,7 @@ dependencies = [ "rand 0.9.4", "ring", "rustc-hash", - "rustls", + "rustls 0.23.40", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -2461,7 +3067,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -2690,6 +3296,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" @@ -2720,12 +3332,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.14", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", - "hyper", - "hyper-rustls", + "hyper 1.9.0", + "hyper-rustls 0.27.9", "hyper-util", "js-sys", "log", @@ -2733,7 +3345,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls", + "rustls 0.23.40", "rustls-pki-types", "rustls-platform-verifier", "serde", @@ -2741,7 +3353,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tokio-util", "tower", "tower-http", @@ -2814,6 +3426,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.40" @@ -2823,7 +3447,7 @@ dependencies = [ "aws-lc-rs", "once_cell", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.13", "subtle", "zeroize", ] @@ -2861,10 +3485,10 @@ dependencies = [ "jni", "log", "once_cell", - "rustls", + "rustls 0.23.40", "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki", + "rustls-webpki 0.103.13", "security-framework", "security-framework-sys", "webpki-root-certs", @@ -2877,6 +3501,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.13" @@ -2951,6 +3585,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "3.7.0" @@ -3031,6 +3675,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap", "itoa", "memchr", "serde", @@ -3038,6 +3683,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 +3773,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 +3840,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" @@ -3207,6 +3880,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.3" @@ -3229,6 +3912,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 +4061,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 +4136,7 @@ dependencies = [ "pest_derive", "phf", "serde", - "sha2", + "sha2 0.10.9", "signal-hook 0.3.18", "siphasher", "terminfo", @@ -3450,6 +4153,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" @@ -3568,7 +4315,7 @@ dependencies = [ "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] @@ -3584,13 +4331,23 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls", + "rustls 0.23.40", "tokio", ] @@ -3658,8 +4415,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 +4539,7 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http", + "http 1.4.0", "httparse", "log", "rand 0.9.4", @@ -3791,6 +4548,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 +4673,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 +4722,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 +4939,7 @@ dependencies = [ "getrandom 0.3.4", "mac_address", "serde", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "uuid", ] @@ -4654,6 +5429,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 +5578,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..87345c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ 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", "rustls"] } +aws-sdk-devicefarm = { version = "1", default-features = false, features = ["behavior-version-latest", "rt-tokio", "rustls"] } better-panic = "0.3.0" chromiumoxide = { version = "0.9.1", features = ["bytes"], default-features = false } chrono = { version = "0.4.44", features = ["serde"] } @@ -41,6 +43,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" diff --git a/browser/Cargo.toml b/browser/Cargo.toml index 3fa0f8b..453fff4 100644 --- a/browser/Cargo.toml +++ b/browser/Cargo.toml @@ -6,6 +6,8 @@ edition.workspace = true repository.workspace = true [dependencies] +aws-config.workspace = true +aws-sdk-devicefarm.workspace = true chromiumoxide.workspace = true chrono.workspace = true derive_more.workspace = true @@ -15,6 +17,7 @@ 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 From 4b65ea5d1f2b4ea27ef31e8cb3ddf43ec4f30cfe Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:09:54 +0200 Subject: [PATCH 03/42] test(browser): pin local command symbols before frontend extraction --- browser/src/participant/local/commands.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/browser/src/participant/local/commands.rs b/browser/src/participant/local/commands.rs index 2fb9bfa..bbb63bf 100644 --- a/browser/src/participant/local/commands.rs +++ b/browser/src/participant/local/commands.rs @@ -133,3 +133,13 @@ create_eval_setter!( set_force_webrtc, "function f(value) { return hyper.settings.sessionDebug.actions.setForceWebrtc(value); }" ); + +#[cfg(test)] +mod tests { + #[tokio::test] + async fn module_exports_getters_and_setters() { + // Compile-time pin: these symbols must continue to exist after the refactor. + let _ = super::get_noise_suppression; + let _ = super::set_noise_suppression::; + } +} From 5f1dfb71374fb35f78d288fe3d8fa982d001a818 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:10:39 +0200 Subject: [PATCH 04/42] refactor(browser): add driver-agnostic BrowserDriver trait + FrontendContext --- browser/src/participant/frontend/driver.rs | 80 ++++++++++++++++++++++ browser/src/participant/frontend/mod.rs | 11 +++ browser/src/participant/mod.rs | 1 + 3 files changed, 92 insertions(+) create mode 100644 browser/src/participant/frontend/driver.rs create mode 100644 browser/src/participant/frontend/mod.rs 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/frontend/mod.rs b/browser/src/participant/frontend/mod.rs new file mode 100644 index 0000000..4cc389e --- /dev/null +++ b/browser/src/participant/frontend/mod.rs @@ -0,0 +1,11 @@ +//! Driver-agnostic Hyper frontend automation, shared by the Local (chromiumoxide) +//! and Device Farm (WebDriver) backends. + +mod driver; + +pub(in crate::participant) use driver::{ + decode_test_state, + BrowserDriver, + FrontendAutomation, + FrontendContext, +}; diff --git a/browser/src/participant/mod.rs b/browser/src/participant/mod.rs index 7776c31..4a77863 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -47,6 +47,7 @@ use tokio_util::sync::{ }; mod cloudflare; +mod frontend; mod local; mod remote_stub; pub mod shared; From 45e6b2316db7f92130f257a48be21fe38b6244ae Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:11:13 +0200 Subject: [PATCH 05/42] refactor(browser): move selectors + retarget commands to BrowserDriver [wip] --- browser/src/participant/frontend/commands.rs | 83 ++++++++++ .../{local => frontend}/selectors.rs | 0 browser/src/participant/local/commands.rs | 145 ------------------ 3 files changed, 83 insertions(+), 145 deletions(-) create mode 100644 browser/src/participant/frontend/commands.rs rename browser/src/participant/{local => frontend}/selectors.rs (100%) delete mode 100644 browser/src/participant/local/commands.rs diff --git a/browser/src/participant/frontend/commands.rs b/browser/src/participant/frontend/commands.rs new file mode 100644 index 0000000..31f1fa8 --- /dev/null +++ b/browser/src/participant/frontend/commands.rs @@ -0,0 +1,83 @@ +use super::driver::BrowserDriver; +use client_simulator_config::{ + NoiseSuppression, + WebcamResolution, +}; +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 CAMERA_RES_GET: &str = "return hyper.settings.videoCodec.videoResolutionForWebcamEncoder.name;"; +const CAMERA_RES_SET: &str = "hyper.settings.videoCodec.actions.setVideoResolutionForWebcamEncoder(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_outgoing_camera_resolution(driver: &dyn BrowserDriver) -> Result { + get_string(driver, CAMERA_RES_GET) + .await? + .parse::() + .context("failed to parse WebcamResolution from string") +} + +pub(super) async fn set_outgoing_camera_resolution(driver: &dyn BrowserDriver, value: &WebcamResolution) -> Result<()> { + set_value(driver, CAMERA_RES_SET, value.to_string()).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 +} 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/commands.rs b/browser/src/participant/local/commands.rs deleted file mode 100644 index bbb63bf..0000000 --- a/browser/src/participant/local/commands.rs +++ /dev/null @@ -1,145 +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); }" -); - -#[cfg(test)] -mod tests { - #[tokio::test] - async fn module_exports_getters_and_setters() { - // Compile-time pin: these symbols must continue to exist after the refactor. - let _ = super::get_noise_suppression; - let _ = super::set_noise_suppression::; - } -} From 12648ec30fa8b463aa59c8dd5c3a2ae14c1bfa7e Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:13:27 +0200 Subject: [PATCH 06/42] refactor(browser): move HyperCore/HyperLite automation behind BrowserDriver [wip] --- browser/src/participant/frontend/builder.rs | 62 +++++++ .../participant/{local => frontend}/core.rs | 157 ++++++++---------- .../participant/{local => frontend}/lite.rs | 151 ++++++++--------- browser/src/participant/frontend/mod.rs | 9 + browser/src/participant/local/frontend.rs | 64 ------- 5 files changed, 209 insertions(+), 234 deletions(-) create mode 100644 browser/src/participant/frontend/builder.rs rename browser/src/participant/{local => frontend}/core.rs (68%) rename browser/src/participant/{local => frontend}/lite.rs (82%) delete mode 100644 browser/src/participant/local/frontend.rs 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/local/core.rs b/browser/src/participant/frontend/core.rs similarity index 68% rename from browser/src/participant/local/core.rs rename to browser/src/participant/frontend/core.rs index 6c443fd..b4acff4 100644 --- a/browser/src/participant/local/core.rs +++ b/browser/src/participant/frontend/core.rs @@ -17,18 +17,14 @@ use super::{ set_noise_suppression, set_outgoing_camera_resolution, }, - 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, @@ -57,12 +53,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 +82,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 +110,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 +123,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 +145,21 @@ 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_outgoing_camera_resolution(driver, &settings.resolution) .await .context("failed to set outgoing camera resolution")?; - set_force_webrtc(&self.context.page, settings.transport == TransportMode::WebRTC) + set_force_webrtc(driver, settings.transport == TransportMode::WebRTC) .await .context("failed to set transport mode")?; @@ -177,9 +179,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 +192,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 +205,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,9 +218,9 @@ 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(|_| ()) @@ -227,7 +229,7 @@ impl ParticipantInner { async fn set_webcam_resolutions_inner(&self, value: WebcamResolution) -> Result<()> { debug!(participant = %self.participant_name(), "Changing to {value} resolution"); - set_outgoing_camera_resolution(&self.context.page, &value) + set_outgoing_camera_resolution(self.context.driver.as_ref(), &value) .await .context("Failed to set outgoing camera resolution")?; @@ -242,7 +244,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 +252,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 +279,43 @@ 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 { + if let Ok(value) = get_outgoing_camera_resolution(driver).await { state.webcam_resolution = 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; } diff --git a/browser/src/participant/local/lite.rs b/browser/src/participant/frontend/lite.rs similarity index 82% rename from browser/src/participant/local/lite.rs rename to browser/src/participant/frontend/lite.rs index e08ec41..d0a569c 100644 --- a/browser/src/participant/local/lite.rs +++ b/browser/src/participant/frontend/lite.rs @@ -5,17 +5,14 @@ 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, @@ -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"); @@ -293,16 +291,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,59 +317,39 @@ 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(); - Ok(element_state(&button).await.or(aria_pressed(&button).await)) + Ok(decode_test_state( + driver + .attribute(lite::SCREEN_SHARE_BUTTON, "data-test-state") + .await?, + ) + .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, @@ -400,17 +379,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( diff --git a/browser/src/participant/frontend/mod.rs b/browser/src/participant/frontend/mod.rs index 4cc389e..9fc6ab3 100644 --- a/browser/src/participant/frontend/mod.rs +++ b/browser/src/participant/frontend/mod.rs @@ -1,8 +1,17 @@ //! 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::{ decode_test_state, BrowserDriver, 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") -} From 7328970e4c188f785f4896f3d1a7fabcb0b964b3 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:16:40 +0200 Subject: [PATCH 07/42] refactor(browser): drive Local backend through ChromiumDriver (no behavior change) --- browser/src/lib.rs | 1 - browser/src/participant/frontend/lite.rs | 8 +- browser/src/participant/frontend/mod.rs | 1 - .../src/participant/local/chromium_driver.rs | 158 ++++++++++++++++++ browser/src/participant/local/mod.rs | 6 +- browser/src/participant/local/session.rs | 63 ++----- browser/src/util.rs | 24 --- 7 files changed, 178 insertions(+), 83 deletions(-) create mode 100644 browser/src/participant/local/chromium_driver.rs delete mode 100644 browser/src/util.rs diff --git a/browser/src/lib.rs b/browser/src/lib.rs index 5181d8a..cc1def2 100644 --- a/browser/src/lib.rs +++ b/browser/src/lib.rs @@ -3,4 +3,3 @@ extern crate tracing; pub mod auth; pub mod participant; -pub mod util; diff --git a/browser/src/participant/frontend/lite.rs b/browser/src/participant/frontend/lite.rs index d0a569c..fbf44dd 100644 --- a/browser/src/participant/frontend/lite.rs +++ b/browser/src/participant/frontend/lite.rs @@ -339,13 +339,9 @@ impl ParticipantInnerLite { async fn screen_share_enabled(&self) -> Result> { let driver = self.context.driver.as_ref(); + let data_test_state = driver.attribute(lite::SCREEN_SHARE_BUTTON, "data-test-state").await?; - Ok(decode_test_state( - driver - .attribute(lite::SCREEN_SHARE_BUTTON, "data-test-state") - .await?, - ) - .or(aria_pressed(driver, lite::SCREEN_SHARE_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 { diff --git a/browser/src/participant/frontend/mod.rs b/browser/src/participant/frontend/mod.rs index 9fc6ab3..daca152 100644 --- a/browser/src/participant/frontend/mod.rs +++ b/browser/src/participant/frontend/mod.rs @@ -13,7 +13,6 @@ pub(in crate::participant) use builder::{ FrontendKindBuilder, }; pub(in crate::participant) use driver::{ - decode_test_state, BrowserDriver, FrontendAutomation, FrontendContext, 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/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..3a2373f 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, }, }, }; @@ -72,7 +70,7 @@ pub(crate) struct LocalChromiumSession { launch_spec: ParticipantLaunchSpec, browser_config: BrowserConfig, sender: UnboundedSender, - frontend_builder: Option, + frontend_builder: Option, automation: Option>, browser: Option, page: Option, @@ -82,14 +80,6 @@ pub(crate) struct LocalChromiumSession { termination_rx: watch::Receiver>, } -enum LocalFrontendBuilder { - HyperCore { - auth: Option, - cookie_manager: HyperSessionCookieManger, - }, - HyperLite, -} - impl LocalChromiumSession { pub(crate) fn new( launch_spec: ParticipantLaunchSpec, @@ -98,10 +88,7 @@ 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); Self { @@ -137,17 +124,19 @@ impl LocalChromiumSession { let detached_target_task = drive_detached_target_events(&self.launch_spec.username, &mut browser, self.termination_tx.clone()).await?; - let frontend_builder = self + 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); @@ -283,24 +272,6 @@ 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"]; #[cfg(any(test, target_os = "macos"))] const MACOS_GOOGLE_CHROME_APP_BINARY: &str = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; 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)); - } - } -} From 2c962999fb383a34456777a62bbe0ae5a36aeeb7 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:33:42 +0200 Subject: [PATCH 08/42] feat(browser): add WebDriverDriver (thirtyfour) BrowserDriver impl --- browser/src/participant/device_farm/mod.rs | 4 + .../device_farm/webdriver_driver.rs | 155 ++++++++++++++++++ browser/src/participant/mod.rs | 1 + 3 files changed, 160 insertions(+) create mode 100644 browser/src/participant/device_farm/mod.rs create mode 100644 browser/src/participant/device_farm/webdriver_driver.rs diff --git a/browser/src/participant/device_farm/mod.rs b/browser/src/participant/device_farm/mod.rs new file mode 100644 index 0000000..e0d93b5 --- /dev/null +++ b/browser/src/participant/device_farm/mod.rs @@ -0,0 +1,4 @@ +mod webdriver_driver; + +#[allow(unused_imports)] +pub(crate) use webdriver_driver::WebDriverDriver; 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/mod.rs b/browser/src/participant/mod.rs index 4a77863..b0fbaa6 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -47,6 +47,7 @@ use tokio_util::sync::{ }; mod cloudflare; +mod device_farm; mod frontend; mod local; mod remote_stub; From 9b482cac02a264f71080d2c39d3d402b86bf1b30 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:37:42 +0200 Subject: [PATCH 09/42] feat(config): add DeviceFarmConfig --- config/src/device_farm_config.rs | 65 ++++++++++++++++++++++++++++++++ config/src/lib.rs | 2 + 2 files changed, 67 insertions(+) create mode 100644 config/src/device_farm_config.rs diff --git a/config/src/device_farm_config.rs b/config/src/device_farm_config.rs new file mode 100644 index 0000000..d276150 --- /dev/null +++ b/config/src/device_farm_config.rs @@ -0,0 +1,65 @@ +use serde::{ + Deserialize, + Serialize, +}; + +const DEFAULT_REGION: &str = "us-west-2"; + +/// Configuration for the AWS Device Farm desktop-browser ("Test Grid") backend. +/// +/// Credentials are NOT stored here; they are resolved from the standard AWS +/// credential chain (env vars / shared profile) by `aws-config` at runtime. +#[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, + pub navigation_timeout_ms: u64, + pub selector_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: String::new(), + region: DEFAULT_REGION.to_owned(), + url_expires_seconds: 300, + session_max_duration_ms: 1_800_000, + idle_timeout_ms: 180_000, + navigation_timeout_ms: 45_000, + selector_timeout_ms: 20_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, "us-west-2"); + 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..bef897b 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; @@ -30,6 +31,7 @@ pub use client_config::{ WebcamResolutionIter, }; pub use cloudflare_config::CloudflareConfig; +pub use device_farm_config::DeviceFarmConfig; use color_eyre::Result; use eyre::Context as _; pub use participant_config::{ From 2e00441527d4cf9b1ef2ff98ce4dfc7d6daf0cbb Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:38:22 +0200 Subject: [PATCH 10/42] feat(config): add AwsDeviceFarm participant backend kind --- config/src/client_config.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/config/src/client_config.rs b/config/src/client_config.rs index 9544125..b9d672e 100644 --- a/config/src/client_config.rs +++ b/config/src/client_config.rs @@ -98,6 +98,7 @@ pub enum ParticipantBackendKind { Local, Cloudflare, RemoteStub, + AwsDeviceFarm, } impl ParticipantBackendKind { @@ -105,3 +106,17 @@ impl ParticipantBackendKind { matches!(self, Self::Local) } } + +#[cfg(test)] +mod tests { + use super::ParticipantBackendKind; + use std::str::FromStr as _; + + #[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()); + } +} From d01ff2332e7a0e79d299105326809aa5806a3e68 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:39:54 +0200 Subject: [PATCH 11/42] feat(config): wire device_farm config into Config + defaults --- browser/src/participant/mod.rs | 1 + config/src/default-config.yaml | 10 +++++ config/src/lib.rs | 70 +++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/browser/src/participant/mod.rs b/browser/src/participant/mod.rs index b0fbaa6..ed66f36 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -85,6 +85,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 => eyre::bail!("AWS Device Farm backend is not implemented yet"), } } diff --git a/config/src/default-config.yaml b/config/src/default-config.yaml index 0ea0fb8..20df613 100644 --- a/config/src/default-config.yaml +++ b/config/src/default-config.yaml @@ -33,6 +33,16 @@ 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 + navigation_timeout_ms: 45000 + selector_timeout_ms: 20000 + health_poll_interval_ms: 30000 + debug: false audio_enabled: true video_enabled: true auto_gain_control: true diff --git a/config/src/lib.rs b/config/src/lib.rs index bef897b..b86312e 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -31,8 +31,8 @@ pub use client_config::{ WebcamResolutionIter, }; pub use cloudflare_config::CloudflareConfig; -pub use device_farm_config::DeviceFarmConfig; use color_eyre::Result; +pub use device_farm_config::DeviceFarmConfig; use eyre::Context as _; pub use participant_config::{ generate_random_name, @@ -63,6 +63,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)] @@ -131,6 +133,38 @@ 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()), + ( + "navigation_timeout_ms".to_string(), + self.device_farm.navigation_timeout_ms.into(), + ), + ( + "selector_timeout_ms".to_string(), + self.device_farm.selector_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()); } @@ -347,4 +381,38 @@ 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 + navigation_timeout_ms: 45000 + selector_timeout_ms: 20000 + 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); + } } From e5616eb1b10bab9d41991ac89a81cd2925845e76 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:48:26 +0200 Subject: [PATCH 12/42] feat(browser): add TestGridApi seam over aws-sdk-devicefarm --- browser/src/participant/device_farm/mod.rs | 6 +++ .../src/participant/device_farm/test_grid.rs | 54 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 browser/src/participant/device_farm/test_grid.rs diff --git a/browser/src/participant/device_farm/mod.rs b/browser/src/participant/device_farm/mod.rs index e0d93b5..496b2d1 100644 --- a/browser/src/participant/device_farm/mod.rs +++ b/browser/src/participant/device_farm/mod.rs @@ -1,4 +1,10 @@ +mod test_grid; mod webdriver_driver; +#[allow(unused_imports)] +pub(crate) use test_grid::{ + AwsTestGrid, + TestGridApi, +}; #[allow(unused_imports)] pub(crate) use webdriver_driver::WebDriverDriver; 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..0f01689 --- /dev/null +++ b/browser/src/participant/device_farm/test_grid.rs @@ -0,0 +1,54 @@ +use eyre::{ + 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(crate) 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>; +} + +/// Real implementation backed by `aws-sdk-devicefarm`. +pub(crate) struct AwsTestGrid { + client: aws_sdk_devicefarm::Client, +} + +impl AwsTestGrid { + pub(crate) async fn new(region: &str) -> Self { + let region = aws_sdk_devicefarm::config::Region::new(region.to_owned()); + let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .region(region) + .load() + .await; + Self { + client: aws_sdk_devicefarm::Client::new(&config), + } + } +} + +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 + .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() + } +} From e42eb0f215284550e2a6f4e5ff6647666ca18b36 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:50:35 +0200 Subject: [PATCH 13/42] feat(browser): implement DeviceFarmSession lifecycle + keep-alive poller --- browser/src/participant/device_farm/mod.rs | 326 ++++++++++++++++++ .../src/participant/device_farm/test_grid.rs | 2 + 2 files changed, 328 insertions(+) diff --git a/browser/src/participant/device_farm/mod.rs b/browser/src/participant/device_farm/mod.rs index 496b2d1..5362e05 100644 --- a/browser/src/participant/device_farm/mod.rs +++ b/browser/src/participant/device_farm/mod.rs @@ -8,3 +8,329 @@ pub(crate) use test_grid::{ }; #[allow(unused_imports)] pub(crate) use webdriver_driver::WebDriverDriver; + +use crate::{ + auth::{ + BorrowedCookie, + HyperSessionCookieManger, + }, + participant::{ + frontend::{ + FrontendAuth, + FrontendAutomation, + FrontendContext, + FrontendKindBuilder, + }, + shared::{ + messages::{ + ParticipantLogMessage, + ParticipantMessage, + }, + DriverTermination, + ParticipantDriverSession, + ParticipantLaunchSpec, + ParticipantState, + }, + }, +}; +use client_simulator_config::{ + media::FakeMedia, + DeviceFarmConfig, +}; +use eyre::{ + bail, + Context as _, + ContextCompat as _, + Result, +}; +use futures::{ + future::BoxFuture, + FutureExt as _, +}; +use std::{ + sync::Arc, + time::Duration, +}; +use thirtyfour::{ + CapabilitiesHelper, + ChromeCapabilities, + ChromiumLikeCapabilities, + DesiredCapabilities, + WebDriver, +}; +use tokio::{ + sync::{ + mpsc::UnboundedSender, + oneshot, + watch, + }, + task::JoinHandle, + time::MissedTickBehavior, +}; + +#[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>, + 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, + 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!((config.session_max_duration_ms / 1000).max(1)), + ); + caps.insert_base_capability( + "aws:idleTimeoutSecs".to_string(), + serde_json::json!((config.idle_timeout_ms / 1000).max(1)), + ); + 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)?; + // Device Farm can take 60-120s before a requested session becomes usable. + WebDriver::new(&url, caps) + .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?; + 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.start_keep_alive_poller(); + if let Err(err) = automation.join().await { + self.stop_keep_alive_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 = self.config.idle_timeout_ms.saturating_div(2).max(1); + Duration::from_millis(configured.min(budget)) + } + + fn start_keep_alive_poller(&mut self) { + // The WebDriver is owned by frontend automation, so Phase 4 keeps + // reclamation implicit: Device Farm reclaims an abandoned session after + // aws:idleTimeoutSecs, and hard-stops it at aws:maxDurationSecs. + let interval = self.effective_poll_interval(); + let max_duration = Duration::from_millis(self.config.session_max_duration_ms); + 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_keep_alive_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_keep_alive_poller().await; + + if let Some(mut automation) = self.automation.take() { + let joined = automation.refresh_state().await.map(|state| state.joined).unwrap_or(false); + if joined { + if let Err(err) = automation.leave().await { + self.log_message("error", format!("Failed leaving space while closing: {err}")); + } + } + } + + 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 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"); + } + } + } +} + +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 { + match self.automation.as_mut() { + Some(automation) => { + let state = automation.refresh_state().await?; + self.cached_state = state.clone(); + Ok(state) + } + None => Ok(self.cached_state.clone()), + } + } + .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() + } +} diff --git a/browser/src/participant/device_farm/test_grid.rs b/browser/src/participant/device_farm/test_grid.rs index 0f01689..49824a2 100644 --- a/browser/src/participant/device_farm/test_grid.rs +++ b/browser/src/participant/device_farm/test_grid.rs @@ -15,10 +15,12 @@ pub(crate) trait TestGridApi: Send + Sync { } /// Real implementation backed by `aws-sdk-devicefarm`. +#[allow(dead_code)] pub(crate) struct AwsTestGrid { client: aws_sdk_devicefarm::Client, } +#[allow(dead_code)] impl AwsTestGrid { pub(crate) async fn new(region: &str) -> Self { let region = aws_sdk_devicefarm::config::Region::new(region.to_owned()); From 922429e60c4b32ff175e6d67989ba051ec499e8a Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 15:55:56 +0200 Subject: [PATCH 14/42] feat(browser): wire AwsDeviceFarm backend into Participant::spawn --- .../src/participant/device_farm/test_grid.rs | 32 ++++++++---- browser/src/participant/mod.rs | 51 ++++++++++++++++++- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/browser/src/participant/device_farm/test_grid.rs b/browser/src/participant/device_farm/test_grid.rs index 49824a2..6567261 100644 --- a/browser/src/participant/device_farm/test_grid.rs +++ b/browser/src/participant/device_farm/test_grid.rs @@ -15,23 +15,32 @@ pub(crate) trait TestGridApi: Send + Sync { } /// Real implementation backed by `aws-sdk-devicefarm`. -#[allow(dead_code)] pub(crate) struct AwsTestGrid { - client: aws_sdk_devicefarm::Client, + region: String, + client: tokio::sync::OnceCell, } -#[allow(dead_code)] impl AwsTestGrid { - pub(crate) async fn new(region: &str) -> Self { - let region = aws_sdk_devicefarm::config::Region::new(region.to_owned()); - let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) - .region(region) - .load() - .await; + pub(crate) fn new(region: &str) -> Self { Self { - client: aws_sdk_devicefarm::Client::new(&config), + 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 config = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .region(region) + .load() + .await; + aws_sdk_devicefarm::Client::new(&config) + }) + .await + } } impl TestGridApi for AwsTestGrid { @@ -39,7 +48,8 @@ impl TestGridApi for AwsTestGrid { let project_arn = project_arn.to_owned(); async move { let output = self - .client + .client() + .await .create_test_grid_url() .project_arn(project_arn) .expires_in_seconds(expires_seconds as i32) diff --git a/browser/src/participant/mod.rs b/browser/src/participant/mod.rs index ed66f36..7c406fd 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -85,7 +85,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 => eyre::bail!("AWS Device Farm backend is not implemented yet"), + ParticipantBackendKind::AwsDeviceFarm => Self::spawn_device_farm(config, cookie_manager), } } @@ -196,6 +196,55 @@ impl Participant { sender, }) } + + pub fn spawn_device_farm(config: &Config, cookie_manager: HyperSessionCookieManger) -> Result { + use crate::participant::device_farm::{ + AwsTestGrid, + 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 api = Arc::new(AwsTestGrid::new(&device_farm_config.region)); + + 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_guard: task_guard, + sender, + }) + } } fn spawn_session( From befd16f7e563fccaf5dc5fbfb9697180ef77a734 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 16:01:12 +0200 Subject: [PATCH 15/42] test(browser): integration test for Device Farm session lifecycle --- browser/src/lib.rs | 5 + browser/src/participant/device_farm/mod.rs | 11 +- .../src/participant/device_farm/test_grid.rs | 2 +- browser/src/participant/mod.rs | 15 +- browser/tests/device_farm_driver.rs | 362 ++++++++++++++++++ 5 files changed, 390 insertions(+), 5 deletions(-) create mode 100644 browser/tests/device_farm_driver.rs diff --git a/browser/src/lib.rs b/browser/src/lib.rs index cc1def2..dca5811 100644 --- a/browser/src/lib.rs +++ b/browser/src/lib.rs @@ -3,3 +3,8 @@ extern crate tracing; pub mod auth; pub mod participant; + +#[doc(hidden)] +pub mod testing { + pub use crate::participant::device_farm::TestGridApi; +} diff --git a/browser/src/participant/device_farm/mod.rs b/browser/src/participant/device_farm/mod.rs index 5362e05..10b267b 100644 --- a/browser/src/participant/device_farm/mod.rs +++ b/browser/src/participant/device_farm/mod.rs @@ -2,9 +2,9 @@ mod test_grid; mod webdriver_driver; #[allow(unused_imports)] +pub use test_grid::TestGridApi; pub(crate) use test_grid::{ AwsTestGrid, - TestGridApi, }; #[allow(unused_imports)] pub(crate) use webdriver_driver::WebDriverDriver; @@ -93,6 +93,7 @@ pub(super) struct DeviceFarmSession { api: Arc, auth: Option, automation: Option>, + webdriver: Option, cached_state: ParticipantState, termination_tx: watch::Sender>, termination_rx: watch::Receiver>, @@ -126,6 +127,7 @@ impl DeviceFarmSession { api, auth: Some(auth), automation: None, + webdriver: None, termination_tx, termination_rx, poller_shutdown_tx: None, @@ -191,6 +193,7 @@ impl DeviceFarmSession { ), ); 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")?; @@ -280,6 +283,12 @@ impl DeviceFarmSession { } } + if let Some(driver) = self.webdriver.take() { + if let Err(err) = driver.quit().await { + self.log_message("error", format!("Failed closing WebDriver session: {err}")); + } + } + self.cached_state.running = false; self.cached_state.joined = false; self.cached_state.screenshare_activated = false; diff --git a/browser/src/participant/device_farm/test_grid.rs b/browser/src/participant/device_farm/test_grid.rs index 6567261..c488116 100644 --- a/browser/src/participant/device_farm/test_grid.rs +++ b/browser/src/participant/device_farm/test_grid.rs @@ -9,7 +9,7 @@ use futures::{ /// Minimal seam over the Device Farm Test Grid control plane so the session /// logic can be tested without real AWS calls. -pub(crate) trait TestGridApi: Send + Sync { +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>; } diff --git a/browser/src/participant/mod.rs b/browser/src/participant/mod.rs index 7c406fd..dd696a5 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -47,7 +47,7 @@ use tokio_util::sync::{ }; mod cloudflare; -mod device_farm; +pub mod device_farm; mod frontend; mod local; mod remote_stub; @@ -198,8 +198,18 @@ impl Participant { } 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::{ - AwsTestGrid, DeviceFarmLaunchOptions, DeviceFarmSession, }; @@ -220,7 +230,6 @@ impl Participant { let (sender, receiver) = unbounded_channel::(); let (log_sender, _log_receiver) = unbounded_channel::(); - let api = Arc::new(AwsTestGrid::new(&device_farm_config.region)); let (state_receiver, task_guard) = spawn_session( name.clone(), diff --git a/browser/tests/device_farm_driver.rs b/browser/tests/device_farm_driver.rs new file mode 100644 index 0000000..4ac763d --- /dev/null +++ b/browser/tests/device_farm_driver.rs @@ -0,0 +1,362 @@ +//! 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::{ + 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); + + 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"))); +} + +#[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, + navigation_timeout_ms: 45_000, + selector_timeout_ms: 20_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, +} + +async fn spawn_webdriver_mock() -> (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 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); + requests_for_task.lock().unwrap().push(request); + write_response(&mut stream, response).await; + } + }); + + (base_url, requests, task) +} + +fn webdriver_response(request: &CapturedRequest, state: &Arc>) -> MockResponse { + match (request.method.as_str(), request.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") => { + MockResponse::json(200, json!({ "value": "https://example.com/m/demo" })) + } + ("DELETE", "/session/df-1") => MockResponse::json(200, json!({ "value": null })), + ("POST", "/session/df-1/element") => element_response(request, state), + _ if request.path.starts_with("/session/df-1/element/") => element_command_response(request, state), + _ if request.path == "/session/df-1/execute/sync" => MockResponse::json(200, json!({ "value": null })), + _ => MockResponse::json(200, json!({ "value": null })), + } +} + +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(request: &CapturedRequest, state: &Arc>) -> MockResponse { + if request.path.ends_with("/click") { + if request.path.contains("/element/join/") { + state.lock().unwrap().joined = true; + } else if request.path.contains("/element/leave/") || request.path.contains("/element/confirm/") { + state.lock().unwrap().joined = false; + } + return MockResponse::json(200, json!({ "value": null })); + } + + if let Some(attribute) = request.path.rsplit('/').next().filter(|_| request.path.contains("/attribute/")) { + let value = match attribute { + "data-test-state" if request.path.contains("/element/audio/") => json!("true"), + "data-test-state" if request.path.contains("/element/video/") => json!("true"), + "data-test-state" if request.path.contains("/element/screen/") => json!("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 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", + } +} From 4a4474b4d33d7fcf0a567031807a8d8b0f9e061a Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Fri, 29 May 2026 16:11:10 +0200 Subject: [PATCH 16/42] style: cargo fmt --- browser/src/participant/device_farm/mod.rs | 19 ++++++++++--------- browser/src/participant/mod.rs | 4 +++- browser/tests/device_farm_driver.rs | 11 +++++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/browser/src/participant/device_farm/mod.rs b/browser/src/participant/device_farm/mod.rs index 10b267b..b339da1 100644 --- a/browser/src/participant/device_farm/mod.rs +++ b/browser/src/participant/device_farm/mod.rs @@ -1,14 +1,6 @@ mod test_grid; mod webdriver_driver; -#[allow(unused_imports)] -pub use test_grid::TestGridApi; -pub(crate) use test_grid::{ - AwsTestGrid, -}; -#[allow(unused_imports)] -pub(crate) use webdriver_driver::WebDriverDriver; - use crate::{ auth::{ BorrowedCookie, @@ -51,6 +43,9 @@ use std::{ sync::Arc, time::Duration, }; +pub(crate) use test_grid::AwsTestGrid; +#[allow(unused_imports)] +pub use test_grid::TestGridApi; use thirtyfour::{ CapabilitiesHelper, ChromeCapabilities, @@ -67,6 +62,8 @@ use tokio::{ task::JoinHandle, time::MissedTickBehavior, }; +#[allow(unused_imports)] +pub(crate) use webdriver_driver::WebDriverDriver; #[allow(dead_code)] #[derive(Debug, Clone, PartialEq)] @@ -275,7 +272,11 @@ impl DeviceFarmSession { self.stop_keep_alive_poller().await; if let Some(mut automation) = self.automation.take() { - let joined = automation.refresh_state().await.map(|state| state.joined).unwrap_or(false); + let joined = automation + .refresh_state() + .await + .map(|state| state.joined) + .unwrap_or(false); if joined { if let Err(err) = automation.leave().await { self.log_message("error", format!("Failed leaving space while closing: {err}")); diff --git a/browser/src/participant/mod.rs b/browser/src/participant/mod.rs index dd696a5..14f7d86 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -199,7 +199,9 @@ impl Participant { 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)); + let api = Arc::new(crate::participant::device_farm::AwsTestGrid::new( + &device_farm_config.region, + )); Self::spawn_device_farm_with_api(config, cookie_manager, api) } diff --git a/browser/tests/device_farm_driver.rs b/browser/tests/device_farm_driver.rs index 4ac763d..323247e 100644 --- a/browser/tests/device_farm_driver.rs +++ b/browser/tests/device_farm_driver.rs @@ -170,9 +170,7 @@ fn webdriver_response(request: &CapturedRequest, state: &Arc MockResponse::json(200, json!({ "value": null })), - ("GET", "/session/df-1/url") => { - MockResponse::json(200, json!({ "value": "https://example.com/m/demo" })) - } + ("GET", "/session/df-1/url") => MockResponse::json(200, json!({ "value": "https://example.com/m/demo" })), ("DELETE", "/session/df-1") => MockResponse::json(200, json!({ "value": null })), ("POST", "/session/df-1/element") => element_response(request, state), _ if request.path.starts_with("/session/df-1/element/") => element_command_response(request, state), @@ -224,7 +222,12 @@ fn element_command_response(request: &CapturedRequest, state: &Arc json!("true"), "data-test-state" if request.path.contains("/element/video/") => json!("true"), From 3f776d6fc2cdcb242a681ba0fa0ca556dd1eb646 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Tue, 2 Jun 2026 10:08:33 +0200 Subject: [PATCH 17/42] plan 1 --- 2026-06-01-no-tui-mode.md | 874 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 874 insertions(+) create mode 100644 2026-06-01-no-tui-mode.md diff --git a/2026-06-01-no-tui-mode.md b/2026-06-01-no-tui-mode.md new file mode 100644 index 0000000..a29ec47 --- /dev/null +++ b/2026-06-01-no-tui-mode.md @@ -0,0 +1,874 @@ +# Headless Command Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add `hyper-client-simulator headless` so simulator participants can be started directly from CLI arguments without entering the TUI or writing runtime options back to the app config. + +**Architecture:** Keep the TUI path unchanged. Add a headless runner in the root binary that loads persisted config plus global defaults, overlays command-line settings, overlays each `--participant` JSON object on top of that, spawns participants through `ParticipantStore`, streams tracing output to standard streams, and coordinates graceful/forced shutdown. + +**Tech Stack:** Rust 2021, `clap`, `serde`, `serde_json`, `config`, `tokio`, `tracing-subscriber`, existing `client-simulator-config` and `client-simulator-browser` crates. + +--- + +## Design Decisions + +- `headless` means "no TUI". It should not implicitly persist settings. It should respect the existing browser `headless` config value unless `--headless true|false` or a participant JSON `{"headless": true}` overrides it. +- If no `--participant` argument is supplied, start one participant using the global effective config. If one or more `--participant` values are supplied, start exactly one participant per JSON object. +- Participant JSON supports the same launch-time fields exposed as global headless CLI overrides: `url`, `backend`, `headless`, `audio_enabled`, `video_enabled`, `screenshare_enabled`, `auto_gain_control`, `noise_suppression`, `transport`, `resolution`, `blur`, plus nested `cloudflare` and `device_farm`. +- Unknown participant JSON fields fail fast. This prevents silent typos such as `audio_enable`. +- The first `Ctrl-C` calls `ParticipantStore::shutdown_all().await`. A second `Ctrl-C` returns a forced-exit result to `main`, which exits with code `130`. +- Logging uses a normal `tracing_subscriber` fmt subscriber, not `tui_logger`. Warnings and errors go to stderr; info/debug/trace go to stdout. + +## File Structure + +- Modify `Cargo.toml`: add root-crate dependency on `serde` because the binary will derive `Deserialize` for participant JSON. +- Modify `config/src/args.rs`: add `HeadlessConfigArgs`, a `config::Source` implementation, and tests for override collection. +- Modify `config/src/lib.rs`: re-export `HeadlessConfigArgs`, factor config loading into a generic helper, and add `Config::new_headless`. +- Create `src/headless.rs`: own headless CLI args, participant JSON parsing, config layering, logging initialization, participant spawning, and shutdown orchestration. +- Modify `src/main.rs`: add the `headless` subcommand and route it to `headless::run`. + +--- + +### Task 1: Add Headless Config Overrides + +**Files:** +- Modify: `config/src/args.rs` +- Modify: `config/src/lib.rs` + +- [ ] **Step 1: Write config argument tests** + +Add these tests to `config/src/args.rs` under the existing module content: + +```rust +#[cfg(test)] +mod tests { + use super::HeadlessConfigArgs; + use config::Source as _; + + #[test] + fn headless_args_collect_only_present_values() { + let args = HeadlessConfigArgs { + debug: 2, + url: Some("https://example.com/space/demo".to_string()), + backend: Some("cloudflare".to_string()), + audio_enabled: Some(false), + video_enabled: None, + screenshare_enabled: Some(true), + auto_gain_control: None, + noise_suppression: Some("rnnoise".to_string()), + transport: Some("webrtc".to_string()), + resolution: Some("p720".to_string()), + blur: Some(true), + headless: Some(true), + }; + + let values = args.collect().expect("collect headless args"); + + assert_eq!(values.get("debug").unwrap().clone().into_int().unwrap(), 2); + assert_eq!(values.get("url").unwrap().clone().into_string().unwrap(), "https://example.com/space/demo"); + assert_eq!(values.get("backend").unwrap().clone().into_string().unwrap(), "cloudflare"); + assert!(!values.get("audio_enabled").unwrap().clone().into_bool().unwrap()); + assert!(values.get("screenshare_enabled").unwrap().clone().into_bool().unwrap()); + assert_eq!(values.get("noise_suppression").unwrap().clone().into_string().unwrap(), "rnnoise"); + assert_eq!(values.get("transport").unwrap().clone().into_string().unwrap(), "webrtc"); + assert_eq!(values.get("resolution").unwrap().clone().into_string().unwrap(), "p720"); + assert!(values.get("blur").unwrap().clone().into_bool().unwrap()); + assert!(values.get("headless").unwrap().clone().into_bool().unwrap()); + assert!(!values.contains_key("video_enabled")); + assert!(!values.contains_key("auto_gain_control")); + } +} +``` + +- [ ] **Step 2: Run the targeted config test and verify it fails** + +Run: + +```bash +cargo test -p client-simulator-config headless_args_collect_only_present_values +``` + +Expected: compile failure because `HeadlessConfigArgs` does not exist. + +- [ ] **Step 3: Implement `HeadlessConfigArgs`** + +Add this to `config/src/args.rs` after `TuiArgs`: + +```rust +/// Non-TUI participant launch options. +#[derive(clap::Args, Default, Debug, Clone)] +pub struct HeadlessConfigArgs { + /// Verbosity level, set from the parent command. + #[clap(skip)] + pub debug: u8, + + /// URL of the hyper.video session. + #[clap(long, value_name = "URL")] + pub url: Option, + + /// Participant backend: local, cloudflare, remote-stub, or aws-device-farm. + #[clap(long, value_name = "BACKEND")] + pub backend: Option, + + /// Enable or disable browser headless mode. + #[clap(long = "headless", action)] + pub headless: Option, + + /// Enable or disable participant audio. + #[clap(long = "audio-enabled", action)] + pub audio_enabled: Option, + + /// Enable or disable participant video. + #[clap(long = "video-enabled", action)] + pub video_enabled: Option, + + /// Enable or disable participant screenshare. + #[clap(long = "screenshare-enabled", action)] + pub screenshare_enabled: Option, + + /// Enable or disable browser auto gain control. + #[clap(long = "auto-gain-control", action)] + pub auto_gain_control: Option, + + /// Noise suppression model. + #[clap(long = "noise-suppression", value_name = "MODEL")] + pub noise_suppression: Option, + + /// Transport mode: webtransport or webrtc. + #[clap(long, value_name = "MODE")] + pub transport: Option, + + /// Webcam resolution, such as auto, p720, or p1080. + #[clap(long, value_name = "RESOLUTION")] + pub resolution: Option, + + /// Enable or disable background blur. + #[clap(long, action)] + pub blur: Option, +} +``` + +Then add a `config::Source` implementation parallel to `TuiArgs`, inserting only `Some` values and inserting `debug` only when it is greater than zero. + +- [ ] **Step 4: Re-export and add a non-TUI config constructor** + +In `config/src/lib.rs`, change: + +```rust +pub use args::TuiArgs; +``` + +to: + +```rust +pub use args::{ + HeadlessConfigArgs, + TuiArgs, +}; +``` + +Refactor `Config::new` so both TUI and headless loading share the same source order: + +```rust +impl Config { + pub fn new(args: TuiArgs) -> Result { + Self::load_with_overrides(args) + } + + pub fn new_headless(args: HeadlessConfigArgs) -> Result { + Self::load_with_overrides(args) + } + + fn load_with_overrides(overrides: S) -> Result + where + S: config::Source + Send + Sync + 'static, + { + let data_dir = get_data_dir(); + let config_dir = get_config_dir(); + let mut builder = config::Config::builder() + .set_default("data_dir", data_dir.to_str().unwrap())? + .set_default("config_dir", config_dir.to_str().unwrap())?; + + builder = builder.add_source(Config::default()); + + let config_files = [("config.yaml", config::FileFormat::Yaml)]; + + for (file, format) in &config_files { + let source = config::File::from(config_dir.join(file)) + .format(*format) + .required(false); + builder = builder.add_source(source); + } + + builder = builder.add_source(overrides); + + builder.build()?.try_deserialize() + } +} +``` + +- [ ] **Step 5: Run config tests** + +Run: + +```bash +cargo test -p client-simulator-config +``` + +Expected: all config crate tests pass. + +- [ ] **Step 6: Commit** + +```bash +git add config/src/args.rs config/src/lib.rs +git commit -m "feat: add headless config overrides" +``` + +--- + +### Task 2: Add Participant JSON Overlay Logic + +**Files:** +- Modify: `Cargo.toml` +- Create: `src/headless.rs` + +- [ ] **Step 1: Add serde to the root crate** + +In the root `Cargo.toml`, add: + +```toml +serde.workspace = true +``` + +- [ ] **Step 2: Write participant overlay tests** + +Create `src/headless.rs` with the test module first: + +```rust +use client_simulator_config::{ + CloudflareConfig, + Config, + DeviceFarmConfig, + HeadlessConfigArgs, + NoiseSuppression, + ParticipantBackendKind, + TransportMode, + WebcamResolution, +}; +use eyre::{ + Context as _, + Result, +}; +use serde::Deserialize; + +#[cfg(test)] +mod tests { + use super::*; + use url::Url; + + #[test] + fn participant_json_overrides_global_config() { + let global = Config { + url: Some(Url::parse("https://example.com/global").unwrap()), + backend: ParticipantBackendKind::Local, + audio_enabled: true, + video_enabled: true, + transport: TransportMode::WebTransport, + ..Default::default() + }; + + let participant = parse_participant_override( + r#"{ + "url": "https://example.com/participant", + "backend": "cloudflare", + "audio_enabled": false, + "transport": "webrtc", + "resolution": "p720", + "blur": true + }"#, + ) + .expect("valid participant json"); + + let effective = participant.apply_to(global); + + assert_eq!(effective.url.unwrap().as_str(), "https://example.com/participant"); + assert_eq!(effective.backend, ParticipantBackendKind::Cloudflare); + assert!(!effective.audio_enabled); + assert!(effective.video_enabled); + assert_eq!(effective.transport, TransportMode::WebRTC); + assert_eq!(effective.resolution, WebcamResolution::P720); + assert!(effective.blur); + } + + #[test] + fn empty_participant_list_starts_one_default_participant() { + let global = Config { + url: Some(Url::parse("https://example.com/global").unwrap()), + audio_enabled: true, + ..Default::default() + }; + + let configs = participant_configs(global, &[]).expect("participant configs"); + + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].url.as_ref().unwrap().as_str(), "https://example.com/global"); + assert!(configs[0].audio_enabled); + } + + #[test] + fn unknown_participant_json_field_is_rejected() { + let err = parse_participant_override(r#"{"audio_enable": false}"#).unwrap_err(); + + assert!(format!("{err:?}").contains("audio_enable")); + } +} +``` + +- [ ] **Step 3: Run the overlay tests and verify they fail** + +Run: + +```bash +cargo test participant_json_overrides_global_config empty_participant_list_starts_one_default_participant unknown_participant_json_field_is_rejected +``` + +Expected: compile failure because the tested functions and types do not exist. + +- [ ] **Step 4: Implement participant overlay types** + +Add this implementation above the tests in `src/headless.rs`: + +```rust +#[derive(clap::Args, Debug, Clone, Default)] +pub struct HeadlessArgs { + #[command(flatten)] + pub config: HeadlessConfigArgs, + + /// Participant-specific JSON overrides. Repeat this flag to start multiple participants. + #[clap(long = "participant", value_name = "JSON")] + pub participants: Vec, +} + +#[derive(Debug, Clone, Default, Deserialize)] +#[serde(default, deny_unknown_fields)] +struct ParticipantOverride { + url: Option, + backend: Option, + cloudflare: Option, + device_farm: Option, + headless: Option, + audio_enabled: Option, + video_enabled: Option, + screenshare_enabled: Option, + auto_gain_control: Option, + noise_suppression: Option, + transport: Option, + resolution: Option, + blur: Option, +} + +impl ParticipantOverride { + fn apply_to(&self, mut config: Config) -> Config { + if let Some(url) = &self.url { + config.url = Some(url.clone()); + } + if let Some(backend) = self.backend { + config.backend = backend; + } + if let Some(cloudflare) = &self.cloudflare { + config.cloudflare = cloudflare.clone(); + } + if let Some(device_farm) = &self.device_farm { + config.device_farm = device_farm.clone(); + } + if let Some(headless) = self.headless { + config.headless = headless; + } + if let Some(audio_enabled) = self.audio_enabled { + config.audio_enabled = audio_enabled; + } + if let Some(video_enabled) = self.video_enabled { + config.video_enabled = video_enabled; + } + if let Some(screenshare_enabled) = self.screenshare_enabled { + config.screenshare_enabled = screenshare_enabled; + } + if let Some(auto_gain_control) = self.auto_gain_control { + config.auto_gain_control = auto_gain_control; + } + if let Some(noise_suppression) = self.noise_suppression { + config.noise_suppression = noise_suppression; + } + if let Some(transport) = self.transport { + config.transport = transport; + } + if let Some(resolution) = self.resolution { + config.resolution = resolution; + } + if let Some(blur) = self.blur { + config.blur = blur; + } + config + } +} + +fn parse_participant_override(json: &str) -> Result { + serde_json::from_str(json).wrap_err_with(|| format!("Failed to parse --participant JSON: {json}")) +} + +fn participant_configs(global_config: Config, participant_json: &[String]) -> Result> { + if participant_json.is_empty() { + return Ok(vec![global_config]); + } + + participant_json + .iter() + .map(|json| parse_participant_override(json).map(|participant| participant.apply_to(global_config.clone()))) + .collect() +} +``` + +- [ ] **Step 5: Run the overlay tests** + +Run: + +```bash +cargo test participant_json_overrides_global_config empty_participant_list_starts_one_default_participant unknown_participant_json_field_is_rejected +``` + +Expected: all three tests pass. + +- [ ] **Step 6: Commit** + +```bash +git add Cargo.toml src/headless.rs +git commit -m "feat: parse headless participant overrides" +``` + +--- + +### Task 3: Implement Headless Runtime and Shutdown Semantics + +**Files:** +- Modify: `src/headless.rs` + +- [ ] **Step 1: Write shutdown behavior tests** + +Add these tests to the existing test module in `src/headless.rs`: + +```rust +use client_simulator_browser::participant::ParticipantStore; +use std::{ + fs, + path::PathBuf, + time::{ + SystemTime, + UNIX_EPOCH, + }, +}; +use tokio::sync::oneshot; + +#[tokio::test] +async fn shutdown_wait_returns_clean_when_participants_stop() { + let store = ParticipantStore::new(unique_test_data_dir()); + let config = Config { + url: Some(Url::parse("https://example.com/lite/demo").unwrap()), + backend: ParticipantBackendKind::RemoteStub, + ..Default::default() + }; + store.spawn(&config).expect("spawn participant"); + + let (first_tx, first_rx) = oneshot::channel::<()>(); + let (_second_tx, second_rx) = oneshot::channel::<()>(); + let store_for_shutdown = store.clone(); + + tokio::spawn(async move { + first_tx.send(()).expect("send first shutdown signal"); + }); + + let result = wait_for_headless_exit( + store_for_shutdown, + async move { + let _ = first_rx.await; + }, + || async move { + let _ = second_rx.await; + }, + ) + .await; + + assert_eq!(result, HeadlessExit::Clean); + assert!(store.is_empty()); +} + +#[tokio::test] +async fn second_shutdown_signal_returns_forced() { + let store = ParticipantStore::new(unique_test_data_dir()); + let config = Config { + url: Some(Url::parse("https://example.com/lite/demo").unwrap()), + backend: ParticipantBackendKind::RemoteStub, + ..Default::default() + }; + store.spawn(&config).expect("spawn participant"); + + let (first_tx, first_rx) = oneshot::channel::<()>(); + let (second_tx, second_rx) = oneshot::channel::<()>(); + + tokio::spawn(async move { + first_tx.send(()).expect("send first shutdown signal"); + second_tx.send(()).expect("send second shutdown signal"); + }); + + let result = wait_for_headless_exit( + store, + async move { + let _ = first_rx.await; + }, + || async move { + let _ = second_rx.await; + }, + ) + .await; + + assert_eq!(result, HeadlessExit::Forced); +} + +fn unique_test_data_dir() -> PathBuf { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("current time") + .as_nanos(); + let path = std::env::temp_dir().join(format!("hyper-browser-simulator-headless-test-{timestamp}")); + fs::create_dir_all(&path).expect("create test data dir"); + path +} +``` + +- [ ] **Step 2: Run the shutdown tests and verify they fail** + +Run: + +```bash +cargo test shutdown_wait_returns_clean_when_participants_stop second_shutdown_signal_returns_forced +``` + +Expected: compile failure because `HeadlessExit` and `wait_for_headless_exit` do not exist. + +- [ ] **Step 3: Implement runtime entry points** + +Add this to `src/headless.rs`: + +```rust +use client_simulator_browser::participant::ParticipantStore; +use std::{ + future::Future, +}; +use tracing_subscriber::{ + fmt::{ + self, + writer::MakeWriterExt as _, + }, + prelude::*, + registry, + EnvFilter, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum HeadlessExit { + Clean, + Forced, +} + +pub async fn run(mut args: HeadlessArgs, debug: u8) -> Result { + args.config.debug = debug; + init_headless_logging(debug)?; + + let global_config = Config::new_headless(args.config).context("Failed to create headless config")?; + let participant_configs = participant_configs(global_config.clone(), &args.participants)?; + let store = ParticipantStore::new(global_config.data_dir()); + + for config in &participant_configs { + store + .spawn(config) + .wrap_err_with(|| format!("Failed to spawn participant with backend {}", config.backend))?; + } + + let exit = wait_for_headless_exit(store, wait_for_ctrl_c(), wait_for_ctrl_c).await; + + Ok(match exit { + HeadlessExit::Clean => 0, + HeadlessExit::Forced => 130, + }) +} + +fn init_headless_logging(debug: u8) -> Result<()> { + let filter = match debug { + 0 => "info", + 1 => "debug", + _ => "trace", + }; + let env_filter = EnvFilter::builder().parse_lossy(filter); + let writer = std::io::stderr.with_min_level(tracing::Level::WARN).or_else(std::io::stdout); + + registry() + .with(tracing_error::ErrorLayer::default()) + .with(fmt::layer().with_writer(writer).with_filter(env_filter)) + .try_init() + .context("Failed to initialize headless logging") +} + +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_headless_exit( + store: ParticipantStore, + first_shutdown: F1, + second_shutdown: SecondShutdown, +) -> HeadlessExit +where + F1: Future, + SecondShutdown: FnOnce() -> F2, + F2: Future, +{ + tokio::pin!(first_shutdown); + + let mut state_receivers = store + .values() + .into_iter() + .map(|participant| participant.state.clone()) + .collect::>(); + + let all_stopped = async move { + for state in &mut state_receivers { + let _ = state.wait_for(|current| !current.running).await; + } + }; + tokio::pin!(all_stopped); + + tokio::select! { + _ = &mut all_stopped => HeadlessExit::Clean, + _ = &mut first_shutdown => { + let shutdown = store.shutdown_all(); + let second_shutdown = second_shutdown(); + tokio::pin!(shutdown); + tokio::pin!(second_shutdown); + tokio::select! { + _ = &mut shutdown => HeadlessExit::Clean, + _ = &mut second_shutdown => HeadlessExit::Forced, + } + } + } +} +``` + +- [ ] **Step 4: Run the shutdown tests** + +Run: + +```bash +cargo test shutdown_wait_returns_clean_when_participants_stop second_shutdown_signal_returns_forced +``` + +Expected: both tests pass. + +- [ ] **Step 5: Commit** + +```bash +git add src/headless.rs +git commit -m "feat: add headless runner shutdown handling" +``` + +--- + +### Task 4: Wire the CLI Command + +**Files:** +- Modify: `src/main.rs` +- Modify: `src/headless.rs` + +- [ ] **Step 1: Write CLI parsing tests** + +Add this test module to `src/main.rs`: + +```rust +#[cfg(test)] +mod tests { + use super::{ + CliArgs, + Command, + }; + use clap::Parser as _; + + #[test] + fn parses_headless_command_with_repeated_participants() { + let args = CliArgs::parse_from([ + "hyper-client-simulator", + "--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.debug, 1); + assert_eq!(headless.config.url.as_deref(), Some("https://latest.dev.hyper.video/F27-T5F-DXY")); + assert_eq!(headless.participants.len(), 2); + } + other => panic!("expected headless command, got {other:?}"), + } + } +} +``` + +- [ ] **Step 2: Run the CLI parsing test and verify it fails** + +Run: + +```bash +cargo test parses_headless_command_with_repeated_participants +``` + +Expected: compile failure because `headless` is not a module and `Command::Headless` does not exist. + +- [ ] **Step 3: Wire `headless` into `main.rs`** + +At the top of `src/main.rs`, add: + +```rust +mod headless; +``` + +Extend `Command`: + +```rust +#[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), +} +``` + +Derive `Debug` for `CliArgs` if the test needs it: + +```rust +#[derive(Parser, Debug)] +``` + +Route the command in `main`: + +```rust + Some(Command::Headless(args)) => { + let code = headless::run(args, debug).await?; + std::process::exit(code); + } +``` + +- [ ] **Step 4: Run the CLI parsing test** + +Run: + +```bash +cargo test parses_headless_command_with_repeated_participants +``` + +Expected: the test passes. + +- [ ] **Step 5: Run a smoke test with the remote stub backend** + +Run: + +```bash +cargo run -- headless \ + --url "https://example.com/lite/demo" \ + --participant '{"backend": "remote-stub", "audio_enabled": false}' +``` + +Expected: the process starts, logs appear in the terminal, pressing `Ctrl-C` once shuts down the participant and exits. Run it a second time, press `Ctrl-C` twice quickly, and verify the process exits with code `130`. + +- [ ] **Step 6: Commit** + +```bash +git add src/main.rs src/headless.rs +git commit -m "feat: add headless cli command" +``` + +--- + +### Task 5: Full Verification + +**Files:** +- No new files. + +- [ ] **Step 1: Format** + +Run: + +```bash +just fmt +``` + +Expected: formatting completes without errors. + +- [ ] **Step 2: Lint** + +Run: + +```bash +just clippy +``` + +Expected: clippy completes with no warnings. + +- [ ] **Step 3: Test** + +Run: + +```bash +just test +``` + +Expected: all tests pass. + +- [ ] **Step 4: Manual command check** + +Run: + +```bash +cargo run -- headless \ + --url "https://latest.dev.hyper.video/F27-T5F-DXY" \ + --participant '{"audio_enabled": false, "backend": "local"}' \ + --participant '{"audio_enabled": true, "backend": "cloudflare"}' +``` + +Expected: two participants are created, logs stream to the terminal, the app does not enter TUI mode, no config file is written as a result of the command-line overrides, one `Ctrl-C` starts clean shutdown, and a second `Ctrl-C` exits immediately with code `130`. + +- [ ] **Step 5: Commit verification fixes if any were needed** + +If formatting, linting, or tests required changes: + +```bash +git add Cargo.toml config/src/args.rs config/src/lib.rs src/headless.rs src/main.rs +git commit -m "fix: verify headless command" +``` + +Skip this commit if no files changed during verification. + +--- + +## Self-Review + +- Spec coverage: The plan covers the new `headless` command, no config persistence, config/default loading, global CLI overrides, per-participant JSON overrides with highest priority, stdout/stderr tracing, graceful shutdown on first `Ctrl-C`, and forced exit on second `Ctrl-C`. +- Placeholder scan: No incomplete implementation steps remain; tests, commands, expected results, file paths, and concrete code shapes are provided. +- Type consistency: `HeadlessConfigArgs`, `HeadlessArgs`, `ParticipantOverride`, `HeadlessExit`, and `wait_for_headless_exit` are introduced before they are used by later tasks. From 2b486c70690d8bc31574598c0ff4246031e5312c Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Tue, 2 Jun 2026 11:30:20 +0200 Subject: [PATCH 18/42] plan 2 --- 2026-06-01-no-tui-mode.md | 934 +++++++++++--------------------------- 1 file changed, 260 insertions(+), 674 deletions(-) diff --git a/2026-06-01-no-tui-mode.md b/2026-06-01-no-tui-mode.md index a29ec47..4729f99 100644 --- a/2026-06-01-no-tui-mode.md +++ b/2026-06-01-no-tui-mode.md @@ -1,620 +1,309 @@ # Headless Command Implementation Plan -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans to implement this plan task-by-task. -**Goal:** Add `hyper-client-simulator headless` so simulator participants can be started directly from CLI arguments without entering the TUI or writing runtime options back to the app config. +**Goal:** Add `hyper-client-simulator headless` so simulator participants can be started from CLI arguments without entering the TUI or saving runtime options back to the app config. -**Architecture:** Keep the TUI path unchanged. Add a headless runner in the root binary that loads persisted config plus global defaults, overlays command-line settings, overlays each `--participant` JSON object on top of that, spawns participants through `ParticipantStore`, streams tracing output to standard streams, and coordinates graceful/forced shutdown. +**Architecture:** Keep headless behavior local to the root binary. Load the existing `Config` once through the current config path, apply typed CLI overrides in memory, clone that config per participant, apply each participant JSON override in memory, spawn participants through `ParticipantStore`, and handle shutdown with a small `tokio::select!` loop. -**Tech Stack:** Rust 2021, `clap`, `serde`, `serde_json`, `config`, `tokio`, `tracing-subscriber`, existing `client-simulator-config` and `client-simulator-browser` crates. +**Tech Stack:** Rust 2021, `clap`, `serde`, `serde_json`, `tokio`, `tracing-subscriber`, existing `client-simulator-config` and `client-simulator-browser` crates. --- -## Design Decisions +## Scope -- `headless` means "no TUI". It should not implicitly persist settings. It should respect the existing browser `headless` config value unless `--headless true|false` or a participant JSON `{"headless": true}` overrides it. -- If no `--participant` argument is supplied, start one participant using the global effective config. If one or more `--participant` values are supplied, start exactly one participant per JSON object. -- Participant JSON supports the same launch-time fields exposed as global headless CLI overrides: `url`, `backend`, `headless`, `audio_enabled`, `video_enabled`, `screenshare_enabled`, `auto_gain_control`, `noise_suppression`, `transport`, `resolution`, `blur`, plus nested `cloudflare` and `device_farm`. -- Unknown participant JSON fields fail fast. This prevents silent typos such as `audio_enable`. -- The first `Ctrl-C` calls `ParticipantStore::shutdown_all().await`. A second `Ctrl-C` returns a forced-exit result to `main`, which exits with code `130`. -- Logging uses a normal `tracing_subscriber` fmt subscriber, not `tui_logger`. Warnings and errors go to stderr; info/debug/trace go to stdout. +Initial participant JSON supports stable launch fields: -## File Structure +- `url` +- `backend` +- `headless` +- `audio_enabled` +- `video_enabled` +- `screenshare_enabled` +- `auto_gain_control` +- `noise_suppression` +- `transport` +- `resolution` +- `blur` -- Modify `Cargo.toml`: add root-crate dependency on `serde` because the binary will derive `Deserialize` for participant JSON. -- Modify `config/src/args.rs`: add `HeadlessConfigArgs`, a `config::Source` implementation, and tests for override collection. -- Modify `config/src/lib.rs`: re-export `HeadlessConfigArgs`, factor config loading into a generic helper, and add `Config::new_headless`. -- Create `src/headless.rs`: own headless CLI args, participant JSON parsing, config layering, logging initialization, participant spawning, and shutdown orchestration. -- Modify `src/main.rs`: add the `headless` subcommand and route it to `headless::run`. +Nested backend service configs such as `cloudflare` and `device_farm` stay out of the initial implementation. Existing app config/global defaults still provide those values. Add nested per-participant backend overrides later only if a concrete workflow needs them. + +If no `--participant` is supplied, start one participant using the global effective config. If one or more `--participant` values are supplied, start exactly one participant per JSON object. --- -### Task 1: Add Headless Config Overrides +### Task 1: Add Headless CLI Types and Override Logic **Files:** -- Modify: `config/src/args.rs` -- Modify: `config/src/lib.rs` - -- [ ] **Step 1: Write config argument tests** - -Add these tests to `config/src/args.rs` under the existing module content: - -```rust -#[cfg(test)] -mod tests { - use super::HeadlessConfigArgs; - use config::Source as _; - - #[test] - fn headless_args_collect_only_present_values() { - let args = HeadlessConfigArgs { - debug: 2, - url: Some("https://example.com/space/demo".to_string()), - backend: Some("cloudflare".to_string()), - audio_enabled: Some(false), - video_enabled: None, - screenshare_enabled: Some(true), - auto_gain_control: None, - noise_suppression: Some("rnnoise".to_string()), - transport: Some("webrtc".to_string()), - resolution: Some("p720".to_string()), - blur: Some(true), - headless: Some(true), - }; - - let values = args.collect().expect("collect headless args"); - - assert_eq!(values.get("debug").unwrap().clone().into_int().unwrap(), 2); - assert_eq!(values.get("url").unwrap().clone().into_string().unwrap(), "https://example.com/space/demo"); - assert_eq!(values.get("backend").unwrap().clone().into_string().unwrap(), "cloudflare"); - assert!(!values.get("audio_enabled").unwrap().clone().into_bool().unwrap()); - assert!(values.get("screenshare_enabled").unwrap().clone().into_bool().unwrap()); - assert_eq!(values.get("noise_suppression").unwrap().clone().into_string().unwrap(), "rnnoise"); - assert_eq!(values.get("transport").unwrap().clone().into_string().unwrap(), "webrtc"); - assert_eq!(values.get("resolution").unwrap().clone().into_string().unwrap(), "p720"); - assert!(values.get("blur").unwrap().clone().into_bool().unwrap()); - assert!(values.get("headless").unwrap().clone().into_bool().unwrap()); - assert!(!values.contains_key("video_enabled")); - assert!(!values.contains_key("auto_gain_control")); - } -} -``` - -- [ ] **Step 2: Run the targeted config test and verify it fails** +- Modify: `Cargo.toml` +- Create: `src/headless.rs` -Run: +1. Add root-crate dependencies if missing: -```bash -cargo test -p client-simulator-config headless_args_collect_only_present_values +```toml +serde.workspace = true ``` -Expected: compile failure because `HeadlessConfigArgs` does not exist. +2. Create `src/headless.rs` with headless args, participant JSON overrides, and direct apply helpers. -- [ ] **Step 3: Implement `HeadlessConfigArgs`** - -Add this to `config/src/args.rs` after `TuiArgs`: +Use typed fields. Prefer clap parsers that accept the intended UX: ```rust -/// Non-TUI participant launch options. -#[derive(clap::Args, Default, Debug, Clone)] -pub struct HeadlessConfigArgs { - /// Verbosity level, set from the parent command. - #[clap(skip)] - pub debug: u8, - - /// URL of the hyper.video session. +#[derive(clap::Args, Debug, Clone, Default)] +pub struct HeadlessArgs { #[clap(long, value_name = "URL")] - pub url: Option, + pub url: Option, - /// Participant backend: local, cloudflare, remote-stub, or aws-device-farm. #[clap(long, value_name = "BACKEND")] - pub backend: Option, + pub backend: Option, - /// Enable or disable browser headless mode. - #[clap(long = "headless", action)] + #[clap(long = "headless", value_parser = clap::builder::BoolishValueParser::new())] pub headless: Option, - /// Enable or disable participant audio. - #[clap(long = "audio-enabled", action)] + #[clap(long = "audio-enabled", value_parser = clap::builder::BoolishValueParser::new())] pub audio_enabled: Option, - /// Enable or disable participant video. - #[clap(long = "video-enabled", action)] + #[clap(long = "video-enabled", value_parser = clap::builder::BoolishValueParser::new())] pub video_enabled: Option, - /// Enable or disable participant screenshare. - #[clap(long = "screenshare-enabled", action)] + #[clap(long = "screenshare-enabled", value_parser = clap::builder::BoolishValueParser::new())] pub screenshare_enabled: Option, - /// Enable or disable browser auto gain control. - #[clap(long = "auto-gain-control", action)] + #[clap(long = "auto-gain-control", value_parser = clap::builder::BoolishValueParser::new())] pub auto_gain_control: Option, - /// Noise suppression model. #[clap(long = "noise-suppression", value_name = "MODEL")] - pub noise_suppression: Option, + pub noise_suppression: Option, - /// Transport mode: webtransport or webrtc. #[clap(long, value_name = "MODE")] - pub transport: Option, + pub transport: Option, - /// Webcam resolution, such as auto, p720, or p1080. #[clap(long, value_name = "RESOLUTION")] - pub resolution: Option, + pub resolution: Option, - /// Enable or disable background blur. - #[clap(long, action)] + #[clap(long, value_parser = clap::builder::BoolishValueParser::new())] pub blur: Option, + + #[clap(long = "participant", value_name = "JSON")] + pub participants: Vec, } ``` -Then add a `config::Source` implementation parallel to `TuiArgs`, inserting only `Some` values and inserting `debug` only when it is greater than zero. - -- [ ] **Step 4: Re-export and add a non-TUI config constructor** - -In `config/src/lib.rs`, change: - -```rust -pub use args::TuiArgs; -``` +If one of the config enums does not satisfy clap's `FromStr` bounds cleanly, keep that field as `Option` and parse it with the enum's existing `FromStr` implementation inside `apply_cli_overrides`, returning an `eyre` error with the flag name. -to: +3. Add the participant override type: ```rust -pub use args::{ - HeadlessConfigArgs, - TuiArgs, -}; +#[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, + resolution: Option, + blur: Option, +} ``` -Refactor `Config::new` so both TUI and headless loading share the same source order: +4. Add direct override helpers: ```rust -impl Config { - pub fn new(args: TuiArgs) -> Result { - Self::load_with_overrides(args) +fn apply_cli_overrides(config: &mut client_simulator_config::Config, args: &HeadlessArgs) { + if let Some(url) = &args.url { + config.url = Some(url.clone()); } - - pub fn new_headless(args: HeadlessConfigArgs) -> Result { - Self::load_with_overrides(args) + 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(resolution) = args.resolution { + config.resolution = resolution; + } + if let Some(blur) = args.blur { + config.blur = blur; + } +} - fn load_with_overrides(overrides: S) -> Result - where - S: config::Source + Send + Sync + 'static, - { - let data_dir = get_data_dir(); - let config_dir = get_config_dir(); - let mut builder = config::Config::builder() - .set_default("data_dir", data_dir.to_str().unwrap())? - .set_default("config_dir", config_dir.to_str().unwrap())?; - - builder = builder.add_source(Config::default()); - - let config_files = [("config.yaml", config::FileFormat::Yaml)]; - - for (file, format) in &config_files { - let source = config::File::from(config_dir.join(file)) - .format(*format) - .required(false); - builder = builder.add_source(source); - } - - builder = builder.add_source(overrides); - - builder.build()?.try_deserialize() +fn apply_participant_override( + mut config: client_simulator_config::Config, + override_: ParticipantOverride, +) -> client_simulator_config::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(resolution) = override_.resolution { + config.resolution = resolution; + } + if let Some(blur) = override_.blur { + config.blur = blur; + } + config } ``` -- [ ] **Step 5: Run config tests** - -Run: - -```bash -cargo test -p client-simulator-config -``` +5. Add focused tests in `src/headless.rs`: -Expected: all config crate tests pass. +- CLI parsing accepts the user-shaped command with repeated `--participant`. +- CLI bool options accept explicit values, for example `--headless false`. +- participant JSON overrides global config. +- unknown participant JSON field returns an error. +- empty participant list returns one effective config. -- [ ] **Step 6: Commit** +Run: ```bash -git add config/src/args.rs config/src/lib.rs -git commit -m "feat: add headless config overrides" +cargo test -p hyper-client-simulator headless ``` --- -### Task 2: Add Participant JSON Overlay Logic +### Task 2: Implement the Headless Runner **Files:** -- Modify: `Cargo.toml` -- Create: `src/headless.rs` - -- [ ] **Step 1: Add serde to the root crate** - -In the root `Cargo.toml`, add: - -```toml -serde.workspace = true -``` +- Modify: `src/headless.rs` -- [ ] **Step 2: Write participant overlay tests** +1. Add `run(args, debug) -> eyre::Result`. -Create `src/headless.rs` with the test module first: +Implementation shape: ```rust -use client_simulator_config::{ - CloudflareConfig, - Config, - DeviceFarmConfig, - HeadlessConfigArgs, - NoiseSuppression, - ParticipantBackendKind, - TransportMode, - WebcamResolution, -}; -use eyre::{ - Context as _, - Result, -}; -use serde::Deserialize; - -#[cfg(test)] -mod tests { - use super::*; - use url::Url; - - #[test] - fn participant_json_overrides_global_config() { - let global = Config { - url: Some(Url::parse("https://example.com/global").unwrap()), - backend: ParticipantBackendKind::Local, - audio_enabled: true, - video_enabled: true, - transport: TransportMode::WebTransport, - ..Default::default() - }; - - let participant = parse_participant_override( - r#"{ - "url": "https://example.com/participant", - "backend": "cloudflare", - "audio_enabled": false, - "transport": "webrtc", - "resolution": "p720", - "blur": true - }"#, - ) - .expect("valid participant json"); - - let effective = participant.apply_to(global); - - assert_eq!(effective.url.unwrap().as_str(), "https://example.com/participant"); - assert_eq!(effective.backend, ParticipantBackendKind::Cloudflare); - assert!(!effective.audio_enabled); - assert!(effective.video_enabled); - assert_eq!(effective.transport, TransportMode::WebRTC); - assert_eq!(effective.resolution, WebcamResolution::P720); - assert!(effective.blur); - } +pub async fn run(args: HeadlessArgs, debug: u8) -> eyre::Result { + init_logging(debug)?; - #[test] - fn empty_participant_list_starts_one_default_participant() { - let global = Config { - url: Some(Url::parse("https://example.com/global").unwrap()), - audio_enabled: true, - ..Default::default() - }; + let mut global_config = client_simulator_config::Config::new(client_simulator_config::TuiArgs::default())?; + apply_cli_overrides(&mut global_config, &args); - let configs = participant_configs(global, &[]).expect("participant configs"); + let participant_configs = build_participant_configs(global_config.clone(), &args.participants)?; + let store = client_simulator_browser::participant::ParticipantStore::new(global_config.data_dir()); - assert_eq!(configs.len(), 1); - assert_eq!(configs[0].url.as_ref().unwrap().as_str(), "https://example.com/global"); - assert!(configs[0].audio_enabled); + for config in &participant_configs { + store.spawn(config)?; } - #[test] - fn unknown_participant_json_field_is_rejected() { - let err = parse_participant_override(r#"{"audio_enable": false}"#).unwrap_err(); - - assert!(format!("{err:?}").contains("audio_enable")); - } + Ok(wait_for_exit(store).await) } ``` -- [ ] **Step 3: Run the overlay tests and verify they fail** +This uses the existing config/default loading path but never calls `Config::save()` or `Config::update_from_args()`. -Run: - -```bash -cargo test participant_json_overrides_global_config empty_participant_list_starts_one_default_participant unknown_participant_json_field_is_rejected -``` - -Expected: compile failure because the tested functions and types do not exist. - -- [ ] **Step 4: Implement participant overlay types** - -Add this implementation above the tests in `src/headless.rs`: +2. Add `build_participant_configs`: ```rust -#[derive(clap::Args, Debug, Clone, Default)] -pub struct HeadlessArgs { - #[command(flatten)] - pub config: HeadlessConfigArgs, - - /// Participant-specific JSON overrides. Repeat this flag to start multiple participants. - #[clap(long = "participant", value_name = "JSON")] - pub participants: Vec, -} - -#[derive(Debug, Clone, Default, Deserialize)] -#[serde(default, deny_unknown_fields)] -struct ParticipantOverride { - url: Option, - backend: Option, - cloudflare: Option, - device_farm: Option, - headless: Option, - audio_enabled: Option, - video_enabled: Option, - screenshare_enabled: Option, - auto_gain_control: Option, - noise_suppression: Option, - transport: Option, - resolution: Option, - blur: Option, -} - -impl ParticipantOverride { - fn apply_to(&self, mut config: Config) -> Config { - if let Some(url) = &self.url { - config.url = Some(url.clone()); - } - if let Some(backend) = self.backend { - config.backend = backend; - } - if let Some(cloudflare) = &self.cloudflare { - config.cloudflare = cloudflare.clone(); - } - if let Some(device_farm) = &self.device_farm { - config.device_farm = device_farm.clone(); - } - if let Some(headless) = self.headless { - config.headless = headless; - } - if let Some(audio_enabled) = self.audio_enabled { - config.audio_enabled = audio_enabled; - } - if let Some(video_enabled) = self.video_enabled { - config.video_enabled = video_enabled; - } - if let Some(screenshare_enabled) = self.screenshare_enabled { - config.screenshare_enabled = screenshare_enabled; - } - if let Some(auto_gain_control) = self.auto_gain_control { - config.auto_gain_control = auto_gain_control; - } - if let Some(noise_suppression) = self.noise_suppression { - config.noise_suppression = noise_suppression; - } - if let Some(transport) = self.transport { - config.transport = transport; - } - if let Some(resolution) = self.resolution { - config.resolution = resolution; - } - if let Some(blur) = self.blur { - config.blur = blur; - } - config - } -} - -fn parse_participant_override(json: &str) -> Result { - serde_json::from_str(json).wrap_err_with(|| format!("Failed to parse --participant JSON: {json}")) -} - -fn participant_configs(global_config: Config, participant_json: &[String]) -> Result> { - if participant_json.is_empty() { +fn build_participant_configs( + global_config: client_simulator_config::Config, + participants: &[String], +) -> eyre::Result> { + if participants.is_empty() { return Ok(vec![global_config]); } - participant_json + participants .iter() - .map(|json| parse_participant_override(json).map(|participant| participant.apply_to(global_config.clone()))) + .map(|json| { + let override_: ParticipantOverride = serde_json::from_str(json)?; + Ok(apply_participant_override(global_config.clone(), override_)) + }) .collect() } ``` -- [ ] **Step 5: Run the overlay tests** +Wrap JSON errors with context containing `--participant` so invalid input is easy to diagnose. -Run: - -```bash -cargo test participant_json_overrides_global_config empty_participant_list_starts_one_default_participant unknown_participant_json_field_is_rejected -``` - -Expected: all three tests pass. - -- [ ] **Step 6: Commit** - -```bash -git add Cargo.toml src/headless.rs -git commit -m "feat: parse headless participant overrides" -``` - ---- - -### Task 3: Implement Headless Runtime and Shutdown Semantics - -**Files:** -- Modify: `src/headless.rs` - -- [ ] **Step 1: Write shutdown behavior tests** - -Add these tests to the existing test module in `src/headless.rs`: +3. Add logging with normal `tracing_subscriber`, not `tui_logger`: ```rust -use client_simulator_browser::participant::ParticipantStore; -use std::{ - fs, - path::PathBuf, - time::{ - SystemTime, - UNIX_EPOCH, - }, -}; -use tokio::sync::oneshot; - -#[tokio::test] -async fn shutdown_wait_returns_clean_when_participants_stop() { - let store = ParticipantStore::new(unique_test_data_dir()); - let config = Config { - url: Some(Url::parse("https://example.com/lite/demo").unwrap()), - backend: ParticipantBackendKind::RemoteStub, - ..Default::default() - }; - store.spawn(&config).expect("spawn participant"); - - let (first_tx, first_rx) = oneshot::channel::<()>(); - let (_second_tx, second_rx) = oneshot::channel::<()>(); - let store_for_shutdown = store.clone(); - - tokio::spawn(async move { - first_tx.send(()).expect("send first shutdown signal"); - }); - - let result = wait_for_headless_exit( - store_for_shutdown, - async move { - let _ = first_rx.await; +fn init_logging(debug: u8) -> eyre::Result<()> { + use tracing_subscriber::{ + fmt::{ + self, + writer::MakeWriterExt as _, }, - || async move { - let _ = second_rx.await; - }, - ) - .await; - - assert_eq!(result, HeadlessExit::Clean); - assert!(store.is_empty()); -} - -#[tokio::test] -async fn second_shutdown_signal_returns_forced() { - let store = ParticipantStore::new(unique_test_data_dir()); - let config = Config { - url: Some(Url::parse("https://example.com/lite/demo").unwrap()), - backend: ParticipantBackendKind::RemoteStub, - ..Default::default() + prelude::*, + EnvFilter, }; - store.spawn(&config).expect("spawn participant"); - let (first_tx, first_rx) = oneshot::channel::<()>(); - let (second_tx, second_rx) = oneshot::channel::<()>(); - - tokio::spawn(async move { - first_tx.send(()).expect("send first shutdown signal"); - second_tx.send(()).expect("send second shutdown signal"); - }); - - let result = wait_for_headless_exit( - store, - async move { - let _ = first_rx.await; - }, - || async move { - let _ = second_rx.await; - }, - ) - .await; + let filter = match debug { + 0 => "info", + 1 => "debug", + _ => "trace", + }; + let writer = std::io::stderr + .with_min_level(tracing::Level::WARN) + .or_else(std::io::stdout); - assert_eq!(result, HeadlessExit::Forced); -} + tracing_subscriber::registry() + .with(tracing_error::ErrorLayer::default()) + .with(fmt::layer().with_writer(writer).with_filter(EnvFilter::builder().parse_lossy(filter))) + .try_init()?; -fn unique_test_data_dir() -> PathBuf { - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("current time") - .as_nanos(); - let path = std::env::temp_dir().join(format!("hyper-browser-simulator-headless-test-{timestamp}")); - fs::create_dir_all(&path).expect("create test data dir"); - path + Ok(()) } ``` -- [ ] **Step 2: Run the shutdown tests and verify they fail** - -Run: - -```bash -cargo test shutdown_wait_returns_clean_when_participants_stop second_shutdown_signal_returns_forced -``` - -Expected: compile failure because `HeadlessExit` and `wait_for_headless_exit` do not exist. - -- [ ] **Step 3: Implement runtime entry points** - -Add this to `src/headless.rs`: +4. Add shutdown behavior: ```rust -use client_simulator_browser::participant::ParticipantStore; -use std::{ - future::Future, -}; -use tracing_subscriber::{ - fmt::{ - self, - writer::MakeWriterExt as _, - }, - prelude::*, - registry, - EnvFilter, -}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum HeadlessExit { - Clean, - Forced, -} - -pub async fn run(mut args: HeadlessArgs, debug: u8) -> Result { - args.config.debug = debug; - init_headless_logging(debug)?; - - let global_config = Config::new_headless(args.config).context("Failed to create headless config")?; - let participant_configs = participant_configs(global_config.clone(), &args.participants)?; - let store = ParticipantStore::new(global_config.data_dir()); - - for config in &participant_configs { - store - .spawn(config) - .wrap_err_with(|| format!("Failed to spawn participant with backend {}", config.backend))?; +async fn wait_for_exit(store: client_simulator_browser::participant::ParticipantStore) -> i32 { + tokio::select! { + _ = wait_for_all_stopped(&store) => 0, + _ = wait_for_ctrl_c() => { + tracing::info!("Shutting down participants. Press Ctrl-C again to force exit."); + tokio::select! { + _ = store.shutdown_all() => 0, + _ = wait_for_ctrl_c() => 130, + } + } } - - let exit = wait_for_headless_exit(store, wait_for_ctrl_c(), wait_for_ctrl_c).await; - - Ok(match exit { - HeadlessExit::Clean => 0, - HeadlessExit::Forced => 130, - }) -} - -fn init_headless_logging(debug: u8) -> Result<()> { - let filter = match debug { - 0 => "info", - 1 => "debug", - _ => "trace", - }; - let env_filter = EnvFilter::builder().parse_lossy(filter); - let writer = std::io::stderr.with_min_level(tracing::Level::WARN).or_else(std::io::stdout); - - registry() - .with(tracing_error::ErrorLayer::default()) - .with(fmt::layer().with_writer(writer).with_filter(env_filter)) - .try_init() - .context("Failed to initialize headless logging") } async fn wait_for_ctrl_c() { @@ -623,130 +312,41 @@ async fn wait_for_ctrl_c() { } } -async fn wait_for_headless_exit( - store: ParticipantStore, - first_shutdown: F1, - second_shutdown: SecondShutdown, -) -> HeadlessExit -where - F1: Future, - SecondShutdown: FnOnce() -> F2, - F2: Future, -{ - tokio::pin!(first_shutdown); - - let mut state_receivers = store +async fn wait_for_all_stopped(store: &client_simulator_browser::participant::ParticipantStore) { + let mut states = store .values() .into_iter() .map(|participant| participant.state.clone()) .collect::>(); - let all_stopped = async move { - for state in &mut state_receivers { - let _ = state.wait_for(|current| !current.running).await; - } - }; - tokio::pin!(all_stopped); - - tokio::select! { - _ = &mut all_stopped => HeadlessExit::Clean, - _ = &mut first_shutdown => { - let shutdown = store.shutdown_all(); - let second_shutdown = second_shutdown(); - tokio::pin!(shutdown); - tokio::pin!(second_shutdown); - tokio::select! { - _ = &mut shutdown => HeadlessExit::Clean, - _ = &mut second_shutdown => HeadlessExit::Forced, - } - } + for state in &mut states { + let _ = state.wait_for(|current| !current.running).await; } } ``` -- [ ] **Step 4: Run the shutdown tests** +5. Add a small pure shutdown-coordination test if desired. Do not spawn local Chromium in unit tests. Use injected futures or channels to verify "first signal starts shutdown" and "second signal returns 130". Run: ```bash -cargo test shutdown_wait_returns_clean_when_participants_stop second_shutdown_signal_returns_forced -``` - -Expected: both tests pass. - -- [ ] **Step 5: Commit** - -```bash -git add src/headless.rs -git commit -m "feat: add headless runner shutdown handling" +cargo test -p hyper-client-simulator headless ``` --- -### Task 4: Wire the CLI Command +### Task 3: Wire the Command in `main` **Files:** - Modify: `src/main.rs` -- Modify: `src/headless.rs` - -- [ ] **Step 1: Write CLI parsing tests** - -Add this test module to `src/main.rs`: - -```rust -#[cfg(test)] -mod tests { - use super::{ - CliArgs, - Command, - }; - use clap::Parser as _; - - #[test] - fn parses_headless_command_with_repeated_participants() { - let args = CliArgs::parse_from([ - "hyper-client-simulator", - "--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.debug, 1); - assert_eq!(headless.config.url.as_deref(), Some("https://latest.dev.hyper.video/F27-T5F-DXY")); - assert_eq!(headless.participants.len(), 2); - } - other => panic!("expected headless command, got {other:?}"), - } - } -} -``` - -- [ ] **Step 2: Run the CLI parsing test and verify it fails** - -Run: - -```bash -cargo test parses_headless_command_with_repeated_participants -``` -Expected: compile failure because `headless` is not a module and `Command::Headless` does not exist. - -- [ ] **Step 3: Wire `headless` into `main.rs`** - -At the top of `src/main.rs`, add: +1. Add the module: ```rust mod headless; ``` -Extend `Command`: +2. Add the subcommand: ```rust #[derive(Subcommand, Debug)] @@ -760,90 +360,80 @@ enum Command { } ``` -Derive `Debug` for `CliArgs` if the test needs it: +3. Route the command: ```rust -#[derive(Parser, Debug)] +Some(Command::Headless(args)) => { + let code = headless::run(args, debug).await?; + std::process::exit(code); +} ``` -Route the command in `main`: +4. Add or keep a CLI parsing test proving the example shape works: ```rust - Some(Command::Headless(args)) => { - let code = headless::run(args, debug).await?; - std::process::exit(code); +#[test] +fn parses_headless_command_with_repeated_participants() { + let args = CliArgs::parse_from([ + "hyper-client-simulator", + "--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.debug, 1); + 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:?}"), + } +} ``` -- [ ] **Step 4: Run the CLI parsing test** - -Run: - -```bash -cargo test parses_headless_command_with_repeated_participants -``` - -Expected: the test passes. - -- [ ] **Step 5: Run a smoke test with the remote stub backend** - Run: ```bash -cargo run -- headless \ - --url "https://example.com/lite/demo" \ - --participant '{"backend": "remote-stub", "audio_enabled": false}' -``` - -Expected: the process starts, logs appear in the terminal, pressing `Ctrl-C` once shuts down the participant and exits. Run it a second time, press `Ctrl-C` twice quickly, and verify the process exits with code `130`. - -- [ ] **Step 6: Commit** - -```bash -git add src/main.rs src/headless.rs -git commit -m "feat: add headless cli command" +cargo test -p hyper-client-simulator parses_headless_command_with_repeated_participants ``` --- -### Task 5: Full Verification +### Task 4: Verify End to End **Files:** - No new files. -- [ ] **Step 1: Format** - -Run: +Run formatting, linting, and tests: ```bash just fmt -``` - -Expected: formatting completes without errors. - -- [ ] **Step 2: Lint** - -Run: - -```bash just clippy +just test ``` -Expected: clippy completes with no warnings. - -- [ ] **Step 3: Test** - -Run: +Manual smoke test with a backend that does not require Chromium: ```bash -just test +cargo run -- headless \ + --url "https://example.com/lite/demo" \ + --participant '{"backend": "remote-stub", "audio_enabled": false}' ``` -Expected: all tests pass. +Expected: -- [ ] **Step 4: Manual command check** +- logs print in the terminal without entering TUI mode +- no config file is written from CLI overrides +- first `Ctrl-C` starts participant shutdown and exits cleanly +- second `Ctrl-C` exits with code `130` -Run: +Manual example matching the requested command shape: ```bash cargo run -- headless \ @@ -852,23 +442,19 @@ cargo run -- headless \ --participant '{"audio_enabled": true, "backend": "cloudflare"}' ``` -Expected: two participants are created, logs stream to the terminal, the app does not enter TUI mode, no config file is written as a result of the command-line overrides, one `Ctrl-C` starts clean shutdown, and a second `Ctrl-C` exits immediately with code `130`. - -- [ ] **Step 5: Commit verification fixes if any were needed** - -If formatting, linting, or tests required changes: - -```bash -git add Cargo.toml config/src/args.rs config/src/lib.rs src/headless.rs src/main.rs -git commit -m "fix: verify headless command" -``` +Expected: -Skip this commit if no files changed during verification. +- two participants start +- each participant receives the global URL +- each participant JSON overrides its own audio/backend settings +- logs stream to stdout/stderr through tracing --- -## Self-Review +## Non-Goals -- Spec coverage: The plan covers the new `headless` command, no config persistence, config/default loading, global CLI overrides, per-participant JSON overrides with highest priority, stdout/stderr tracing, graceful shutdown on first `Ctrl-C`, and forced exit on second `Ctrl-C`. -- Placeholder scan: No incomplete implementation steps remain; tests, commands, expected results, file paths, and concrete code shapes are provided. -- Type consistency: `HeadlessConfigArgs`, `HeadlessArgs`, `ParticipantOverride`, `HeadlessExit`, and `wait_for_headless_exit` are introduced before they are used by later tasks. +- No TUI changes. +- No config crate changes unless implementation proves a small `Config::load()` helper is necessary. +- No config persistence from `headless`. +- No nested per-participant `cloudflare` or `device_farm` overrides in the first version. +- No broad refactor of participant spawning or logging. From 682eb922aa040b8e5968d6104adf37b0d10f0f02 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Tue, 2 Jun 2026 11:30:35 +0200 Subject: [PATCH 19/42] headless mode --- Cargo.lock | 1 + Cargo.toml | 1 + src/headless.rs | 466 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 41 ++++- 4 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 src/headless.rs diff --git a/Cargo.lock b/Cargo.lock index 32e8055..208152b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1939,6 +1939,7 @@ dependencies = [ "eyre", "human-panic", "libc", + "serde", "serde_json", "strip-ansi-escapes", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 87345c9..30cba67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ eyre.workspace = true human-panic.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/src/headless.rs b/src/headless.rs new file mode 100644 index 0000000..6bef39d --- /dev/null +++ b/src/headless.rs @@ -0,0 +1,466 @@ +use clap::Args; +use client_simulator_config::{ + Config, + NoiseSuppression, + ParticipantBackendKind, + TransportMode, + TuiArgs, + WebcamResolution, +}; +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, value_name = "RESOLUTION")] + pub resolution: 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, + resolution: Option, + blur: Option, +} + +pub async fn run(args: HeadlessArgs, debug: u8) -> Result { + init_logging(debug)?; + + 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()); + + for config in &participant_configs { + store.spawn(config).context("Failed to spawn participant")?; + } + + Ok(wait_for_exit(store).await) +} + +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(resolution) = args.resolution { + config.resolution = resolution; + } + 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(resolution) = override_.resolution { + config.resolution = resolution; + } + 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(debug: u8) -> Result<()> { + let filter = match debug { + 0 => "info", + 1 => "debug", + _ => "trace", + }; + 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(EnvFilter::builder().parse_lossy(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, + }; + 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 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 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); + } + + 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..119edc7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod errors; +mod headless; use clap::{ Parser, @@ -27,14 +28,48 @@ pub struct CliArgs { 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), } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_headless_command_with_repeated_participants() { + let args = CliArgs::parse_from([ + "hyper-client-simulator", + "--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.debug, 1); + 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:?}"), + } + } +} + #[derive(clap::Args, Debug, Clone)] pub struct CookieArgs { /// Base URL of the hyper server @@ -64,6 +99,10 @@ async fn main() -> eyre::Result<()> { args.debug = debug; start_tui(args).await } + Some(Command::Headless(args)) => { + let code = headless::run(args, debug).await?; + std::process::exit(code); + } Some(Command::Cookie(args)) => run_cookie(args, debug).await, } } From 0a107325477c0efcc4f118e5751a655fb90fc72c Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Tue, 2 Jun 2026 20:15:38 +0200 Subject: [PATCH 20/42] 2026-06-02-new-video-constraints-support.org v1 --- 2026-06-02-new-video-constraints-support.org | 1338 ++++++++++++++++++ 1 file changed, 1338 insertions(+) create mode 100644 2026-06-02-new-video-constraints-support.org diff --git a/2026-06-02-new-video-constraints-support.org b/2026-06-02-new-video-constraints-support.org new file mode 100644 index 0000000..d5d5423 --- /dev/null +++ b/2026-06-02-new-video-constraints-support.org @@ -0,0 +1,1338 @@ +#+TITLE: New Video Constraints Support Implementation Plan +#+DATE: [2026-06-02 Tue] +#+FILETAGS: :implementation_plan:video_constraints: + +#+BEGIN_QUOTE +For agentic workers: REQUIRED SUB-SKILL: Use =superpowers:subagent-driven-development= (recommended) or =superpowers:executing-plans= to implement this plan task-by-task. Steps use checkbox syntax for tracking. +#+END_QUOTE + +* Goal + +Replace simulator support for the removed Hyper Core webcam encoder resolution setting with the current media settings: + +- =videoConstraintPublishWebcam=: outgoing webcam max-resolution option. +- =videoConstraintSubscribe=: incoming max-resolution option. +- =videoMaxConcurrentTracks=: max number of outgoing video tracks per local video stream, or =null= for unlimited. + +* Architecture + +Treat the new controls as media settings, not video codec settings. The active local-browser JavaScript API should read and write =window.hyper.settings.media= through =setVideoConstraintPublishWebcam=, =setVideoConstraintSubscribe=, and =setVideoMaxConcurrentTracks=. + +Replace the simulator's old single =resolution= setting throughout the active config, participant launch settings, participant state, headless CLI, remote-stub backend, and TUI surfaces. Keep the Cloudflare backend in sync by updating the bundled worker OpenAPI schema and browser-side mapping tests so remote participants receive the same three setting values. + +* Tech Stack + +Rust 2021, =serde=, =strum=, =clap=, =tokio=, =chromiumoxide=, =thirtyfour=, generated =cloudflare-worker-client= types from =progenitor=, and the existing =just= recipes. + +* Source Context + +The linked Hyper design note is [[file:/Users/robert/projects/shuttle/hyper.video-3/2026-06-02_manual-simulcast-video-stream-control.org][2026-06-02_manual-simulcast-video-stream-control.org]]. + +The current Hyper SDK settings are defined in [[file:/Users/robert/projects/shuttle/hyper.video-3/frontend/web/sdk/src/remote-config/media-settings.ts][media-settings.ts]]: + +#+BEGIN_SRC typescript +export type VideoConstraint = keyof typeof VIDEO_CONSTRAINT_NONE | keyof typeof VIDEO_CONSTRAINT_OPTIONS; + +export const VIDEO_CONSTRAINT_NONE = { + none: { label: "None" }, +}; +export const VIDEO_CONSTRAINT_OPTIONS = { + "90p": { label: "90p", width: 160, height: 90 }, + "144p": { label: "144p", width: 256, height: 144 }, + "240p": { label: "240p", width: 426, height: 240 }, + "360p": { label: "360p", width: 640, height: 360 }, + "480p": { label: "480p", width: 854, height: 480 }, + "720p": { label: "720p", width: 1280, height: 720 }, + "1080p": { label: "1080p", width: 1920, height: 1080 }, + "1440p": { label: "1440p", width: 2560, height: 1440 }, + "2160p": { label: "2160p", width: 3840, height: 2160 }, +} as const; +#+END_SRC + +The active runtime actions are in [[file:/Users/robert/projects/shuttle/hyper.video-3/frontend/web/sdk/src/media/state/media-settings.tsx][media-settings.tsx]]: + +#+BEGIN_SRC typescript +setVideoConstraintPublishWebcam: (constraint) => { + updateBase({ videoConstraintPublishWebcam: constraint }); +}, +setVideoConstraintSubscribe: (constraint) => { + updateBase({ videoConstraintSubscribe: constraint }); +}, +setVideoMaxConcurrentTracks: (maxConcurrentTracks) => { + updateBase({ videoMaxConcurrentTracks: maxConcurrentTracks }); +}, +#+END_SRC + +The simulator currently calls the removed API in [[file:browser/src/participant/frontend/commands.rs][browser/src/participant/frontend/commands.rs]]: + +#+BEGIN_SRC rust +const CAMERA_RES_GET: &str = "return hyper.settings.videoCodec.videoResolutionForWebcamEncoder.name;"; +const CAMERA_RES_SET: &str = "hyper.settings.videoCodec.actions.setVideoResolutionForWebcamEncoder(arguments[0]);"; +#+END_SRC + +* File Map + +- Modify [[file:config/src/client_config.rs][config/src/client_config.rs]]: add =VideoConstraint= and a CLI/TUI-friendly =VideoMaxConcurrentTracksPreset= helper; stop using =WebcamResolution= for active Hyper Core media settings. +- Modify [[file:config/src/lib.rs][config/src/lib.rs]]: replace =Config.resolution= with =video_constraint_publish_webcam=, =video_constraint_subscribe=, and =video_max_concurrent_tracks=. +- Modify [[file:config/src/default-config.yaml][config/src/default-config.yaml]]: default both constraints to =none= and =video_max_concurrent_tracks= to =null= or omitted. +- Modify [[file:browser/src/participant/frontend/commands.rs][browser/src/participant/frontend/commands.rs]]: replace camera resolution commands with media setting commands and unit tests over a fake =BrowserDriver=. +- Modify [[file:browser/src/participant/frontend/core.rs][browser/src/participant/frontend/core.rs]]: apply and refresh the three new settings. +- Modify [[file:browser/src/participant/shared/spec.rs][browser/src/participant/shared/spec.rs]], [[file:browser/src/participant/shared/state.rs][state.rs]], [[file:browser/src/participant/shared/messages.rs][messages.rs]], [[file:browser/src/participant/remote_stub.rs][remote_stub.rs]], and [[file:browser/src/participant/mod.rs][mod.rs]]: thread the new setting names through participant state and commands. +- Modify [[file:src/headless.rs][src/headless.rs]]: replace =--resolution= and participant JSON =resolution= with the three new fields. +- Modify [[file:tui/src/tui/components/browser_start.rs][tui/src/tui/components/browser_start.rs]] and [[file:tui/src/tui/components/participants.rs][participants.rs]]: replace the camera resolution selector/table cell with outgoing constraint, incoming constraint, and track-limit controls. +- Modify [[file:cloudflare-worker-client/openapi/cloudflare-browser-simulator.json][cloudflare-worker-client/openapi/cloudflare-browser-simulator.json]], [[file:cloudflare-worker-client/src/client.rs][client.rs]], and [[file:browser/src/participant/cloudflare/mod.rs][browser/src/participant/cloudflare/mod.rs]]: replace worker schema and mappings for legacy resolution. + +* Task 1: Add Config Types and Replace Active Config Fields + +** Files + +- Modify: [[file:config/src/client_config.rs][config/src/client_config.rs]] +- Modify: [[file:config/src/lib.rs][config/src/lib.rs]] +- Modify: [[file:config/src/default-config.yaml][config/src/default-config.yaml]] + +** Steps + +- [ ] Step 1: Write failing config type tests. + +Add these tests in =config/src/client_config.rs=. + +#+BEGIN_SRC rust +#[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::from_option(None), VideoMaxConcurrentTracksPreset::Unlimited); + assert_eq!(VideoMaxConcurrentTracksPreset::from_option(Some(1)), VideoMaxConcurrentTracksPreset::One); + assert_eq!(VideoMaxConcurrentTracksPreset::from_option(Some(2)), VideoMaxConcurrentTracksPreset::Two); + assert_eq!(VideoMaxConcurrentTracksPreset::from_option(Some(8)), VideoMaxConcurrentTracksPreset::Custom); + } + + #[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()); + } +} +#+END_SRC + +Replace the existing =tests= module instead of creating a duplicate module. + +- [ ] Step 2: Run the failing config type tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-config video_constraint_round_trips_hyper_media_setting_names video_track_limit_presets_convert_to_nullable_track_counts +#+END_SRC + +Expected: the command fails because =VideoConstraint= and =VideoMaxConcurrentTracksPreset= do not exist. + +- [ ] Step 3: Add the new config enums. + +Add this near =WebcamResolution= in =config/src/client_config.rs=. Keep =WebcamResolution= only if Cloudflare compatibility tests still need it during the transition; do not use it for active Hyper Core media settings after Task 3. + +#+BEGIN_SRC rust +#[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub enum VideoConstraint { + #[default] + #[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, +} + +#[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 = "custom")] + Custom, +} + +impl VideoMaxConcurrentTracksPreset { + pub const fn to_option(self) -> Option { + match self { + Self::Unlimited => None, + Self::One => Some(1), + Self::Two => Some(2), + Self::Custom => None, + } + } + + pub const fn from_option(value: Option) -> Self { + match value { + None => Self::Unlimited, + Some(1) => Self::One, + Some(2) => Self::Two, + Some(_) => Self::Custom, + } + } +} +#+END_SRC + +- [ ] Step 4: Export the new types. + +Modify the =pub use client_config= block in =config/src/lib.rs=. + +#+BEGIN_SRC rust +pub use client_config::{ + NoiseSuppression, + NoiseSuppressionIter, + ParticipantBackendKind, + TransportMode, + TransportModeIter, + VideoConstraint, + VideoConstraintIter, + VideoMaxConcurrentTracksPreset, + VideoMaxConcurrentTracksPresetIter, +}; +#+END_SRC + +- [ ] Step 5: Replace the active config fields. + +In =config/src/lib.rs=, replace: + +#+BEGIN_SRC rust +#[serde(default)] +pub resolution: WebcamResolution, +#+END_SRC + +with: + +#+BEGIN_SRC rust +#[serde(default)] +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, +#+END_SRC + +Update =impl config::Source for Config= so the collected source emits only the new fields: + +#+BEGIN_SRC rust +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()); +} +#+END_SRC + +Remove the old =resolution= insert from this method. + +- [ ] Step 6: Update default YAML. + +In =config/src/default-config.yaml=, replace: + +#+BEGIN_SRC yaml +resolution: auto +#+END_SRC + +with: + +#+BEGIN_SRC yaml +video_constraint_publish_webcam: none +video_constraint_subscribe: none +video_max_concurrent_tracks: +#+END_SRC + +- [ ] Step 7: Add config deserialization tests. + +Add these tests in =config/src/lib.rs=. + +#+BEGIN_SRC rust +#[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); +} +#+END_SRC + +- [ ] Step 8: Run config tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-config +#+END_SRC + +Expected: all =client-simulator-config= tests pass. + +* Task 2: Replace Local Browser JavaScript Commands + +** Files + +- Modify: [[file:browser/src/participant/frontend/commands.rs][browser/src/participant/frontend/commands.rs]] + +** Steps + +- [ ] Step 1: Write failing command tests with a fake driver. + +Add a =#[cfg(test)]= module in =browser/src/participant/frontend/commands.rs=. Use a recording driver so the tests prove exact JavaScript bodies and serialized arguments. + +#+BEGIN_SRC rust +#[cfg(test)] +mod tests { + use super::*; + 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, + )], + ); + } +} +#+END_SRC + +- [ ] Step 2: Run the failing browser command tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-browser video_constraints_through_media_settings_api gets_video_constraints_from_media_settings_api gets_nullable_video_max_concurrent_tracks +#+END_SRC + +Expected: the command fails because the new functions and imported types do not exist. + +- [ ] Step 3: Replace the command constants and functions. + +In =browser/src/participant/frontend/commands.rs=, import =VideoConstraint= instead of =WebcamResolution= for video constraints: + +#+BEGIN_SRC rust +use client_simulator_config::{ + NoiseSuppression, + VideoConstraint, +}; +#+END_SRC + +Remove =CAMERA_RES_GET= and =CAMERA_RES_SET=. Add: + +#+BEGIN_SRC rust +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]);"; +#+END_SRC + +Replace =get_outgoing_camera_resolution= and =set_outgoing_camera_resolution= with: + +#+BEGIN_SRC rust +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> { + let value = driver.eval(VIDEO_MAX_CONCURRENT_TRACKS_GET, None).await?; + serde_json::from_value(value).context("failed to read videoMaxConcurrentTracks from eval result") +} + +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 +} +#+END_SRC + +- [ ] Step 4: Run browser command tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-browser video_constraints +#+END_SRC + +Expected: command tests pass and no remaining test references call =videoResolutionForWebcamEncoder=. + +* Task 3: Thread New Settings Through Participant Runtime + +** Files + +- Modify: [[file:browser/src/participant/shared/spec.rs][browser/src/participant/shared/spec.rs]] +- Modify: [[file:browser/src/participant/shared/state.rs][browser/src/participant/shared/state.rs]] +- Modify: [[file:browser/src/participant/shared/messages.rs][browser/src/participant/shared/messages.rs]] +- Modify: [[file:browser/src/participant/frontend/core.rs][browser/src/participant/frontend/core.rs]] +- Modify: [[file:browser/src/participant/remote_stub.rs][browser/src/participant/remote_stub.rs]] +- Modify: [[file:browser/src/participant/mod.rs][browser/src/participant/mod.rs]] +- Modify: [[file:browser/src/participant/frontend/lite.rs][browser/src/participant/frontend/lite.rs]] + +** Steps + +- [ ] Step 1: Write failing launch-spec and state tests. + +Update =browser/src/participant/shared/spec.rs= test =converts_participant_config_to_launch_spec= so its config and assertions use all three new fields: + +#+BEGIN_SRC rust +let participant_config = ParticipantConfig { + username: "robert".to_string(), + session_url: Url::parse("https://example.com/m/demo").unwrap(), + app_config: Config { + audio_enabled: true, + video_enabled: false, + screenshare_enabled: true, + auto_gain_control: true, + noise_suppression: NoiseSuppression::RNNoise, + transport: TransportMode::WebRTC, + video_constraint_publish_webcam: VideoConstraint::P480, + video_constraint_subscribe: VideoConstraint::P720, + video_max_concurrent_tracks: Some(2), + blur: true, + ..Default::default() + }, +}; + +let spec = ParticipantLaunchSpec::from(participant_config); + +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)); +#+END_SRC + +- [ ] Step 2: Run the failing launch-spec test. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-browser converts_participant_config_to_launch_spec +#+END_SRC + +Expected: the test fails because =ParticipantSettings= still has =resolution=. + +- [ ] Step 3: Replace participant setting and state fields. + +In =browser/src/participant/shared/spec.rs=, replace: + +#+BEGIN_SRC rust +pub(in crate::participant) resolution: WebcamResolution, +#+END_SRC + +with: + +#+BEGIN_SRC rust +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, +#+END_SRC + +In =ParticipantSettings::from=, assign the new fields from =app_config=. + +In =browser/src/participant/shared/state.rs=, replace: + +#+BEGIN_SRC rust +pub webcam_resolution: WebcamResolution, +#+END_SRC + +with: + +#+BEGIN_SRC rust +pub video_constraint_publish_webcam: VideoConstraint, +pub video_constraint_subscribe: VideoConstraint, +pub video_max_concurrent_tracks: Option, +#+END_SRC + +- [ ] Step 4: Replace participant commands. + +In =browser/src/participant/shared/messages.rs=, replace: + +#+BEGIN_SRC rust +SetWebcamResolutions(WebcamResolution), +#+END_SRC + +with: + +#+BEGIN_SRC rust +SetVideoConstraintPublishWebcam(VideoConstraint), +SetVideoConstraintSubscribe(VideoConstraint), +SetVideoMaxConcurrentTracks(Option), +#+END_SRC + +In =browser/src/participant/mod.rs=, replace =set_webcam_resolutions= and =set_webcam_resolution= with: + +#+BEGIN_SRC rust +pub fn set_video_constraint_publish_webcam(&self, value: client_simulator_config::VideoConstraint) { + self.send_message(ParticipantMessage::SetVideoConstraintPublishWebcam(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)); +} +#+END_SRC + +- [ ] Step 5: Apply new settings in Hyper Core frontend automation. + +Update imports in =browser/src/participant/frontend/core.rs= to use the new command functions from Task 2. In =apply_all_settings=, replace the old outgoing resolution setter with: + +#+BEGIN_SRC rust +set_video_constraint_publish_webcam(driver, settings.video_constraint_publish_webcam) + .await + .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")?; +#+END_SRC + +Replace =set_webcam_resolutions_inner= with three focused setters: + +#+BEGIN_SRC rust +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") +} + +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 incoming video constraint") +} + +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") +} +#+END_SRC + +In =refresh_state_inner=, populate the new state fields: + +#+BEGIN_SRC rust +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; +} +#+END_SRC + +Update =handle_command= to dispatch the three new =ParticipantMessage= variants. + +- [ ] Step 6: Update Hyper Lite and remote stub behavior. + +In =browser/src/participant/frontend/lite.rs=, make the three new commands return a clear unsupported message matching the old lite behavior: + +#+BEGIN_SRC rust +warn!( + participant = %self.participant_name(), + "Video constraint changes not supported in lite frontend" +); +#+END_SRC + +In =browser/src/participant/remote_stub.rs=, initialize and mutate the three new fields: + +#+BEGIN_SRC rust +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, +#+END_SRC + +and in command handling: + +#+BEGIN_SRC rust +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:?}")); +} +#+END_SRC + +- [ ] Step 7: Run browser runtime tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-browser converts_participant_config_to_launch_spec remote_stub +#+END_SRC + +Expected: tests pass or expose remaining direct references to =WebcamResolution= that need replacement. + +* Task 4: Replace Headless CLI and Participant JSON Fields + +** Files + +- Modify: [[file:src/headless.rs][src/headless.rs]] + +** Steps + +- [ ] Step 1: Write failing headless parsing and override tests. + +Update =src/headless.rs= tests so command-line and participant JSON use the new names: + +#+BEGIN_SRC rust +#[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":null}"#, + ]); + 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(Some(2))); + assert_eq!(args.participants.len(), 1); +} + +#[test] +fn participant_json_can_override_video_constraints_to_unlimited() { + 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_constraint_subscribe":"480p","video_max_concurrent_tracks":null}"#.to_string()], + ) + .expect("participant configs"); + + assert_eq!(configs[0].video_constraint_publish_webcam, VideoConstraint::P360); + assert_eq!(configs[0].video_constraint_subscribe, VideoConstraint::P480); + assert_eq!(configs[0].video_max_concurrent_tracks, None); +} +#+END_SRC + +- [ ] Step 2: Run the failing headless tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p hyper-client-simulator headless::tests::cli_parsing_accepts_video_constraint_options headless::tests::participant_json_can_override_video_constraints_to_unlimited +#+END_SRC + +Expected: the command fails because =HeadlessArgs= and =ParticipantOverride= still use =resolution=. + +- [ ] Step 3: Replace =HeadlessArgs= fields. + +In =src/headless.rs=, replace: + +#+BEGIN_SRC rust +#[clap(long, value_name = "RESOLUTION")] +pub resolution: Option, +#+END_SRC + +with: + +#+BEGIN_SRC rust +#[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", value_parser = parse_video_max_concurrent_tracks)] +pub video_max_concurrent_tracks: Option>, +#+END_SRC + +Add this parser in =src/headless.rs=: + +#+BEGIN_SRC rust +fn parse_video_max_concurrent_tracks(raw: &str) -> Result, String> { + match raw { + "none" | "null" | "unlimited" => Ok(None), + _ => raw + .parse::() + .map(Some) + .map_err(|err| format!("expected unlimited/null/none or a non-negative integer: {err}")), + } +} +#+END_SRC + +- [ ] Step 4: Replace participant JSON override fields. + +In =ParticipantOverride=, replace =resolution= with: + +#+BEGIN_SRC rust +video_constraint_publish_webcam: Option, +video_constraint_subscribe: Option, +video_max_concurrent_tracks: Option>, +#+END_SRC + +In =apply_cli_overrides= and =apply_participant_override=, set the three =Config= fields. Because =video_max_concurrent_tracks= is nullable, assign when the outer option is present: + +#+BEGIN_SRC rust +if let Some(value) = args.video_max_concurrent_tracks { + config.video_max_concurrent_tracks = value; +} + +if let Some(value) = override_.video_max_concurrent_tracks { + config.video_max_concurrent_tracks = value; +} +#+END_SRC + +- [ ] Step 5: Remove old =resolution= headless support. + +Delete =resolution= from: + +- =HeadlessArgs= +- =ParticipantOverride= +- =apply_cli_overrides= +- =apply_participant_override= +- headless tests + +Then run: + +#+BEGIN_SRC bash +rg -n "\bresolution\b|WebcamResolution" src/headless.rs +#+END_SRC + +Expected: no matches in =src/headless.rs=. + +- [ ] Step 6: Run headless tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p hyper-client-simulator headless +#+END_SRC + +Expected: all headless tests pass. + +* Task 5: Replace TUI Surfaces + +** Files + +- Modify: [[file:tui/src/tui/components/browser_start.rs][tui/src/tui/components/browser_start.rs]] +- Modify: [[file:tui/src/tui/components/participants.rs][tui/src/tui/components/participants.rs]] + +** Steps + +- [ ] Step 1: Replace imports and component fields. + +In both files, replace =WebcamResolution= imports and =resolution_list= fields with: + +#+BEGIN_SRC rust +use client_simulator_config::{ + Config, + NoiseSuppression, + VideoConstraint, + VideoConstraintIter, + VideoMaxConcurrentTracksPreset, + VideoMaxConcurrentTracksPresetIter, +}; +#+END_SRC + +Use these component fields: + +#+BEGIN_SRC rust +video_constraint_publish_webcam_list: Option>, +video_constraint_subscribe_list: Option>, +video_max_concurrent_tracks_list: Option>, +#+END_SRC + +- [ ] Step 2: Update start-screen labels and selection handlers. + +In =browser_start.rs=, replace camera resolution text with: + +#+BEGIN_SRC rust +SelectedField::VideoConstraintPublishWebcam => " Select outgoing webcam max resolution. to select. ", +SelectedField::VideoConstraintSubscribe => " Select incoming max resolution. to select. ", +SelectedField::VideoMaxConcurrentTracks => " Select max concurrent webcam tracks. to select. ", +#+END_SRC + +When a preset is selected, assign: + +#+BEGIN_SRC rust +self.config.video_max_concurrent_tracks = value.to_option(); +#+END_SRC + +For the current value, derive the preset with: + +#+BEGIN_SRC rust +VideoMaxConcurrentTracksPreset::from_option(self.config.video_max_concurrent_tracks) +#+END_SRC + +- [ ] Step 3: Update participant table columns and live commands. + +In =participants.rs=, replace the single camera resolution list with three selection paths: + +#+BEGIN_SRC rust +participant.set_video_constraint_publish_webcam(value); +participant.set_video_constraint_subscribe(value); +participant.set_video_max_concurrent_tracks(value.to_option()); +#+END_SRC + +Display the state values with: + +#+BEGIN_SRC rust +let publish_constraint = state.video_constraint_publish_webcam.to_string(); +let subscribe_constraint = state.video_constraint_subscribe.to_string(); +let max_tracks = state + .video_max_concurrent_tracks + .map(|value| value.to_string()) + .unwrap_or_else(|| "unlimited".to_string()); +#+END_SRC + +- [ ] Step 4: Run TUI tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p client-simulator-tui +#+END_SRC + +Expected: TUI tests pass and compile errors identify any remaining old field names. + +* Task 6: Update Cloudflare Worker Client Contract and Mappings + +** Files + +- Modify: [[file:cloudflare-worker-client/openapi/cloudflare-browser-simulator.json][cloudflare-worker-client/openapi/cloudflare-browser-simulator.json]] +- Modify: [[file:cloudflare-worker-client/src/client.rs][cloudflare-worker-client/src/client.rs]] +- Modify: [[file:browser/src/participant/cloudflare/mod.rs][browser/src/participant/cloudflare/mod.rs]] +- Modify: [[file:browser/tests/cloudflare_driver.rs][browser/tests/cloudflare_driver.rs]] + +** Steps + +- [ ] Step 1: Update the OpenAPI schema. + +In =ParticipantSettings=, replace the =resolution= property with: + +#+BEGIN_SRC json +"videoConstraintPublishWebcam": { + "type": "string", + "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] +}, +"videoConstraintSubscribe": { + "type": "string", + "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] +}, +"videoMaxConcurrentTracks": { + "type": ["number", "null"], + "minimum": 0 +} +#+END_SRC + +Replace =resolution= in =required= with the three new property names. + +In =SessionCommandRequest=, replace the =set-webcam-resolution= variant with these three variants: + +#+BEGIN_SRC json +{ + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["set-video-constraint-publish-webcam"] }, + "videoConstraintPublishWebcam": { + "type": "string", + "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] + } + }, + "required": ["type", "videoConstraintPublishWebcam"] +}, +{ + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["set-video-constraint-subscribe"] }, + "videoConstraintSubscribe": { + "type": "string", + "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] + } + }, + "required": ["type", "videoConstraintSubscribe"] +}, +{ + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["set-video-max-concurrent-tracks"] }, + "videoMaxConcurrentTracks": { + "type": ["number", "null"], + "minimum": 0 + } + }, + "required": ["type", "videoMaxConcurrentTracks"] +} +#+END_SRC + +If the deployed worker uses different command names, update these strings to match the worker source before running tests. Do not keep =set-webcam-resolution= in the new simulator contract. + +- [ ] Step 2: Regenerate and compile the worker client. + +Run: + +#+BEGIN_SRC bash +cargo check -p cloudflare-worker-client +#+END_SRC + +Expected: generated types include =ParticipantSettingsVideoConstraintPublishWebcam=, =ParticipantSettingsVideoConstraintSubscribe=, and command variants for all three settings. Compile errors in tests still reference =ParticipantSettingsResolution=. + +- [ ] Step 3: Update cloudflare-worker-client tests. + +In =cloudflare-worker-client/src/client.rs=, replace imports: + +#+BEGIN_SRC rust +ParticipantSettingsResolution, +#+END_SRC + +with the generated video constraint enum names from the compile output, then update =create_session_request=: + +#+BEGIN_SRC rust +settings: ParticipantSettings { + audio_enabled: true, + auto_gain_control: true, + blur: false, + noise_suppression: ParticipantSettingsNoiseSuppression::None, + 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, +}, +#+END_SRC + +Update schema-mismatch fixture JSON to contain the three new state fields if =ParticipantState= is changed in the worker schema. + +- [ ] Step 4: Update browser Cloudflare mappings. + +In =browser/src/participant/cloudflare/mod.rs=, replace =map_command_webcam_resolution= with: + +#+BEGIN_SRC rust +fn map_video_constraint(value: client_simulator_config::VideoConstraint) -> types::ParticipantSettingsVideoConstraintPublishWebcam { + match value { + client_simulator_config::VideoConstraint::None => types::ParticipantSettingsVideoConstraintPublishWebcam::None, + client_simulator_config::VideoConstraint::P90 => types::ParticipantSettingsVideoConstraintPublishWebcam::P90, + client_simulator_config::VideoConstraint::P144 => types::ParticipantSettingsVideoConstraintPublishWebcam::P144, + client_simulator_config::VideoConstraint::P240 => types::ParticipantSettingsVideoConstraintPublishWebcam::P240, + client_simulator_config::VideoConstraint::P360 => types::ParticipantSettingsVideoConstraintPublishWebcam::P360, + client_simulator_config::VideoConstraint::P480 => types::ParticipantSettingsVideoConstraintPublishWebcam::P480, + client_simulator_config::VideoConstraint::P720 => types::ParticipantSettingsVideoConstraintPublishWebcam::P720, + client_simulator_config::VideoConstraint::P1080 => types::ParticipantSettingsVideoConstraintPublishWebcam::P1080, + client_simulator_config::VideoConstraint::P1440 => types::ParticipantSettingsVideoConstraintPublishWebcam::P1440, + client_simulator_config::VideoConstraint::P2160 => types::ParticipantSettingsVideoConstraintPublishWebcam::P2160, + } +} +#+END_SRC + +If =progenitor= generates separate enum types for publish and subscribe settings, use two mapping functions with identical match arms: + +- =map_video_constraint_publish_webcam= returns =types::ParticipantSettingsVideoConstraintPublishWebcam=. +- =map_video_constraint_subscribe= returns =types::ParticipantSettingsVideoConstraintSubscribe=. + +Update =map_settings=: + +#+BEGIN_SRC rust +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 f64), +#+END_SRC + +Update =command_request= for the three new message variants. Use the generated variant names from =cargo check=, for example: + +#+BEGIN_SRC rust +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 f64), + } +} +#+END_SRC + +- [ ] Step 5: Update Cloudflare tests and fixtures. + +Replace expected JSON: + +#+BEGIN_SRC json +{ "type": "set-webcam-resolution", "webcamResolution": "p1080" } +#+END_SRC + +with three separate command expectations: + +#+BEGIN_SRC rust +json!({ "type": "set-video-constraint-publish-webcam", "videoConstraintPublishWebcam": "1080p" }) +json!({ "type": "set-video-constraint-subscribe", "videoConstraintSubscribe": "720p" }) +json!({ "type": "set-video-max-concurrent-tracks", "videoMaxConcurrentTracks": 2 }) +#+END_SRC + +Update worker state fixtures that currently use =webcamResolution= so they use: + +#+BEGIN_SRC json +"videoConstraintPublishWebcam": "720p", +"videoConstraintSubscribe": "none", +"videoMaxConcurrentTracks": null +#+END_SRC + +- [ ] Step 6: Run Cloudflare tests. + +Run: + +#+BEGIN_SRC bash +cargo test -p cloudflare-worker-client +cargo test -p client-simulator-browser cloudflare +#+END_SRC + +Expected: both commands pass. + +* Task 7: Remove Legacy Resolution References and Run Full Verification + +** Files + +- Modify any file reported by the search commands in this task. + +** Steps + +- [ ] Step 1: Search for legacy runtime API references. + +Run: + +#+BEGIN_SRC bash +rg -n "videoResolutionForWebcamEncoder|setVideoResolutionForWebcamEncoder|videoResolutionWebcamMedia|setVideoResolutionWebcamMedia" . +#+END_SRC + +Expected: no matches in simulator source. Matches in documentation are acceptable only if they explicitly describe removed legacy behavior. + +- [ ] Step 2: Search for old simulator setting names. + +Run: + +#+BEGIN_SRC bash +rg -n "\bresolution\b|WebcamResolution|webcam_resolution|SetWebcamResolutions|set_webcam_resolution|webcamResolution|set-webcam-resolution" config/src browser/src tui/src src cloudflare-worker-client browser/tests +#+END_SRC + +Expected: no active simulator code uses old webcam resolution names. Remaining matches must be deliberate comments in this plan or compatibility notes outside active code. + +- [ ] Step 3: Run formatting and linting. + +Run: + +#+BEGIN_SRC bash +just fmt +just clippy +#+END_SRC + +Expected: both commands exit 0. + +- [ ] Step 4: Run the full test suite. + +Run: + +#+BEGIN_SRC bash +just test +#+END_SRC + +Expected: all tests pass. + +- [ ] Step 5: Run a live local-backend smoke test against latest.dev. + +Run: + +#+BEGIN_SRC bash +cargo run -- headless \ + --url https://latest.dev.hyper.video/0Y6-6FZ-20J \ + --participant '{"backend":"local","video_constraint_publish_webcam":"480p","video_constraint_subscribe":"720p","video_max_concurrent_tracks":1}' +#+END_SRC + +Expected: + +- Logs do not contain =Failed to apply settings before joining=. +- Logs do not contain =videoResolutionForWebcamEncoder=. +- Participant reaches =Joined the space=. +- The process stays running until Ctrl-C. + +- [ ] Step 6: Run a nullable track-limit smoke test. + +Run: + +#+BEGIN_SRC bash +cargo run -- headless \ + --url https://latest.dev.hyper.video/0Y6-6FZ-20J \ + --participant '{"backend":"local","video_constraint_publish_webcam":"360p","video_constraint_subscribe":"none","video_max_concurrent_tracks":null}' +#+END_SRC + +Expected: + +- Logs do not contain =Failed to apply settings before joining=. +- Live page has =window.hyper.settings.media.videoMaxConcurrentTracks === null=. +- Ctrl-C performs graceful shutdown. + +* Self-Review Checklist + +- [ ] Every active simulator path that used =resolution= now has publish constraint, subscribe constraint, and max-track setting coverage. +- [ ] Local Hyper Core browser automation uses =hyper.settings.media=, not =hyper.settings.videoCodec=, for these settings. +- [ ] Headless CLI and participant JSON can set =video_max_concurrent_tracks= to both a number and =null=. +- [ ] TUI can inspect and change the three new values for running participants. +- [ ] Cloudflare worker schema and mappings no longer serialize =webcamResolution= or =set-webcam-resolution=. +- [ ] Full verification ran with =just fmt=, =just clippy=, =just test=, and at least one live latest.dev smoke test. From 61b3e7f1f9bd66b818c9e4278db44fdf12cda402 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Tue, 2 Jun 2026 20:28:14 +0200 Subject: [PATCH 21/42] 2026-06-02-new-video-constraints-support.org v2 --- 2026-06-02-new-video-constraints-support.org | 675 +++++++++++-------- 1 file changed, 383 insertions(+), 292 deletions(-) diff --git a/2026-06-02-new-video-constraints-support.org b/2026-06-02-new-video-constraints-support.org index d5d5423..361db74 100644 --- a/2026-06-02-new-video-constraints-support.org +++ b/2026-06-02-new-video-constraints-support.org @@ -14,15 +14,32 @@ Replace simulator support for the removed Hyper Core webcam encoder resolution s - =videoConstraintSubscribe=: incoming max-resolution option. - =videoMaxConcurrentTracks=: max number of outgoing video tracks per local video stream, or =null= for unlimited. +This spans TWO repositories: + +1. The simulator: [[file:/Users/robert/projects/shuttle/simulator/hyper-browser-simulator][hyper-browser-simulator]] (this repo). +2. The Cloudflare worker: [[file:/Users/robert/projects/shuttle/simulator/cloudflare-browser-simulator][cloudflare-browser-simulator]] (sibling repo) — the remote backend the simulator's =cloudflare= participant type talks to. The worker is currently broken against the live Hyper SDK anyway: it still calls the removed =hyper.settings.videoCodec.actions.setVideoResolutionForWebcamEncoder= API, so it must be updated regardless. + +* Decisions (resolved during planning) + +- *Worker:* update the worker repo too, for full parity. The simulator's bundled OpenAPI is generated output, vendored from the worker. +- *OpenAPI sync:* worker-first. Change worker Zod + controls, run =just export-openapi= (chanfana), then *full re-vendor* of the regenerated JSON into the simulator. The simulator's vendored copy is already stale (=0.3.0= vs worker =0.4.1=), so re-vendoring moves it to the current version and may surface unrelated drift that needs simulator-side fixups. +- *Deploy:* all worker + simulator code changes and regenerated artifacts land in this work. The outward-facing =just deploy-worker= and the live =cloudflare=-backend smoke test are a MANUAL step the maintainer runs/approves — do not deploy automatically. +- *Wire casing:* standardize on =1080p= (digits-then-=p=) everywhere — worker Zod, OpenAPI, browser commands. This is the literal string the page's =setVideoConstraintPublishWebcam= expects. Drop the old =p1080= (=p=-then-digits) casing. +- *Default:* =none= / =none= / =null= (unconstrained), matching the SDK. Closest equivalent to the old =auto=. +- *Nullable semantics:* =null= = absent = "no override". Fields are plain =Option= (NOT double-option). Consequence: a participant cannot force a numeric global back to unlimited via CLI/JSON; to get unlimited, omit the field / rely on the default. +- *Max-tracks presets (TUI):* drop the unreachable =Custom=. Presets are =Unlimited / 1 / 2 / 3 / 4=. Off-list live values (e.g. =8= from config) fall back to =Unlimited= for the selector's preselection only; the table still displays the true number. +- *Participants table:* replace the single =Resolution= column with ONE combined column (12 columns total, not 14). Format =out:480p in:720p t:2=; unlimited shows =t:∞=. Header "Video constraints". +- *TUI per-participant edit:* the single =r= key opens a small chooser sub-menu (Outgoing / Incoming / Track limit), then opens the value list for the chosen setting. + * Architecture Treat the new controls as media settings, not video codec settings. The active local-browser JavaScript API should read and write =window.hyper.settings.media= through =setVideoConstraintPublishWebcam=, =setVideoConstraintSubscribe=, and =setVideoMaxConcurrentTracks=. -Replace the simulator's old single =resolution= setting throughout the active config, participant launch settings, participant state, headless CLI, remote-stub backend, and TUI surfaces. Keep the Cloudflare backend in sync by updating the bundled worker OpenAPI schema and browser-side mapping tests so remote participants receive the same three setting values. +Replace the simulator's old single =resolution= setting throughout the active config, participant launch settings, participant state, headless CLI, remote-stub backend, and TUI surfaces. Keep the Cloudflare backend in sync by updating the worker's Zod schemas + controls, regenerating the OpenAPI, and re-vendoring it into the simulator so the progenitor-generated client carries the same three setting values. * Tech Stack -Rust 2021, =serde=, =strum=, =clap=, =tokio=, =chromiumoxide=, =thirtyfour=, generated =cloudflare-worker-client= types from =progenitor=, and the existing =just= recipes. +Rust 2021, =serde=, =strum=, =clap=, =tokio=, =chromiumoxide=, =thirtyfour=, generated =cloudflare-worker-client= types from =progenitor= (built by =build.rs= over the vendored OpenAPI), and the existing =just= recipes. Worker repo: TypeScript, =zod=, =chanfana= (Zod→OpenAPI), =wrangler=. * Source Context @@ -49,6 +66,8 @@ export const VIDEO_CONSTRAINT_OPTIONS = { } as const; #+END_SRC +Note vs the old =WebcamResolution=: the new =VideoConstraint= *adds* =90p=, *drops* =4320p= (8K), and replaces =auto= with =none=. Match the SDK exactly. + The active runtime actions are in [[file:/Users/robert/projects/shuttle/hyper.video-3/frontend/web/sdk/src/media/state/media-settings.tsx][media-settings.tsx]]: #+BEGIN_SRC typescript @@ -70,17 +89,30 @@ const CAMERA_RES_GET: &str = "return hyper.settings.videoCodec.videoResolutionFo const CAMERA_RES_SET: &str = "hyper.settings.videoCodec.actions.setVideoResolutionForWebcamEncoder(arguments[0]);"; #+END_SRC +*Precondition for the live smoke tests:* =latest.dev.hyper.video= must run a Hyper build that exposes =hyper.settings.media.actions.setVideoConstraintPublishWebcam= (and the subscribe / max-tracks actions). If the deployed SDK is behind, the live runs in Task 7 will fail even with correct code — confirm the deployed SDK version first. + * File Map -- Modify [[file:config/src/client_config.rs][config/src/client_config.rs]]: add =VideoConstraint= and a CLI/TUI-friendly =VideoMaxConcurrentTracksPreset= helper; stop using =WebcamResolution= for active Hyper Core media settings. -- Modify [[file:config/src/lib.rs][config/src/lib.rs]]: replace =Config.resolution= with =video_constraint_publish_webcam=, =video_constraint_subscribe=, and =video_max_concurrent_tracks=. -- Modify [[file:config/src/default-config.yaml][config/src/default-config.yaml]]: default both constraints to =none= and =video_max_concurrent_tracks= to =null= or omitted. +** Simulator repo + +- Modify [[file:config/src/client_config.rs][config/src/client_config.rs]]: add =VideoConstraint= and a CLI/TUI-friendly =VideoMaxConcurrentTracksPreset= helper; remove =WebcamResolution=. +- Modify [[file:config/src/lib.rs][config/src/lib.rs]]: replace =Config.resolution= with =video_constraint_publish_webcam=, =video_constraint_subscribe=, and =video_max_concurrent_tracks=; update the exported names. +- Modify [[file:config/src/default-config.yaml][config/src/default-config.yaml]]: default both constraints to =none= and omit =video_max_concurrent_tracks=. - Modify [[file:browser/src/participant/frontend/commands.rs][browser/src/participant/frontend/commands.rs]]: replace camera resolution commands with media setting commands and unit tests over a fake =BrowserDriver=. -- Modify [[file:browser/src/participant/frontend/core.rs][browser/src/participant/frontend/core.rs]]: apply and refresh the three new settings. -- Modify [[file:browser/src/participant/shared/spec.rs][browser/src/participant/shared/spec.rs]], [[file:browser/src/participant/shared/state.rs][state.rs]], [[file:browser/src/participant/shared/messages.rs][messages.rs]], [[file:browser/src/participant/remote_stub.rs][remote_stub.rs]], and [[file:browser/src/participant/mod.rs][mod.rs]]: thread the new setting names through participant state and commands. -- Modify [[file:src/headless.rs][src/headless.rs]]: replace =--resolution= and participant JSON =resolution= with the three new fields. -- Modify [[file:tui/src/tui/components/browser_start.rs][tui/src/tui/components/browser_start.rs]] and [[file:tui/src/tui/components/participants.rs][participants.rs]]: replace the camera resolution selector/table cell with outgoing constraint, incoming constraint, and track-limit controls. -- Modify [[file:cloudflare-worker-client/openapi/cloudflare-browser-simulator.json][cloudflare-worker-client/openapi/cloudflare-browser-simulator.json]], [[file:cloudflare-worker-client/src/client.rs][client.rs]], and [[file:browser/src/participant/cloudflare/mod.rs][browser/src/participant/cloudflare/mod.rs]]: replace worker schema and mappings for legacy resolution. +- Modify [[file:browser/src/participant/frontend/core.rs][browser/src/participant/frontend/core.rs]]: apply and refresh the three new settings; dispatch the three new commands. +- Modify [[file:browser/src/participant/frontend/lite.rs][browser/src/participant/frontend/lite.rs]]: warn-unsupported on the three commands AND replace the hardcoded =refresh_state_inner= resolution default with the three new defaulted fields. +- Modify [[file:browser/src/participant/shared/spec.rs][spec.rs]], [[file:browser/src/participant/shared/state.rs][state.rs]], [[file:browser/src/participant/shared/messages.rs][messages.rs]], [[file:browser/src/participant/shared/runtime.rs][runtime.rs]], [[file:browser/src/participant/remote_stub.rs][remote_stub.rs]], and [[file:browser/src/participant/mod.rs][mod.rs]]: thread the new setting names through participant state and commands. =runtime.rs= has an exhaustive =ParticipantMessage= match (test mock) that must be updated. =mod.rs= must drop the =set_webcam_resolution= singular alias. +- Modify [[file:src/headless.rs][src/headless.rs]]: replace =--resolution= and participant JSON =resolution= with the three new fields (plain =Option= for the track limit). +- Modify [[file:tui/src/tui/components/browser_start.rs][tui/src/tui/components/browser_start.rs]] and [[file:tui/src/tui/components/participants.rs][participants.rs]]: replace the camera resolution selector/table cell with outgoing constraint, incoming constraint, and track-limit controls (start screen: three fields; participants table: one combined column + chooser sub-menu). +- Modify [[file:cloudflare-worker-client/openapi/cloudflare-browser-simulator.json][cloudflare-worker-client/openapi/cloudflare-browser-simulator.json]] (re-vendored, not hand-edited), [[file:cloudflare-worker-client/src/client.rs][client.rs]], [[file:browser/src/participant/cloudflare/mod.rs][browser/src/participant/cloudflare/mod.rs]], and [[file:browser/tests/cloudflare_driver.rs][browser/tests/cloudflare_driver.rs]]. + +** Worker repo (=../cloudflare-browser-simulator=) + +- Modify =worker/src/api/schemas.ts=: replace =WebcamResolutionSchema= with =VideoConstraintSchema=; replace =resolution= / =webcamResolution= in =ParticipantSettings= and =ParticipantState= with the three new fields; replace the =set-webcam-resolution= command schema with three new ones and add them to the command union. +- Modify =worker/src/frontend/core.ts=: replace =setWebcamResolution= in =CoreControls= with three methods that call =hyper.settings.media.actions.*=; update the command switch, the pre-join apply, the state readback (read bare strings + number|null from =hyper.settings.media=), and =buildParticipantState= defaults. +- Modify =worker/src/frontend/lite.ts=: replace the =setWebcamResolution= stub with three no-op stubs; update the unsupported log, the command cases, and =buildLiteParticipantState= defaults. +- Modify worker unit tests (=worker/src/*.test.ts=) and the worker CLI smoke test (=cli/src/commands/smoke_test.rs=) fixtures. +- Regenerate =cli/openapi/cloudflare-browser-simulator.json= and =worker/worker-configuration.d.ts= via the justfile recipes. * Task 1: Add Config Types and Replace Active Config Fields @@ -94,7 +126,7 @@ const CAMERA_RES_SET: &str = "hyper.settings.videoCodec.actions.setVideoResoluti - [ ] Step 1: Write failing config type tests. -Add these tests in =config/src/client_config.rs=. +Replace the existing =tests= module in =config/src/client_config.rs= with: #+BEGIN_SRC rust #[cfg(test)] @@ -134,10 +166,14 @@ mod tests { 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(2)), VideoMaxConcurrentTracksPreset::Two); - assert_eq!(VideoMaxConcurrentTracksPreset::from_option(Some(8)), VideoMaxConcurrentTracksPreset::Custom); + 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] @@ -150,24 +186,20 @@ mod tests { } #+END_SRC -Replace the existing =tests= module instead of creating a duplicate module. - - [ ] Step 2: Run the failing config type tests. -Run: - #+BEGIN_SRC bash cargo test -p client-simulator-config video_constraint_round_trips_hyper_media_setting_names video_track_limit_presets_convert_to_nullable_track_counts #+END_SRC -Expected: the command fails because =VideoConstraint= and =VideoMaxConcurrentTracksPreset= do not exist. +Expected: fails because =VideoConstraint= and =VideoMaxConcurrentTracksPreset= do not exist. -- [ ] Step 3: Add the new config enums. +- [ ] Step 3: Add the new config enums and remove =WebcamResolution=. -Add this near =WebcamResolution= in =config/src/client_config.rs=. Keep =WebcamResolution= only if Cloudflare compatibility tests still need it during the transition; do not use it for active Hyper Core media settings after Task 3. +In =config/src/client_config.rs=, delete the =WebcamResolution= enum and add: #+BEGIN_SRC rust -#[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Default, Clone, Copy, Display, EnumIter, EnumString, Serialize, Deserialize, PartialEq, Eq)] pub enum VideoConstraint { #[default] #[strum(to_string = "none")] @@ -211,8 +243,10 @@ pub enum VideoMaxConcurrentTracksPreset { One, #[strum(to_string = "2")] Two, - #[strum(to_string = "custom")] - Custom, + #[strum(to_string = "3")] + Three, + #[strum(to_string = "4")] + Four, } impl VideoMaxConcurrentTracksPreset { @@ -221,7 +255,8 @@ impl VideoMaxConcurrentTracksPreset { Self::Unlimited => None, Self::One => Some(1), Self::Two => Some(2), - Self::Custom => None, + Self::Three => Some(3), + Self::Four => Some(4), } } @@ -230,15 +265,21 @@ impl VideoMaxConcurrentTracksPreset { None => Self::Unlimited, Some(1) => Self::One, Some(2) => Self::Two, - Some(_) => Self::Custom, + 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, } } } #+END_SRC -- [ ] Step 4: Export the new types. +(=WebcamResolution= is removed entirely; nothing keeps it during the transition because Task 6 also replaces the Cloudflare mappings.) + +- [ ] Step 4: Update the exports. -Modify the =pub use client_config= block in =config/src/lib.rs=. +In =config/src/lib.rs=, replace =WebcamResolution, WebcamResolutionIter= in the =pub use client_config= block with: #+BEGIN_SRC rust pub use client_config::{ @@ -274,7 +315,7 @@ pub video_constraint_subscribe: VideoConstraint, pub video_max_concurrent_tracks: Option, #+END_SRC -Update =impl config::Source for Config= so the collected source emits only the new fields: +In =impl config::Source for Config=, replace the =resolution= insert with: #+BEGIN_SRC rust cache.insert( @@ -290,17 +331,9 @@ if let Some(value) = self.video_max_concurrent_tracks { } #+END_SRC -Remove the old =resolution= insert from this method. - - [ ] Step 6: Update default YAML. -In =config/src/default-config.yaml=, replace: - -#+BEGIN_SRC yaml -resolution: auto -#+END_SRC - -with: +In =config/src/default-config.yaml=, replace =resolution: auto= with: #+BEGIN_SRC yaml video_constraint_publish_webcam: none @@ -310,7 +343,7 @@ video_max_concurrent_tracks: - [ ] Step 7: Add config deserialization tests. -Add these tests in =config/src/lib.rs=. +Add to the =tests= module in =config/src/lib.rs=: #+BEGIN_SRC rust #[test] @@ -347,8 +380,6 @@ fn default_video_max_concurrent_tracks_is_unlimited() { - [ ] Step 8: Run config tests. -Run: - #+BEGIN_SRC bash cargo test -p client-simulator-config #+END_SRC @@ -500,22 +531,30 @@ mod tests { )], ); } + + #[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)); + } } #+END_SRC - [ ] Step 2: Run the failing browser command tests. -Run: - #+BEGIN_SRC bash -cargo test -p client-simulator-browser video_constraints_through_media_settings_api gets_video_constraints_from_media_settings_api gets_nullable_video_max_concurrent_tracks +cargo test -p client-simulator-browser sets_video_constraints_through_media_settings_api gets_video_constraints_from_media_settings_api gets_nullable_video_max_concurrent_tracks reads_integer_valued_max_concurrent_tracks_even_as_float #+END_SRC -Expected: the command fails because the new functions and imported types do not exist. +Expected: fails because the new functions and imported types do not exist. - [ ] Step 3: Replace the command constants and functions. -In =browser/src/participant/frontend/commands.rs=, import =VideoConstraint= instead of =WebcamResolution= for video constraints: +Import =VideoConstraint= instead of =WebcamResolution=: #+BEGIN_SRC rust use client_simulator_config::{ @@ -538,7 +577,7 @@ const VIDEO_MAX_CONCURRENT_TRACKS_SET: &str = "hyper.settings.media.actions.setVideoMaxConcurrentTracks(arguments[0]);"; #+END_SRC -Replace =get_outgoing_camera_resolution= and =set_outgoing_camera_resolution= with: +Replace =get_outgoing_camera_resolution= / =set_outgoing_camera_resolution= with: #+BEGIN_SRC rust pub(super) async fn get_video_constraint_publish_webcam(driver: &dyn BrowserDriver) -> Result { @@ -567,8 +606,11 @@ pub(super) async fn set_video_constraint_subscribe(driver: &dyn BrowserDriver, v } 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?; - serde_json::from_value(value).context("failed to read videoMaxConcurrentTracks from eval result") + 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( @@ -581,13 +623,11 @@ pub(super) async fn set_video_max_concurrent_tracks( - [ ] Step 4: Run browser command tests. -Run: - #+BEGIN_SRC bash -cargo test -p client-simulator-browser video_constraints +cargo test -p client-simulator-browser video_constraint #+END_SRC -Expected: command tests pass and no remaining test references call =videoResolutionForWebcamEncoder=. +Expected: command tests pass; no remaining references to =videoResolutionForWebcamEncoder=. * Task 3: Thread New Settings Through Participant Runtime @@ -596,16 +636,17 @@ Expected: command tests pass and no remaining test references call =videoResolut - Modify: [[file:browser/src/participant/shared/spec.rs][browser/src/participant/shared/spec.rs]] - Modify: [[file:browser/src/participant/shared/state.rs][browser/src/participant/shared/state.rs]] - Modify: [[file:browser/src/participant/shared/messages.rs][browser/src/participant/shared/messages.rs]] +- Modify: [[file:browser/src/participant/shared/runtime.rs][browser/src/participant/shared/runtime.rs]] - Modify: [[file:browser/src/participant/frontend/core.rs][browser/src/participant/frontend/core.rs]] +- Modify: [[file:browser/src/participant/frontend/lite.rs][browser/src/participant/frontend/lite.rs]] - Modify: [[file:browser/src/participant/remote_stub.rs][browser/src/participant/remote_stub.rs]] - Modify: [[file:browser/src/participant/mod.rs][browser/src/participant/mod.rs]] -- Modify: [[file:browser/src/participant/frontend/lite.rs][browser/src/participant/frontend/lite.rs]] ** Steps - [ ] Step 1: Write failing launch-spec and state tests. -Update =browser/src/participant/shared/spec.rs= test =converts_participant_config_to_launch_spec= so its config and assertions use all three new fields: +Update the =spec.rs= test =converts_participant_config_to_launch_spec= so its config and assertions use all three new fields: #+BEGIN_SRC rust let participant_config = ParticipantConfig { @@ -635,23 +676,15 @@ assert_eq!(spec.settings.video_max_concurrent_tracks, Some(2)); - [ ] Step 2: Run the failing launch-spec test. -Run: - #+BEGIN_SRC bash cargo test -p client-simulator-browser converts_participant_config_to_launch_spec #+END_SRC -Expected: the test fails because =ParticipantSettings= still has =resolution=. +Expected: fails because =ParticipantSettings= still has =resolution=. - [ ] Step 3: Replace participant setting and state fields. -In =browser/src/participant/shared/spec.rs=, replace: - -#+BEGIN_SRC rust -pub(in crate::participant) resolution: WebcamResolution, -#+END_SRC - -with: +In =spec.rs=, replace =pub(in crate::participant) resolution: WebcamResolution,= with: #+BEGIN_SRC rust pub(in crate::participant) video_constraint_publish_webcam: VideoConstraint, @@ -659,15 +692,9 @@ pub(in crate::participant) video_constraint_subscribe: VideoConstraint, pub(in crate::participant) video_max_concurrent_tracks: Option, #+END_SRC -In =ParticipantSettings::from=, assign the new fields from =app_config=. - -In =browser/src/participant/shared/state.rs=, replace: - -#+BEGIN_SRC rust -pub webcam_resolution: WebcamResolution, -#+END_SRC +In =ParticipantSettings::from=, assign the three new fields from =app_config=. -with: +In =state.rs=, replace =pub webcam_resolution: WebcamResolution,= with: #+BEGIN_SRC rust pub video_constraint_publish_webcam: VideoConstraint, @@ -675,15 +702,9 @@ pub video_constraint_subscribe: VideoConstraint, pub video_max_concurrent_tracks: Option, #+END_SRC -- [ ] Step 4: Replace participant commands. +- [ ] Step 4: Replace participant commands and the public API. -In =browser/src/participant/shared/messages.rs=, replace: - -#+BEGIN_SRC rust -SetWebcamResolutions(WebcamResolution), -#+END_SRC - -with: +In =messages.rs=, replace =SetWebcamResolutions(WebcamResolution),= with: #+BEGIN_SRC rust SetVideoConstraintPublishWebcam(VideoConstraint), @@ -691,7 +712,7 @@ SetVideoConstraintSubscribe(VideoConstraint), SetVideoMaxConcurrentTracks(Option), #+END_SRC -In =browser/src/participant/mod.rs=, replace =set_webcam_resolutions= and =set_webcam_resolution= with: +In =mod.rs=, *delete* both =set_webcam_resolutions= and the =set_webcam_resolution= singular alias, and add: #+BEGIN_SRC rust pub fn set_video_constraint_publish_webcam(&self, value: client_simulator_config::VideoConstraint) { @@ -707,9 +728,11 @@ pub fn set_video_max_concurrent_tracks(&self, value: Option) { } #+END_SRC -- [ ] Step 5: Apply new settings in Hyper Core frontend automation. +In =runtime.rs=, update the exhaustive =ParticipantMessage= match in the test-mock =handle_command= (it currently lists =SetWebcamResolutions(_)= among the no-op arms) so it lists the three new variants instead. This is required for the crate to compile. -Update imports in =browser/src/participant/frontend/core.rs= to use the new command functions from Task 2. In =apply_all_settings=, replace the old outgoing resolution setter with: +- [ ] Step 5: Apply and refresh new settings in Hyper Core (=core.rs=). + +Update imports to the new command functions. In =apply_all_settings=, replace the outgoing-resolution setter with: #+BEGIN_SRC rust set_video_constraint_publish_webcam(driver, settings.video_constraint_publish_webcam) @@ -748,7 +771,7 @@ async fn set_video_max_concurrent_tracks_inner(&self, value: Option) -> R } #+END_SRC -In =refresh_state_inner=, populate the new state fields: +In =refresh_state_inner=, populate the new state fields (keep the existing =if let Ok(...)= soft-fail pattern): #+BEGIN_SRC rust if let Ok(value) = get_video_constraint_publish_webcam(driver).await { @@ -764,11 +787,11 @@ if let Ok(value) = get_video_max_concurrent_tracks(driver).await { } #+END_SRC -Update =handle_command= to dispatch the three new =ParticipantMessage= variants. +Update =handle_command= to dispatch the three new =ParticipantMessage= variants to the three inner setters. -- [ ] Step 6: Update Hyper Lite and remote stub behavior. +- [ ] Step 6: Update Hyper Lite (=lite.rs=). -In =browser/src/participant/frontend/lite.rs=, make the three new commands return a clear unsupported message matching the old lite behavior: +Replace =set_webcam_resolutions_inner= with three no-op inner methods that warn, matching the existing lite pattern: #+BEGIN_SRC rust warn!( @@ -777,7 +800,19 @@ warn!( ); #+END_SRC -In =browser/src/participant/remote_stub.rs=, initialize and mutate the three new fields: +Update =handle_command= to dispatch the three new variants to those no-ops. + +Also update lite's =refresh_state_inner= — it currently hardcodes =webcam_resolution: WebcamResolution::default()=. Replace that single field with the three new fields, defaulted: + +#+BEGIN_SRC rust +video_constraint_publish_webcam: VideoConstraint::default(), +video_constraint_subscribe: VideoConstraint::default(), +video_max_concurrent_tracks: None, +#+END_SRC + +- [ ] Step 7: Update the remote stub (=remote_stub.rs=). + +Initialize the three new state fields from =launch_spec.settings=: #+BEGIN_SRC rust video_constraint_publish_webcam: self.launch_spec.settings.video_constraint_publish_webcam, @@ -785,7 +820,7 @@ video_constraint_subscribe: self.launch_spec.settings.video_constraint_subscribe video_max_concurrent_tracks: self.launch_spec.settings.video_max_concurrent_tracks, #+END_SRC -and in command handling: +and handle the three commands: #+BEGIN_SRC rust ParticipantMessage::SetVideoConstraintPublishWebcam(value) => { @@ -802,18 +837,18 @@ ParticipantMessage::SetVideoMaxConcurrentTracks(value) => { } #+END_SRC -- [ ] Step 7: Run browser runtime tests. - -Run: +- [ ] Step 8: Run browser runtime tests. #+BEGIN_SRC bash cargo test -p client-simulator-browser converts_participant_config_to_launch_spec remote_stub #+END_SRC -Expected: tests pass or expose remaining direct references to =WebcamResolution= that need replacement. +Expected: tests pass or expose remaining =WebcamResolution= references that need replacement. * Task 4: Replace Headless CLI and Participant JSON Fields +Note: =null= = absent = "no override" (plain =Option=). No double-option, no =serde_with=, no custom parser. A participant cannot force unlimited via CLI/JSON when a numeric value is in play; to get unlimited, omit the field / rely on the default. + ** Files - Modify: [[file:src/headless.rs][src/headless.rs]] @@ -822,8 +857,6 @@ Expected: tests pass or expose remaining direct references to =WebcamResolution= - [ ] Step 1: Write failing headless parsing and override tests. -Update =src/headless.rs= tests so command-line and participant JSON use the new names: - #+BEGIN_SRC rust #[test] fn cli_parsing_accepts_video_constraint_options() { @@ -836,18 +869,18 @@ fn cli_parsing_accepts_video_constraint_options() { "--video-max-concurrent-tracks", "2", "--participant", - r#"{"video_constraint_publish_webcam":"360p","video_constraint_subscribe":"none","video_max_concurrent_tracks":null}"#, + 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(Some(2))); + assert_eq!(args.video_max_concurrent_tracks, Some(2)); assert_eq!(args.participants.len(), 1); } #[test] -fn participant_json_can_override_video_constraints_to_unlimited() { +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, @@ -857,29 +890,45 @@ fn participant_json_can_override_video_constraints_to_unlimited() { let configs = build_participant_configs( global_config, - &[r#"{"video_constraint_publish_webcam":"360p","video_constraint_subscribe":"480p","video_max_concurrent_tracks":null}"#.to_string()], + &[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::P480); - assert_eq!(configs[0].video_max_concurrent_tracks, None); + 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)); } #+END_SRC - [ ] Step 2: Run the failing headless tests. -Run: - #+BEGIN_SRC bash -cargo test -p hyper-client-simulator headless::tests::cli_parsing_accepts_video_constraint_options headless::tests::participant_json_can_override_video_constraints_to_unlimited +cargo test -p hyper-client-simulator headless::tests::cli_parsing_accepts_video_constraint_options headless::tests::participant_json_overrides_video_constraints_and_treats_null_tracks_as_absent headless::tests::participant_json_numeric_tracks_override_global #+END_SRC -Expected: the command fails because =HeadlessArgs= and =ParticipantOverride= still use =resolution=. +Expected: fails because =HeadlessArgs= and =ParticipantOverride= still use =resolution=. - [ ] Step 3: Replace =HeadlessArgs= fields. -In =src/headless.rs=, replace: +Replace the import of =WebcamResolution= with =VideoConstraint=. Replace: #+BEGIN_SRC rust #[clap(long, value_name = "RESOLUTION")] @@ -895,68 +944,50 @@ 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", value_parser = parse_video_max_concurrent_tracks)] -pub video_max_concurrent_tracks: Option>, +#[clap(long = "video-max-concurrent-tracks", value_name = "TRACKS")] +pub video_max_concurrent_tracks: Option, #+END_SRC -Add this parser in =src/headless.rs=: - -#+BEGIN_SRC rust -fn parse_video_max_concurrent_tracks(raw: &str) -> Result, String> { - match raw { - "none" | "null" | "unlimited" => Ok(None), - _ => raw - .parse::() - .map(Some) - .map_err(|err| format!("expected unlimited/null/none or a non-negative integer: {err}")), - } -} -#+END_SRC +(No custom =value_parser=; a bare integer parses into =usize=. To run unlimited, omit the flag.) -- [ ] Step 4: Replace participant JSON override fields. +- [ ] Step 4: Replace participant JSON override fields and apply logic. In =ParticipantOverride=, replace =resolution= with: #+BEGIN_SRC rust video_constraint_publish_webcam: Option, video_constraint_subscribe: Option, -video_max_concurrent_tracks: Option>, +video_max_concurrent_tracks: Option, #+END_SRC -In =apply_cli_overrides= and =apply_participant_override=, set the three =Config= fields. Because =video_max_concurrent_tracks= is nullable, assign when the outer option is present: +In =apply_cli_overrides= and =apply_participant_override=, set the three =Config= fields with the same =if let Some(value)= pattern as the other fields (JSON =null= deserializes to =None= and is skipped): #+BEGIN_SRC rust -if let Some(value) = args.video_max_concurrent_tracks { - config.video_max_concurrent_tracks = value; +if let Some(value) = args.video_constraint_publish_webcam { + config.video_constraint_publish_webcam = value; } - -if let Some(value) = override_.video_max_concurrent_tracks { - config.video_max_concurrent_tracks = 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); } #+END_SRC -- [ ] Step 5: Remove old =resolution= headless support. - -Delete =resolution= from: +(and the analogous block reading from =override_= in =apply_participant_override=.) -- =HeadlessArgs= -- =ParticipantOverride= -- =apply_cli_overrides= -- =apply_participant_override= -- headless tests +- [ ] Step 5: Remove old =resolution= headless support. -Then run: +Delete =resolution= from =HeadlessArgs=, =ParticipantOverride=, =apply_cli_overrides=, =apply_participant_override=, and old tests. Then: #+BEGIN_SRC bash rg -n "\bresolution\b|WebcamResolution" src/headless.rs #+END_SRC -Expected: no matches in =src/headless.rs=. +Expected: no matches. - [ ] Step 6: Run headless tests. -Run: - #+BEGIN_SRC bash cargo test -p hyper-client-simulator headless #+END_SRC @@ -972,9 +1003,9 @@ Expected: all headless tests pass. ** Steps -- [ ] Step 1: Replace imports and component fields. +- [ ] Step 1: Replace imports and the start-screen field model. -In both files, replace =WebcamResolution= imports and =resolution_list= fields with: +In both files, replace the =WebcamResolution= import with: #+BEGIN_SRC rust use client_simulator_config::{ @@ -987,7 +1018,15 @@ use client_simulator_config::{ }; #+END_SRC -Use these component fields: +In =browser_start.rs=, replace the single =SelectedField::Resolution= variant with three variants in the same navigation position (between =Transport= and =BackgroundBlur=): + +#+BEGIN_SRC rust +VideoConstraintPublishWebcam, +VideoConstraintSubscribe, +VideoMaxConcurrentTracks, +#+END_SRC + +Update the exhaustive =MoveUp= / =MoveDown= match arms (the circular field order), the per-field render rows (two more =Constraint::Length(1)= rows and two more render blocks), and replace the =resolution_list= field with three list fields: #+BEGIN_SRC rust video_constraint_publish_webcam_list: Option>, @@ -997,7 +1036,7 @@ video_max_concurrent_tracks_list: Option " Select outgoing webcam max resolution. to select. ", @@ -1005,21 +1044,64 @@ SelectedField::VideoConstraintSubscribe => " Select incoming max resolution. " Select max concurrent webcam tracks. to select. ", #+END_SRC -When a preset is selected, assign: +The two constraint lists are seeded from =self.config.video_constraint_*= via =VideoConstraint::iter()=. The track-limit list is seeded from the preset, and on selection writes back through =to_option=: #+BEGIN_SRC rust +// open: +self.video_max_concurrent_tracks_list = Some(EnumListInput::new( + "Max concurrent webcam tracks", + VideoMaxConcurrentTracksPreset::iter(), + VideoMaxConcurrentTracksPreset::from_option(self.config.video_max_concurrent_tracks), +)); +// on finish: self.config.video_max_concurrent_tracks = value.to_option(); #+END_SRC -For the current value, derive the preset with: +Update the =Esc=-to-close and =draw= blocks to cover all three lists. + +- [ ] Step 3: Participants table — one combined column. + +Replace the single =Resolution= header/cell with ONE combined column (table stays at 12 columns). Header "Video constraints"; cell: + +#+BEGIN_SRC rust +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}"); +#+END_SRC + +Give the combined column more width than the old 8% (it holds ~22 chars at the extreme, e.g. =out:1080p in:1080p t:∞=) and rebalance the other percentage widths so they still sum to 100. Exact split at implementer discretion (e.g. bump this column to ~16–18% and shave a couple of points each from Name / Created / Noise Suppression / Blur). Truncation on an 80-col terminal is acceptable per the design decision. + +- [ ] Step 4: Participants table — per-participant edit via a chooser sub-menu. + +Keep the single =r= key. Pressing =r= opens a small chooser listing the three settings; selecting one opens that setting's value list; selecting a value sends the command. Add a chooser enum and the modal state: #+BEGIN_SRC rust -VideoMaxConcurrentTracksPreset::from_option(self.config.video_max_concurrent_tracks) +#[derive(Copy, Clone, Debug, PartialEq, Eq, strum::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, +} + +// component state: +video_setting_menu: Option>, +video_constraint_publish_webcam_list: Option>, +video_constraint_subscribe_list: Option>, +video_max_concurrent_tracks_list: Option>, #+END_SRC -- [ ] Step 3: Update participant table columns and live commands. +Flow: -In =participants.rs=, replace the single camera resolution list with three selection paths: +1. =r= → open =video_setting_menu= (preselect =PublishWebcam=). +2. Menu =Enter= → match the chosen =VideoSetting= and open the matching value list, seeded from =selected.state.borrow()= (=from_option= for the track preset). +3. Value-list =Enter= → send the command: #+BEGIN_SRC rust participant.set_video_constraint_publish_webcam(value); @@ -1027,118 +1109,150 @@ participant.set_video_constraint_subscribe(value); participant.set_video_max_concurrent_tracks(value.to_option()); #+END_SRC -Display the state values with: +4. =Esc= closes whichever modal is open; =draw= renders whichever modal is =Some=. -#+BEGIN_SRC rust -let publish_constraint = state.video_constraint_publish_webcam.to_string(); -let subscribe_constraint = state.video_constraint_subscribe.to_string(); -let max_tracks = state - .video_max_concurrent_tracks - .map(|value| value.to_string()) - .unwrap_or_else(|| "unlimited".to_string()); -#+END_SRC +Update the footer help text, replacing =esolutions= with a single entry such as = video constraints=. -- [ ] Step 4: Run TUI tests. - -Run: +- [ ] Step 5: Run TUI tests. #+BEGIN_SRC bash cargo test -p client-simulator-tui #+END_SRC -Expected: TUI tests pass and compile errors identify any remaining old field names. +Expected: TUI tests pass; compile errors flag any remaining old field names. -* Task 6: Update Cloudflare Worker Client Contract and Mappings +* Task 6: Update the Cloudflare Worker and Re-Vendor the Contract -** Files +Order matters: change the worker first, regenerate the OpenAPI from its Zod schemas, then re-vendor into the simulator. Do NOT hand-edit the simulator's vendored JSON — it is generated output. Wire casing is =1080p= throughout. -- Modify: [[file:cloudflare-worker-client/openapi/cloudflare-browser-simulator.json][cloudflare-worker-client/openapi/cloudflare-browser-simulator.json]] -- Modify: [[file:cloudflare-worker-client/src/client.rs][cloudflare-worker-client/src/client.rs]] -- Modify: [[file:browser/src/participant/cloudflare/mod.rs][browser/src/participant/cloudflare/mod.rs]] -- Modify: [[file:browser/tests/cloudflare_driver.rs][browser/tests/cloudflare_driver.rs]] +** Part A — Worker repo (=../cloudflare-browser-simulator=) -** Steps +*** Files -- [ ] Step 1: Update the OpenAPI schema. +- Modify: =worker/src/api/schemas.ts= +- Modify: =worker/src/frontend/core.ts= +- Modify: =worker/src/frontend/lite.ts= +- Modify: worker unit tests =worker/src/*.test.ts= +- Modify: =cli/src/commands/smoke_test.rs= +- Regenerate: =cli/openapi/cloudflare-browser-simulator.json=, =worker/worker-configuration.d.ts= -In =ParticipantSettings=, replace the =resolution= property with: +*** Steps -#+BEGIN_SRC json -"videoConstraintPublishWebcam": { - "type": "string", - "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] -}, -"videoConstraintSubscribe": { - "type": "string", - "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] -}, -"videoMaxConcurrentTracks": { - "type": ["number", "null"], - "minimum": 0 -} +- [ ] Step 1: Update Zod schemas (=worker/src/api/schemas.ts=). + +Replace =WebcamResolutionSchema= with: + +#+BEGIN_SRC typescript +export const VideoConstraintSchema = z.enum([ + "none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p", +]); #+END_SRC -Replace =resolution= in =required= with the three new property names. +In =ParticipantSettingsSchema=, replace =resolution: WebcamResolutionSchema= with: -In =SessionCommandRequest=, replace the =set-webcam-resolution= variant with these three variants: +#+BEGIN_SRC typescript +videoConstraintPublishWebcam: VideoConstraintSchema, +videoConstraintSubscribe: VideoConstraintSchema, +videoMaxConcurrentTracks: z.number().int().min(0).nullable(), +#+END_SRC -#+BEGIN_SRC json -{ - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["set-video-constraint-publish-webcam"] }, - "videoConstraintPublishWebcam": { - "type": "string", - "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] - } - }, - "required": ["type", "videoConstraintPublishWebcam"] +In =ParticipantStateSchema=, replace =webcamResolution: WebcamResolutionSchema= with the same three fields. + +Replace =SetWebcamResolutionCommandSchema= with three command schemas, and add them to the command discriminated union (and remove the old one): + +#+BEGIN_SRC typescript +const SetVideoConstraintPublishWebcamCommandSchema = z.object({ + type: z.literal("set-video-constraint-publish-webcam"), + videoConstraintPublishWebcam: VideoConstraintSchema, +}); +const SetVideoConstraintSubscribeCommandSchema = z.object({ + type: z.literal("set-video-constraint-subscribe"), + videoConstraintSubscribe: VideoConstraintSchema, +}); +const SetVideoMaxConcurrentTracksCommandSchema = z.object({ + type: z.literal("set-video-max-concurrent-tracks"), + videoMaxConcurrentTracks: z.number().int().min(0).nullable(), +}); +#+END_SRC + +- [ ] Step 2: Update Core controls and state (=worker/src/frontend/core.ts=). + +In =CoreControls=, replace =setWebcamResolution= with three methods. Implement them against =hyper.settings.media= (NOT the removed =videoCodec= API): + +#+BEGIN_SRC typescript +async setVideoConstraintPublishWebcam(value) { + await page.evaluate((next) => { + const hyper = (globalThis as typeof globalThis & { hyper?: any }).hyper; + hyper?.settings?.media?.actions?.setVideoConstraintPublishWebcam?.(next); + }, value); }, -{ - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["set-video-constraint-subscribe"] }, - "videoConstraintSubscribe": { - "type": "string", - "enum": ["none", "90p", "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p"] - } - }, - "required": ["type", "videoConstraintSubscribe"] +async setVideoConstraintSubscribe(value) { + await page.evaluate((next) => { + const hyper = (globalThis as typeof globalThis & { hyper?: any }).hyper; + hyper?.settings?.media?.actions?.setVideoConstraintSubscribe?.(next); + }, value); +}, +async setVideoMaxConcurrentTracks(value) { + await page.evaluate((next) => { + const hyper = (globalThis as typeof globalThis & { hyper?: any }).hyper; + hyper?.settings?.media?.actions?.setVideoMaxConcurrentTracks?.(next); + }, value); }, -{ - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["set-video-max-concurrent-tracks"] }, - "videoMaxConcurrentTracks": { - "type": ["number", "null"], - "minimum": 0 - } - }, - "required": ["type", "videoMaxConcurrentTracks"] -} #+END_SRC -If the deployed worker uses different command names, update these strings to match the worker source before running tests. Do not keep =set-webcam-resolution= in the new simulator contract. +Update the command switch (three cases), the pre-join settings apply (three calls from =request.settings=), the state readback (read =hyper.settings.media.videoConstraintPublishWebcam= / =videoConstraintSubscribe= as bare strings and =videoMaxConcurrentTracks= as a number|null), and =buildParticipantState= defaults (=none= / =none= / =null=). + +- [ ] Step 3: Update Lite (=worker/src/frontend/lite.ts=). -- [ ] Step 2: Regenerate and compile the worker client. +Replace the =setWebcamResolution= stub with three no-op stubs; update the unsupported log message, the command cases, and =buildLiteParticipantState= defaults (=videoConstraintPublishWebcam: "none"=, =videoConstraintSubscribe: "none"=, =videoMaxConcurrentTracks: null=). -Run: +- [ ] Step 4: Update worker unit tests. + +Update =worker/src/*.test.ts= fixtures/expectations that reference =resolution=, =webcamResolution=, =set-webcam-resolution=, or the =p1080= casing. + +- [ ] Step 5: Regenerate the OpenAPI and worker types, fix the worker CLI. + +From the worker repo: #+BEGIN_SRC bash -cargo check -p cloudflare-worker-client +just export-openapi +just generate-worker-types +#+END_SRC + +Update =cli/src/commands/smoke_test.rs= fixtures so the generated client compiles, then: + +#+BEGIN_SRC bash +just check-generated #+END_SRC -Expected: generated types include =ParticipantSettingsVideoConstraintPublishWebcam=, =ParticipantSettingsVideoConstraintSubscribe=, and command variants for all three settings. Compile errors in tests still reference =ParticipantSettingsResolution=. +Expected: =check-generated= passes (OpenAPI + worker types regenerated, =cargo check= clean, no stray git diff). -- [ ] Step 3: Update cloudflare-worker-client tests. +- [ ] Step 6: Deploy is a MANUAL, maintainer-approved step (see Task 7). Do not run =just deploy-worker= as part of automated execution. -In =cloudflare-worker-client/src/client.rs=, replace imports: +** Part B — Simulator repo -#+BEGIN_SRC rust -ParticipantSettingsResolution, +*** Files + +- Modify: [[file:cloudflare-worker-client/openapi/cloudflare-browser-simulator.json][cloudflare-worker-client/openapi/cloudflare-browser-simulator.json]] (re-vendor) +- Modify: [[file:cloudflare-worker-client/src/client.rs][cloudflare-worker-client/src/client.rs]] +- Modify: [[file:browser/src/participant/cloudflare/mod.rs][browser/src/participant/cloudflare/mod.rs]] +- Modify: [[file:browser/tests/cloudflare_driver.rs][browser/tests/cloudflare_driver.rs]] + +*** Steps + +- [ ] Step 1: Re-vendor the regenerated OpenAPI and regenerate the progenitor client. + +#+BEGIN_SRC bash +cp ../cloudflare-browser-simulator/cli/openapi/cloudflare-browser-simulator.json \ + cloudflare-worker-client/openapi/cloudflare-browser-simulator.json +cargo check -p cloudflare-worker-client #+END_SRC -with the generated video constraint enum names from the compile output, then update =create_session_request=: +This moves the vendored copy from =0.3.0= to the current worker version. Expect the full re-vendor to surface unrelated =0.3.0→0.4.x= schema drift — be prepared for additional progenitor type renames that ripple into =client.rs= and =cloudflare/mod.rs=. Note the generated type names (progenitor): expect =ParticipantSettingsVideoConstraintPublishWebcam=, =ParticipantSettingsVideoConstraintSubscribe=, and =SessionCommandRequest::SetVideoConstraintPublishWebcam {{ .. }}= variants (confirm exact names from the compile output). + +- [ ] Step 2: Update =cloudflare-worker-client/src/client.rs=. + +Replace the =ParticipantSettingsResolution= import/usage. Update =create_session_request=: #+BEGIN_SRC rust settings: ParticipantSettings { @@ -1155,33 +1269,11 @@ settings: ParticipantSettings { }, #+END_SRC -Update schema-mismatch fixture JSON to contain the three new state fields if =ParticipantState= is changed in the worker schema. +Update any schema-mismatch fixture JSON to carry the three new =ParticipantState= fields. -- [ ] Step 4: Update browser Cloudflare mappings. +- [ ] Step 3: Update =browser/src/participant/cloudflare/mod.rs=. -In =browser/src/participant/cloudflare/mod.rs=, replace =map_command_webcam_resolution= with: - -#+BEGIN_SRC rust -fn map_video_constraint(value: client_simulator_config::VideoConstraint) -> types::ParticipantSettingsVideoConstraintPublishWebcam { - match value { - client_simulator_config::VideoConstraint::None => types::ParticipantSettingsVideoConstraintPublishWebcam::None, - client_simulator_config::VideoConstraint::P90 => types::ParticipantSettingsVideoConstraintPublishWebcam::P90, - client_simulator_config::VideoConstraint::P144 => types::ParticipantSettingsVideoConstraintPublishWebcam::P144, - client_simulator_config::VideoConstraint::P240 => types::ParticipantSettingsVideoConstraintPublishWebcam::P240, - client_simulator_config::VideoConstraint::P360 => types::ParticipantSettingsVideoConstraintPublishWebcam::P360, - client_simulator_config::VideoConstraint::P480 => types::ParticipantSettingsVideoConstraintPublishWebcam::P480, - client_simulator_config::VideoConstraint::P720 => types::ParticipantSettingsVideoConstraintPublishWebcam::P720, - client_simulator_config::VideoConstraint::P1080 => types::ParticipantSettingsVideoConstraintPublishWebcam::P1080, - client_simulator_config::VideoConstraint::P1440 => types::ParticipantSettingsVideoConstraintPublishWebcam::P1440, - client_simulator_config::VideoConstraint::P2160 => types::ParticipantSettingsVideoConstraintPublishWebcam::P2160, - } -} -#+END_SRC - -If =progenitor= generates separate enum types for publish and subscribe settings, use two mapping functions with identical match arms: - -- =map_video_constraint_publish_webcam= returns =types::ParticipantSettingsVideoConstraintPublishWebcam=. -- =map_video_constraint_subscribe= returns =types::ParticipantSettingsVideoConstraintSubscribe=. +Replace =map_command_webcam_resolution= / the settings match with mapping function(s) over =VideoConstraint=. If progenitor emits distinct publish/subscribe enum types, use two functions with identical arms (=map_video_constraint_publish_webcam=, =map_video_constraint_subscribe=). Each arm maps =VideoConstraint::P1080 => ...::P1080=, etc. Update =map_settings=: @@ -1191,7 +1283,9 @@ video_constraint_subscribe: map_video_constraint_subscribe(settings.video_constr video_max_concurrent_tracks: settings.video_max_concurrent_tracks.map(|value| value as f64), #+END_SRC -Update =command_request= for the three new message variants. Use the generated variant names from =cargo check=, for example: +(=video_max_concurrent_tracks= maps to whatever numeric type progenitor generates — likely =Option=; cast accordingly.) + +Update the state mapping (worker → =ParticipantState=) for the three new fields, and =command_request= for the three new message variants: #+BEGIN_SRC rust ParticipantMessage::SetVideoConstraintPublishWebcam(value) => { @@ -1211,15 +1305,9 @@ ParticipantMessage::SetVideoMaxConcurrentTracks(value) => { } #+END_SRC -- [ ] Step 5: Update Cloudflare tests and fixtures. +- [ ] Step 4: Update Cloudflare tests and fixtures (=browser/tests/cloudflare_driver.rs= and the in-module tests in =cloudflare/mod.rs=). -Replace expected JSON: - -#+BEGIN_SRC json -{ "type": "set-webcam-resolution", "webcamResolution": "p1080" } -#+END_SRC - -with three separate command expectations: +Replace the old expected command JSON with three: #+BEGIN_SRC rust json!({ "type": "set-video-constraint-publish-webcam", "videoConstraintPublishWebcam": "1080p" }) @@ -1227,7 +1315,7 @@ json!({ "type": "set-video-constraint-subscribe", "videoConstraintSubscribe": "7 json!({ "type": "set-video-max-concurrent-tracks", "videoMaxConcurrentTracks": 2 }) #+END_SRC -Update worker state fixtures that currently use =webcamResolution= so they use: +Update worker state fixtures that used =webcamResolution= so they use: #+BEGIN_SRC json "videoConstraintPublishWebcam": "720p", @@ -1235,18 +1323,16 @@ Update worker state fixtures that currently use =webcamResolution= so they use: "videoMaxConcurrentTracks": null #+END_SRC -- [ ] Step 6: Run Cloudflare tests. - -Run: +- [ ] Step 5: Run Cloudflare tests. #+BEGIN_SRC bash cargo test -p cloudflare-worker-client cargo test -p client-simulator-browser cloudflare #+END_SRC -Expected: both commands pass. +Expected: both pass. -* Task 7: Remove Legacy Resolution References and Run Full Verification +* Task 7: Remove Legacy References and Run Full Verification ** Files @@ -1254,40 +1340,32 @@ Expected: both commands pass. ** Steps -- [ ] Step 1: Search for legacy runtime API references. - -Run: +- [ ] Step 1: Search for legacy runtime API references (both repos). #+BEGIN_SRC bash -rg -n "videoResolutionForWebcamEncoder|setVideoResolutionForWebcamEncoder|videoResolutionWebcamMedia|setVideoResolutionWebcamMedia" . +rg -n "videoResolutionForWebcamEncoder|setVideoResolutionForWebcamEncoder" . ../cloudflare-browser-simulator #+END_SRC -Expected: no matches in simulator source. Matches in documentation are acceptable only if they explicitly describe removed legacy behavior. +Expected: no matches in active source (simulator or worker). Doc references are acceptable only if they explicitly describe removed legacy behavior. - [ ] Step 2: Search for old simulator setting names. -Run: - #+BEGIN_SRC bash rg -n "\bresolution\b|WebcamResolution|webcam_resolution|SetWebcamResolutions|set_webcam_resolution|webcamResolution|set-webcam-resolution" config/src browser/src tui/src src cloudflare-worker-client browser/tests #+END_SRC -Expected: no active simulator code uses old webcam resolution names. Remaining matches must be deliberate comments in this plan or compatibility notes outside active code. - -- [ ] Step 3: Run formatting and linting. +Expected: no active simulator code uses old webcam resolution names. -Run: +- [ ] Step 3: Format and lint (simulator). #+BEGIN_SRC bash just fmt just clippy #+END_SRC -Expected: both commands exit 0. - -- [ ] Step 4: Run the full test suite. +Expected: both exit 0. -Run: +- [ ] Step 4: Run the full simulator test suite. #+BEGIN_SRC bash just test @@ -1295,9 +1373,17 @@ just test Expected: all tests pass. -- [ ] Step 5: Run a live local-backend smoke test against latest.dev. +- [ ] Step 5: MANUAL — deploy the worker and confirm the deployed SDK. -Run: +This is the outward-facing, maintainer-approved step. From the worker repo: + +#+BEGIN_SRC bash +just deploy-worker +#+END_SRC + +Confirm =latest.dev.hyper.video= runs a Hyper build exposing =hyper.settings.media.actions.setVideoConstraintPublishWebcam= (and subscribe / max-tracks). If the deployed SDK is behind, the live runs below will fail regardless of code correctness. + +- [ ] Step 6: Live local-backend smoke test against latest.dev. #+BEGIN_SRC bash cargo run -- headless \ @@ -1312,9 +1398,7 @@ Expected: - Participant reaches =Joined the space=. - The process stays running until Ctrl-C. -- [ ] Step 6: Run a nullable track-limit smoke test. - -Run: +- [ ] Step 7: Nullable track-limit smoke test. #+BEGIN_SRC bash cargo run -- headless \ @@ -1328,11 +1412,18 @@ Expected: - Live page has =window.hyper.settings.media.videoMaxConcurrentTracks === null=. - Ctrl-C performs graceful shutdown. +- [ ] Step 8: OPTIONAL — live cloudflare-backend smoke test. + +After the worker is deployed, optionally run one participant with =--participant '{"backend":"cloudflare", ...}'= against the deployed worker to confirm the three new commands round-trip end-to-end. + * Self-Review Checklist -- [ ] Every active simulator path that used =resolution= now has publish constraint, subscribe constraint, and max-track setting coverage. +- [ ] Every active simulator path that used =resolution= now has publish constraint, subscribe constraint, and max-track setting coverage (config, spec, state, messages, runtime, mod, core, lite, remote_stub, headless, TUI, cloudflare). - [ ] Local Hyper Core browser automation uses =hyper.settings.media=, not =hyper.settings.videoCodec=, for these settings. -- [ ] Headless CLI and participant JSON can set =video_max_concurrent_tracks= to both a number and =null=. -- [ ] TUI can inspect and change the three new values for running participants. -- [ ] Cloudflare worker schema and mappings no longer serialize =webcamResolution= or =set-webcam-resolution=. +- [ ] =WebcamResolution= is fully deleted from the simulator (config enum, exports, all usages). +- [ ] Headless CLI and participant JSON treat =null= / absent as "no override" (plain =Option=); a numeric value overrides. +- [ ] TUI start screen exposes three fields; the participants table shows one combined column and edits the three settings via the =r= chooser sub-menu. =Custom= is gone; presets are Unlimited/1/2/3/4. +- [ ] Worker Zod schemas, controls (=hyper.settings.media=), and lite stub updated; OpenAPI regenerated via =just export-openapi=; =just check-generated= clean. +- [ ] Simulator re-vendored the regenerated OpenAPI (=0.3.0= → current); progenitor client + mappings updated; wire casing is =1080p=. +- [ ] Worker deploy + live latest.dev validation performed manually by the maintainer. - [ ] Full verification ran with =just fmt=, =just clippy=, =just test=, and at least one live latest.dev smoke test. From f7166f3d28c291148325412f31c00736b14f09c0 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Wed, 3 Jun 2026 11:59:06 +0200 Subject: [PATCH 22/42] Add video constraint support Replace webcam resolution configuration with publish and subscribe video constraints plus nullable max concurrent track support across config, browser runtime, headless CLI, TUI, Cloudflare mappings, and generated client artifacts. --- Cargo.lock | 1 + browser/src/participant/cloudflare/mod.rs | 276 +++++++++++++----- browser/src/participant/frontend/commands.rs | 198 ++++++++++++- browser/src/participant/frontend/core.rs | 63 +++- browser/src/participant/frontend/lite.rs | 33 ++- browser/src/participant/mod.rs | 12 +- browser/src/participant/remote_stub.rs | 24 +- browser/src/participant/shared/messages.rs | 16 +- browser/src/participant/shared/runtime.rs | 4 +- browser/src/participant/shared/spec.rs | 20 +- browser/src/participant/shared/state.rs | 6 +- browser/tests/cloudflare_driver.rs | 36 ++- .../openapi/cloudflare-browser-simulator.json | 170 ++++++++--- cloudflare-worker-client/src/client.rs | 9 +- config/Cargo.toml | 3 + config/src/client_config.rs | 126 +++++++- config/src/default-config.yaml | 4 +- config/src/lib.rs | 55 +++- src/headless.rs | 93 +++++- tui/src/tui/components/browser_start.rs | 207 +++++++++++-- tui/src/tui/components/participants.rs | 185 +++++++++--- tui/src/tui/layout.rs | 6 +- 22 files changed, 1277 insertions(+), 270 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 208152b..7ea53c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -835,6 +835,7 @@ dependencies = [ "names", "reqwest", "serde", + "serde_json", "sha1 0.11.0", "strum 0.28.0", "temp-dir", 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/frontend/commands.rs b/browser/src/participant/frontend/commands.rs index 31f1fa8..b035c1b 100644 --- a/browser/src/participant/frontend/commands.rs +++ b/browser/src/participant/frontend/commands.rs @@ -1,7 +1,7 @@ use super::driver::BrowserDriver; use client_simulator_config::{ NoiseSuppression, - WebcamResolution, + VideoConstraint, }; use eyre::{ Context as _, @@ -15,8 +15,13 @@ 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 CAMERA_RES_GET: &str = "return hyper.settings.videoCodec.videoResolutionForWebcamEncoder.name;"; -const CAMERA_RES_SET: &str = "hyper.settings.videoCodec.actions.setVideoResolutionForWebcamEncoder(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]);"; @@ -63,15 +68,41 @@ pub(super) async fn set_background_blur(driver: &dyn BrowserDriver, value: bool) set_value(driver, BACKGROUND_BLUR_SET, value).await } -pub(super) async fn get_outgoing_camera_resolution(driver: &dyn BrowserDriver) -> Result { - get_string(driver, CAMERA_RES_GET) +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 WebcamResolution from string") + .parse::() + .context("failed to parse videoConstraintPublishWebcam from string") } -pub(super) async fn set_outgoing_camera_resolution(driver: &dyn BrowserDriver, value: &WebcamResolution) -> Result<()> { - set_value(driver, CAMERA_RES_SET, value.to_string()).await +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 { @@ -81,3 +112,152 @@ pub(super) async fn get_force_webrtc(driver: &dyn BrowserDriver) -> Result 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/frontend/core.rs b/browser/src/participant/frontend/core.rs index b4acff4..129490f 100644 --- a/browser/src/participant/frontend/core.rs +++ b/browser/src/participant/frontend/core.rs @@ -10,12 +10,16 @@ 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, }, driver::{ decode_test_state, @@ -28,7 +32,7 @@ use crate::auth::BorrowedCookie; use client_simulator_config::{ NoiseSuppression, TransportMode, - WebcamResolution, + VideoConstraint, }; use eyre::{ Context as _, @@ -156,9 +160,15 @@ impl ParticipantInner { set_background_blur(driver, settings.blur) .await .context("failed to set background blur")?; - set_outgoing_camera_resolution(driver, &settings.resolution) + set_video_constraint_publish_webcam(driver, settings.video_constraint_publish_webcam) .await - .context("failed to set outgoing camera resolution")?; + .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")?; @@ -226,14 +236,25 @@ impl ParticipantInner { .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.driver.as_ref(), &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<()> { @@ -311,8 +332,16 @@ impl ParticipantInner { } } - if let Ok(value) = get_outgoing_camera_resolution(driver).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(driver).await { @@ -342,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/lite.rs b/browser/src/participant/frontend/lite.rs index fbf44dd..b623575 100644 --- a/browser/src/participant/frontend/lite.rs +++ b/browser/src/participant/frontend/lite.rs @@ -16,7 +16,7 @@ use super::{ use client_simulator_config::{ NoiseSuppression, TransportMode, - WebcamResolution, + VideoConstraint, }; use eyre::{ Context as _, @@ -272,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(()) } @@ -352,7 +359,9 @@ impl ParticipantInnerLite { 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, @@ -447,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/mod.rs b/browser/src/participant/mod.rs index 14f7d86..8ce3f09 100644 --- a/browser/src/participant/mod.rs +++ b/browser/src/participant/mod.rs @@ -410,12 +410,16 @@ 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) { 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..58f418e 100644 --- a/browser/src/participant/shared/runtime.rs +++ b/browser/src/participant/shared/runtime.rs @@ -325,7 +325,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/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/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json b/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json index b52fe5a..f1875a1 100644 --- a/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json +++ b/cloudflare-worker-client/openapi/cloudflare-browser-simulator.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "cloudflare-browser-simulator", - "version": "0.3.0", + "version": "0.4.1", "description": "Cloudflare Worker that launches Browser Rendering sessions, joins Hyper rooms, and exposes its API contract from code." }, "components": { @@ -267,21 +267,41 @@ "webtransport" ] }, - "webcamResolution": { + "videoConstraintPublishWebcam": { "type": "string", "enum": [ - "auto", - "p144", - "p240", - "p360", - "p480", - "p720", - "p1080", - "p1440", - "p2160", - "p4320" + "none", + "90p", + "144p", + "240p", + "360p", + "480p", + "720p", + "1080p", + "1440p", + "2160p" + ] + }, + "videoConstraintSubscribe": { + "type": "string", + "enum": [ + "none", + "90p", + "144p", + "240p", + "360p", + "480p", + "720p", + "1080p", + "1440p", + "2160p" ] }, + "videoMaxConcurrentTracks": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, "backgroundBlur": { "type": "boolean" } @@ -295,7 +315,9 @@ "autoGainControl", "noiseSuppression", "transportMode", - "webcamResolution", + "videoConstraintPublishWebcam", + "videoConstraintSubscribe", + "videoMaxConcurrentTracks", "backgroundBlur" ] }, @@ -490,21 +512,41 @@ "webtransport" ] }, - "resolution": { + "videoConstraintPublishWebcam": { + "type": "string", + "enum": [ + "none", + "90p", + "144p", + "240p", + "360p", + "480p", + "720p", + "1080p", + "1440p", + "2160p" + ] + }, + "videoConstraintSubscribe": { "type": "string", "enum": [ - "auto", - "p144", - "p240", - "p360", - "p480", - "p720", - "p1080", - "p1440", - "p2160", - "p4320" + "none", + "90p", + "144p", + "240p", + "360p", + "480p", + "720p", + "1080p", + "1440p", + "2160p" ] }, + "videoMaxConcurrentTracks": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, "blur": { "type": "boolean", "description": "Whether background blur should be enabled." @@ -517,7 +559,9 @@ "autoGainControl", "noiseSuppression", "transport", - "resolution", + "videoConstraintPublishWebcam", + "videoConstraintSubscribe", + "videoMaxConcurrentTracks", "blur" ] }, @@ -766,28 +810,78 @@ "type": { "type": "string", "enum": [ - "set-webcam-resolution" + "set-video-constraint-publish-webcam" ] }, - "webcamResolution": { + "videoConstraintPublishWebcam": { "type": "string", "enum": [ - "auto", - "p144", - "p240", - "p360", - "p480", - "p720", - "p1080", - "p1440", - "p2160", - "p4320" + "none", + "90p", + "144p", + "240p", + "360p", + "480p", + "720p", + "1080p", + "1440p", + "2160p" ] } }, "required": [ "type", - "webcamResolution" + "videoConstraintPublishWebcam" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "set-video-constraint-subscribe" + ] + }, + "videoConstraintSubscribe": { + "type": "string", + "enum": [ + "none", + "90p", + "144p", + "240p", + "360p", + "480p", + "720p", + "1080p", + "1440p", + "2160p" + ] + } + }, + "required": [ + "type", + "videoConstraintSubscribe" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "set-video-max-concurrent-tracks" + ] + }, + "videoMaxConcurrentTracks": { + "type": "integer", + "nullable": true, + "minimum": 0 + } + }, + "required": [ + "type", + "videoMaxConcurrentTracks" ] }, { 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/client_config.rs b/config/src/client_config.rs index b9d672e..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)] @@ -109,9 +167,63 @@ impl ParticipantBackendKind { #[cfg(test)] mod tests { - use super::ParticipantBackendKind; + 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(); diff --git a/config/src/default-config.yaml b/config/src/default-config.yaml index 20df613..381dd79 100644 --- a/config/src/default-config.yaml +++ b/config/src/default-config.yaml @@ -48,5 +48,7 @@ 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/lib.rs b/config/src/lib.rs index b86312e..15a6a04 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -27,8 +27,10 @@ pub use client_config::{ ParticipantBackendKind, TransportMode, TransportModeIter, - WebcamResolution, - WebcamResolutionIter, + VideoConstraint, + VideoConstraintIter, + VideoMaxConcurrentTracksPreset, + VideoMaxConcurrentTracksPresetIter, }; pub use cloudflare_config::CloudflareConfig; use color_eyre::Result; @@ -78,7 +80,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, } @@ -178,7 +184,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()); @@ -415,4 +431,35 @@ device_farm: assert_eq!(config.device_farm.region, "us-west-2"); assert!(config.device_farm.debug); } + + #[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/src/headless.rs b/src/headless.rs index 6bef39d..af5a1eb 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -5,7 +5,7 @@ use client_simulator_config::{ ParticipantBackendKind, TransportMode, TuiArgs, - WebcamResolution, + VideoConstraint, }; use eyre::{ Context as _, @@ -51,8 +51,14 @@ pub struct HeadlessArgs { #[clap(long, value_name = "MODE")] pub transport: Option, - #[clap(long, value_name = "RESOLUTION")] - pub resolution: 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, @@ -73,7 +79,9 @@ struct ParticipantOverride { auto_gain_control: Option, noise_suppression: Option, transport: Option, - resolution: Option, + video_constraint_publish_webcam: Option, + video_constraint_subscribe: Option, + video_max_concurrent_tracks: Option, blur: Option, } @@ -121,8 +129,14 @@ fn apply_cli_overrides(config: &mut Config, args: &HeadlessArgs) { if let Some(transport) = args.transport { config.transport = transport; } - if let Some(resolution) = args.resolution { - config.resolution = resolution; + 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; @@ -157,8 +171,14 @@ fn apply_participant_override(mut config: Config, override_: ParticipantOverride if let Some(transport) = override_.transport { config.transport = transport; } - if let Some(resolution) = override_.resolution { - config.resolution = resolution; + 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; @@ -290,6 +310,7 @@ mod tests { use client_simulator_config::{ Config, ParticipantBackendKind, + VideoConstraint, }; use std::{ fs, @@ -340,6 +361,27 @@ mod tests { 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 { @@ -367,6 +409,41 @@ mod tests { 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()]) 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); From d65128999203938c026d424e3a9d21109befec69 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Wed, 3 Jun 2026 13:46:13 +0200 Subject: [PATCH 23/42] improved logging for headless mode --- config/src/args.rs | 7 --- src/headless.rs | 17 ++---- src/main.rs | 146 ++++++++++++++++++++++++++++++++++----------- tui/src/lib.rs | 5 +- tui/src/logging.rs | 29 ++++++--- 5 files changed, 140 insertions(+), 64 deletions(-) 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/src/headless.rs b/src/headless.rs index af5a1eb..8a0f466 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -85,8 +85,8 @@ struct ParticipantOverride { blur: Option, } -pub async fn run(args: HeadlessArgs, debug: u8) -> Result { - init_logging(debug)?; +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); @@ -201,23 +201,14 @@ fn build_participant_configs(global_config: Config, participants: &[String]) -> .collect() } -fn init_logging(debug: u8) -> Result<()> { - let filter = match debug { - 0 => "info", - 1 => "debug", - _ => "trace", - }; +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(EnvFilter::builder().parse_lossy(filter)), - ) + .with(fmt::layer().with_writer(writer).with_filter(filter)) .try_init()?; Ok(()) diff --git a/src/main.rs b/src/main.rs index 119edc7..3da495a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,17 +12,59 @@ 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, @@ -46,7 +88,8 @@ mod tests { fn parses_headless_command_with_repeated_participants() { let args = CliArgs::parse_from([ "hyper-client-simulator", - "--debug", + "--logging", + "debug", "headless", "--url", "https://latest.dev.hyper.video/F27-T5F-DXY", @@ -58,7 +101,7 @@ mod tests { match args.command { Some(Command::Headless(headless)) => { - assert_eq!(args.debug, 1); + 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") @@ -68,6 +111,52 @@ mod tests { 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")); + } } #[derive(clap::Args, Debug, Clone)] @@ -85,45 +174,32 @@ 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 - } - Some(Command::Tui(mut args)) => { - args.debug = debug; - start_tui(args).await + let args = TuiArgs::default(); + start_tui(args, logging_filter_from_env(logging)).await } + Some(Command::Tui(args)) => start_tui(args, logging_filter_from_env(logging)).await, Some(Command::Headless(args)) => { - let code = headless::run(args, debug).await?; + 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, } } -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, + } +} From e5e643344b45a627f3d810317b0be91e2fbfd0d0 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Wed, 3 Jun 2026 14:03:23 +0200 Subject: [PATCH 24/42] nix flake update (for recent chrome version) --- flake.lock | 8 ++++---- flake.nix | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) 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"; }; From 55258868759682ccd0c2b7a372cac9c1f8297e87 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Wed, 3 Jun 2026 14:05:47 +0200 Subject: [PATCH 25/42] Fix Chrome request extra info CDP parsing --- Cargo.lock | 2 - Cargo.toml | 3 + browser/src/participant/local/session.rs | 42 + vendor/chromiumoxide_cdp/Cargo.toml | 70 + vendor/chromiumoxide_cdp/LICENSE-APACHE | 201 + vendor/chromiumoxide_cdp/LICENSE-MIT | 21 + vendor/chromiumoxide_cdp/PATCHES.md | 5 + vendor/chromiumoxide_cdp/README.md | 136 + .../pdl/browser_protocol.pdl | 57 + vendor/chromiumoxide_cdp/pdl/js_protocol.pdl | 1843 + vendor/chromiumoxide_cdp/src/cdp.rs | 111038 +++++++++++++++ vendor/chromiumoxide_cdp/src/lib.rs | 168 + vendor/chromiumoxide_cdp/src/revision.rs | 17 + 13 files changed, 113601 insertions(+), 2 deletions(-) create mode 100644 vendor/chromiumoxide_cdp/Cargo.toml create mode 100644 vendor/chromiumoxide_cdp/LICENSE-APACHE create mode 100644 vendor/chromiumoxide_cdp/LICENSE-MIT create mode 100644 vendor/chromiumoxide_cdp/PATCHES.md create mode 100644 vendor/chromiumoxide_cdp/README.md create mode 100644 vendor/chromiumoxide_cdp/pdl/browser_protocol.pdl create mode 100644 vendor/chromiumoxide_cdp/pdl/js_protocol.pdl create mode 100644 vendor/chromiumoxide_cdp/src/cdp.rs create mode 100644 vendor/chromiumoxide_cdp/src/lib.rs create mode 100644 vendor/chromiumoxide_cdp/src/revision.rs diff --git a/Cargo.lock b/Cargo.lock index 7ea53c8..902988d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,8 +708,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", diff --git a/Cargo.toml b/Cargo.toml index 30cba67..3854a33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,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" diff --git a/browser/src/participant/local/session.rs b/browser/src/participant/local/session.rs index 3a2373f..85e0686 100644 --- a/browser/src/participant/local/session.rs +++ b/browser/src/participant/local/session.rs @@ -516,6 +516,48 @@ 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( 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 `