1pub mod accessibility_yaml;
2use serde::{Deserialize, Serialize};
3
4pub const ALLWRIGHT_PLUGIN_API_VERSION: u32 = 1;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum SurfaceFamily {
8 Web,
9 Mobile,
10 Desktop,
11}
12
13#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
14#[serde(rename_all = "snake_case")]
15pub enum BrowserKind {
16 Chromium,
17 Firefox,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct SurfacePluginDescriptor {
22 pub id: &'static str,
23 pub family: SurfaceFamily,
24 pub version: &'static str,
25 pub description: &'static str,
26}
27
28pub trait SurfacePlugin: Send + Sync {
29 fn descriptor(&self) -> SurfacePluginDescriptor;
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
33pub struct ChromeLaunchInfo {
34 pub browser: String,
35 pub note: String,
36 pub cdp_websocket_url: String,
37 pub user_data_dir: String,
38 pub process_id: u32,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
42pub struct BrowserLaunchInfo {
43 pub browser_kind: BrowserKind,
44 pub browser: String,
45 pub note: String,
46 pub user_data_dir: String,
47 pub process_id: u32,
48 pub browser_session: BrowserSessionHandle,
49 pub initial_page: PageInfo,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53pub struct ChromeTabInfo {
54 pub note: String,
55 pub target_id: String,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
59pub struct TabNavigationInfo {
60 pub url: String,
61 pub note: String,
62 pub page_session: PageSessionHandle,
63 pub automation: AutomationSessionInfo,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
67pub struct ChromiumBidiMapperInfo {
68 pub package_version: String,
69 pub mapper_target_id: String,
70 pub mapper_session_id: String,
71 pub note: String,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
75#[serde(tag = "kind", rename_all = "snake_case")]
76pub enum BrowserSessionHandle {
77 Chromium {
78 cdp_websocket_url: String,
79 },
80 Firefox {
81 connection_id: String,
82 bidi_session_id: String,
83 },
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
87#[serde(tag = "kind", rename_all = "snake_case")]
88pub enum PageSessionHandle {
89 Chromium {
90 target_id: String,
91 browsing_context_id: Option<String>,
92 #[serde(default)]
93 mapper_target_id: Option<String>,
94 },
95 Firefox {
96 browsing_context_id: String,
97 },
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
101pub struct PageInfo {
102 pub note: String,
103 pub page_session: PageSessionHandle,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
107pub enum HookType {
108 NewPage,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
112pub struct HookRegistration {
113 pub opaque_state: String,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
117#[serde(tag = "hook_type", content = "value", rename_all = "snake_case")]
118pub enum HookResult {
119 NewPage(PageInfo),
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
123pub struct AutomationSessionInfo {
124 pub bidi_session_id: String,
125 pub note: String,
126 pub mapper_target_id: Option<String>,
127 pub mapper_session_id: Option<String>,
128 pub package_version: Option<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
132pub struct ClickInfo {
133 pub css_selector: String,
134 pub note: String,
135 pub bidi_session_id: String,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
139pub struct ElementCountInfo {
140 pub css_selector: String,
141 pub count: u32,
142 pub note: String,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
146pub struct HighlightElementsInfo {
147 pub css_selector: String,
148 pub count: u32,
149 pub note: String,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
153pub struct FocusInfo {
154 pub css_selector: String,
155 pub note: String,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
159pub struct FillInfo {
160 pub css_selector: String,
161 pub value: String,
162 pub note: String,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
166pub struct HoverInfo {
167 pub css_selector: String,
168 pub note: String,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
172pub struct PressKeyInfo {
173 pub css_selector: String,
174 pub key: String,
175 pub note: String,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
179pub struct TextInfo {
180 pub css_selector: String,
181 pub text: String,
182 pub note: String,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
186pub struct WaitForSelectorInfo {
187 pub css_selector: String,
188 pub visible: bool,
189 pub note: String,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193pub struct AccessibilitySnapshotInfo {
194 pub snapshot: String,
195 pub format: String,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
199pub struct ScreenshotInfo {
200 pub png_data: Vec<u8>,
201 pub note: String,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
205#[serde(tag = "command", rename_all = "snake_case")]
206pub enum PluginCommand {
207 LaunchBrowser {
208 browser_kind: BrowserKind,
209 browser_binary: Option<String>,
210 },
211 OpenPage {
212 browser_session: BrowserSessionHandle,
213 },
214 RegisterHook {
215 browser_session: BrowserSessionHandle,
216 page_session: PageSessionHandle,
217 hook_type: HookType,
218 },
219 PollHook {
220 browser_session: BrowserSessionHandle,
221 registration: HookRegistration,
222 },
223 ClosePage {
224 browser_session: BrowserSessionHandle,
225 page_session: PageSessionHandle,
226 },
227 NavigatePage {
228 browser_session: BrowserSessionHandle,
229 page_session: PageSessionHandle,
230 url: String,
231 },
232 ClickElement {
233 browser_session: BrowserSessionHandle,
234 page_session: PageSessionHandle,
235 css_selector: String,
236 },
237 CountElements {
238 browser_session: BrowserSessionHandle,
239 page_session: PageSessionHandle,
240 css_selector: String,
241 },
242 HighlightElements {
243 browser_session: BrowserSessionHandle,
244 page_session: PageSessionHandle,
245 css_selector: String,
246 duration_ms: u64,
247 },
248 FocusElement {
249 browser_session: BrowserSessionHandle,
250 page_session: PageSessionHandle,
251 css_selector: String,
252 },
253 FillElement {
254 browser_session: BrowserSessionHandle,
255 page_session: PageSessionHandle,
256 css_selector: String,
257 value: String,
258 },
259 HoverElement {
260 browser_session: BrowserSessionHandle,
261 page_session: PageSessionHandle,
262 css_selector: String,
263 },
264 PressKey {
265 browser_session: BrowserSessionHandle,
266 page_session: PageSessionHandle,
267 css_selector: String,
268 key: String,
269 text: Option<String>,
270 },
271 GetTextContent {
272 browser_session: BrowserSessionHandle,
273 page_session: PageSessionHandle,
274 css_selector: String,
275 },
276 GetInnerText {
277 browser_session: BrowserSessionHandle,
278 page_session: PageSessionHandle,
279 css_selector: String,
280 },
281 WaitForSelector {
282 browser_session: BrowserSessionHandle,
283 page_session: PageSessionHandle,
284 css_selector: String,
285 visible: bool,
286 },
287 AccessibilitySnapshot {
288 browser_session: BrowserSessionHandle,
289 page_session: PageSessionHandle,
290 format: String,
291 #[serde(default)]
292 mode: String,
293 },
294 Screenshot {
295 browser_session: BrowserSessionHandle,
296 page_session: PageSessionHandle,
297 full_page: bool,
298 },
299 OpenChromeWindow {
300 chrome_binary: Option<String>,
301 },
302 DiscoverInitialTab {
303 cdp_websocket_url: String,
304 },
305 OpenChromeTab {
306 cdp_websocket_url: String,
307 },
308 CloseBrowserProcess {
309 process_id: u32,
310 },
311 CloseChromeTab {
312 cdp_websocket_url: String,
313 target_id: String,
314 },
315 NavigateChromeTab {
316 cdp_websocket_url: String,
317 target_id: String,
318 url: String,
319 },
320 InjectChromiumBidiMapper {
321 cdp_websocket_url: String,
322 },
323 ResolveBidiContextForTab {
324 cdp_websocket_url: String,
325 mapper_target_id: Option<String>,
326 browsing_context_id: Option<String>,
327 url: Option<String>,
328 },
329 ClickElementViaCdp {
330 cdp_websocket_url: String,
331 target_id: String,
332 css_selector: String,
333 },
334 CountElementsViaCdp {
335 cdp_websocket_url: String,
336 target_id: String,
337 css_selector: String,
338 },
339 HighlightElementsViaCdp {
340 cdp_websocket_url: String,
341 target_id: String,
342 css_selector: String,
343 duration_ms: u64,
344 },
345 FocusElementViaCdp {
346 cdp_websocket_url: String,
347 target_id: String,
348 css_selector: String,
349 },
350 FillElementViaCdp {
351 cdp_websocket_url: String,
352 target_id: String,
353 css_selector: String,
354 value: String,
355 },
356 HoverElementViaCdp {
357 cdp_websocket_url: String,
358 target_id: String,
359 css_selector: String,
360 },
361 PressKeyViaCdp {
362 cdp_websocket_url: String,
363 target_id: String,
364 css_selector: String,
365 key: String,
366 text: Option<String>,
367 },
368 GetTextContentViaCdp {
369 cdp_websocket_url: String,
370 target_id: String,
371 css_selector: String,
372 },
373 GetInnerTextViaCdp {
374 cdp_websocket_url: String,
375 target_id: String,
376 css_selector: String,
377 },
378 WaitForSelectorViaCdp {
379 cdp_websocket_url: String,
380 target_id: String,
381 css_selector: String,
382 visible: bool,
383 },
384 ScreenshotViaCdp {
385 cdp_websocket_url: String,
386 target_id: String,
387 },
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
391#[serde(tag = "result", rename_all = "snake_case")]
392pub enum PluginResult {
393 LaunchBrowser(BrowserLaunchInfo),
394 OpenPage(PageInfo),
395 RegisterHook(HookRegistration),
396 PollHook(HookResult),
397 ClosePage,
398 NavigatePage(TabNavigationInfo),
399 ClickElement(ClickInfo),
400 CountElements(ElementCountInfo),
401 HighlightElements(HighlightElementsInfo),
402 FocusElement(FocusInfo),
403 FillElement(FillInfo),
404 HoverElement(HoverInfo),
405 PressKey(PressKeyInfo),
406 GetTextContent(TextInfo),
407 GetInnerText(TextInfo),
408 WaitForSelector(WaitForSelectorInfo),
409 AccessibilitySnapshot(AccessibilitySnapshotInfo),
410 Screenshot(ScreenshotInfo),
411 OpenChromeWindow(ChromeLaunchInfo),
412 DiscoverInitialTab(ChromeTabInfo),
413 OpenChromeTab(ChromeTabInfo),
414 CloseBrowserProcess,
415 CloseChromeTab,
416 NavigateChromeTab(TabNavigationInfo),
417 InjectChromiumBidiMapper(ChromiumBidiMapperInfo),
418 ResolveBidiContextForTab {
419 browsing_context_id: String,
420 mapper: ChromiumBidiMapperInfo,
421 },
422 ClickElementViaCdp(ClickInfo),
423 CountElementsViaCdp(ElementCountInfo),
424 HighlightElementsViaCdp(HighlightElementsInfo),
425 FocusElementViaCdp(FocusInfo),
426 FillElementViaCdp(FillInfo),
427 HoverElementViaCdp(HoverInfo),
428 PressKeyViaCdp(PressKeyInfo),
429 GetTextContentViaCdp(TextInfo),
430 GetInnerTextViaCdp(TextInfo),
431 WaitForSelectorViaCdp(WaitForSelectorInfo),
432 ScreenshotViaCdp(ScreenshotInfo),
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
436pub struct PluginEnvelope {
437 pub ok: bool,
438 pub result: Option<PluginResult>,
439 pub error: Option<String>,
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn poll_hook_envelope_round_trips_without_duplicate_result_fields() {
448 let envelope = PluginEnvelope {
449 ok: true,
450 result: Some(PluginResult::PollHook(HookResult::NewPage(PageInfo {
451 note: "opened".to_string(),
452 page_session: PageSessionHandle::Chromium {
453 target_id: "target-2".to_string(),
454 browsing_context_id: None,
455 mapper_target_id: None,
456 },
457 }))),
458 error: None,
459 };
460
461 let json = serde_json::to_string(&envelope).unwrap();
462 assert_eq!(
463 serde_json::from_str::<PluginEnvelope>(&json).unwrap(),
464 envelope
465 );
466 }
467}