Skip to main content

playwright_rs/protocol/
cdp_session.rs

1// Copyright 2026 Paul Adamson
2// Licensed under the Apache License, Version 2.0
3//
4// CDPSession — Chrome DevTools Protocol session object
5//
6// Architecture Reference:
7// - Python: playwright-python/playwright/_impl/_cdp_session.py
8// - JavaScript: playwright/packages/playwright-core/src/client/cdpSession.ts
9// - Docs: https://playwright.dev/docs/api/class-cdpsession
10
11//! CDPSession — Chrome DevTools Protocol session
12//!
13//! Provides access to the Chrome DevTools Protocol for Chromium-based browsers.
14//! CDPSession is created via [`BrowserContext::new_cdp_session`](crate::protocol::BrowserContext::new_cdp_session).
15//!
16//! # Example
17//!
18//! ```ignore
19//! use playwright_rs::protocol::Playwright;
20//!
21//! #[tokio::main]
22//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
23//!     let playwright = Playwright::launch().await?;
24//!     let browser = playwright.chromium().launch().await?;
25//!     let context = browser.new_context().await?;
26//!     let page = context.new_page().await?;
27//!
28//!     // Create a CDP session for the page
29//!     let session = context.new_cdp_session(&page).await?;
30//!
31//!     // Send a CDP command
32//!     let result = session
33//!         .send("Runtime.evaluate", Some(serde_json::json!({ "expression": "1+1" })))
34//!         .await?;
35//!
36//!     println!("Result: {:?}", result);
37//!
38//!     session.detach().await?;
39//!     context.close().await?;
40//!     browser.close().await?;
41//!     Ok(())
42//! }
43//! ```
44//!
45//! See: <https://playwright.dev/docs/api/class-cdpsession>
46
47use crate::error::Result;
48use crate::server::channel::Channel;
49use crate::server::channel_owner::{
50    ChannelOwner, ChannelOwnerImpl, DisposeReason, ParentOrConnection,
51};
52use crate::server::connection::ConnectionLike;
53use serde_json::Value;
54use std::any::Any;
55use std::sync::Arc;
56
57/// A Chrome DevTools Protocol session for a page or browser context.
58///
59/// CDPSession is only available in Chromium-based browsers.
60///
61/// See: <https://playwright.dev/docs/api/class-cdpsession>
62#[derive(Clone)]
63pub struct CDPSession {
64    base: ChannelOwnerImpl,
65}
66
67impl CDPSession {
68    /// Creates a new CDPSession from protocol initialization.
69    ///
70    /// Called by the object factory when the server sends a `__create__` message.
71    pub fn new(
72        parent: ParentOrConnection,
73        type_name: String,
74        guid: Arc<str>,
75        initializer: Value,
76    ) -> Result<Self> {
77        Ok(Self {
78            base: ChannelOwnerImpl::new(parent, type_name, guid, initializer),
79        })
80    }
81
82    /// Send a CDP command and return the result.
83    ///
84    /// # Arguments
85    ///
86    /// * `method` - The CDP method name (e.g., `"Runtime.evaluate"`)
87    /// * `params` - Optional JSON parameters for the method
88    ///
89    /// # Errors
90    ///
91    /// Returns error if:
92    /// - The session has been detached
93    /// - The CDP method fails
94    /// - Communication with browser process fails
95    ///
96    /// See: <https://playwright.dev/docs/api/class-cdpsession#cdp-session-send>
97    pub async fn send(&self, method: &str, params: Option<Value>) -> Result<Value> {
98        let params = serde_json::json!({
99            "method": method,
100            "params": params.unwrap_or(serde_json::json!({})),
101        });
102        self.channel().send("send", params).await
103    }
104
105    /// Detach the CDP session from the target.
106    ///
107    /// After detaching, the session can no longer be used to send commands.
108    ///
109    /// # Errors
110    ///
111    /// Returns error if:
112    /// - The session has already been detached
113    /// - Communication with browser process fails
114    ///
115    /// See: <https://playwright.dev/docs/api/class-cdpsession#cdp-session-detach>
116    pub async fn detach(&self) -> Result<()> {
117        self.channel()
118            .send_no_result("detach", serde_json::json!({}))
119            .await
120    }
121}
122
123impl ChannelOwner for CDPSession {
124    fn guid(&self) -> &str {
125        self.base.guid()
126    }
127
128    fn type_name(&self) -> &str {
129        self.base.type_name()
130    }
131
132    fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
133        self.base.parent()
134    }
135
136    fn connection(&self) -> Arc<dyn ConnectionLike> {
137        self.base.connection()
138    }
139
140    fn initializer(&self) -> &Value {
141        self.base.initializer()
142    }
143
144    fn channel(&self) -> &Channel {
145        self.base.channel()
146    }
147
148    fn dispose(&self, reason: DisposeReason) {
149        self.base.dispose(reason)
150    }
151
152    fn adopt(&self, child: Arc<dyn ChannelOwner>) {
153        self.base.adopt(child)
154    }
155
156    fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
157        self.base.add_child(guid, child)
158    }
159
160    fn remove_child(&self, guid: &str) {
161        self.base.remove_child(guid)
162    }
163
164    fn on_event(&self, method: &str, params: Value) {
165        self.base.on_event(method, params)
166    }
167
168    fn was_collected(&self) -> bool {
169        self.base.was_collected()
170    }
171
172    fn as_any(&self) -> &dyn Any {
173        self
174    }
175}
176
177impl std::fmt::Debug for CDPSession {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        f.debug_struct("CDPSession")
180            .field("guid", &self.guid())
181            .finish()
182    }
183}