Skip to main content

allwright_plugin_sdk/
lib.rs

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}