Skip to main content

cbf_chrome/
event.rs

1//! Chrome-specific event types and conversion utilities.
2//!
3//! This module defines [`ChromeEvent`], an event type that carries
4//! Chrome-specific vocabulary and serves as the primary event stream payload
5//! of the Chrome backend. The underlying FFI bindings live in `cbf-chrome-sys`;
6//! the types here are safe Rust abstractions on top of them.
7//!
8//! Two conversion functions translate Chrome-specific events into
9//! browser-generic ones where a mapping exists:
10//!
11//! - [`to_generic_event`] — converts a [`ChromeEvent`] into a
12//!   [`cbf::event::BrowserEvent`].
13//! - [`map_ipc_event_to_generic`] — converts a [`crate::ffi::IpcEvent`]
14//!   received over the IPC bridge into a [`cbf::event::BrowserEvent`].
15//!
16//! Not every Chrome-specific event has a generic counterpart; those return
17//! `None` and are intended to be consumed only by Chrome-aware code.
18
19use cbf::data::{
20    auxiliary_window::{
21        AuxiliaryWindowCloseReason, AuxiliaryWindowId, AuxiliaryWindowKind,
22        AuxiliaryWindowResolution, PermissionPromptResult, PermissionPromptType,
23    },
24    dialog::DialogType,
25    download::DownloadPromptResult,
26    extension::{ExtensionInstallPromptResult, ExtensionUninstallPromptResult},
27    ids::WindowId,
28    transient_browsing_context::{
29        TransientBrowsingContextCloseReason, TransientBrowsingContextKind,
30    },
31    window_open::{
32        WindowBounds, WindowDescriptor, WindowKind, WindowOpenReason, WindowOpenRequest,
33        WindowOpenResult, WindowState,
34    },
35};
36use cbf::event::{BrowserEvent, BrowsingContextEvent, TransientBrowsingContextEvent};
37
38use crate::data::{
39    browsing_context_open::ChromeBrowsingContextOpenResult,
40    download::ChromeDownloadPromptResult,
41    extension::ChromeExtensionInfo,
42    ids::PopupId,
43    lifecycle::{ChromeBackendErrorInfo, ChromeBackendStopReason},
44    profile::ChromeProfileInfo,
45    prompt_ui::{
46        PromptUiCloseReason, PromptUiDialogResult, PromptUiExtensionInstallResult,
47        PromptUiExtensionUninstallResult, PromptUiId, PromptUiKind, PromptUiPermissionType,
48        PromptUiResolution, PromptUiResolutionResult,
49    },
50    tab_open::{TabOpenHint, TabOpenResult},
51};
52use crate::ffi::IpcEvent;
53
54/// Chromium-specific raw event stream payload.
55#[derive(Debug, Clone)]
56pub enum ChromeEvent {
57    /// Raw IPC event from the bridge.
58    Ipc(Box<IpcEvent>),
59    /// Backend connected and ready.
60    BackendReady,
61    /// Backend stopped with a reason.
62    BackendStopped { reason: ChromeBackendStopReason },
63    /// Backend error surfaced from command/event processing.
64    BackendError {
65        info: ChromeBackendErrorInfo,
66        terminal_hint: bool,
67    },
68    /// Profile list obtained through backend-side request/response path.
69    ProfilesListed { profiles: Vec<ChromeProfileInfo> },
70}
71
72/// Maps Chromium raw events into browser-generic events when possible.
73///
74/// Some Chrome-specific events are implementation details that do not map to
75/// generic browser events. See each event variant's documentation for conversion
76/// behavior.
77pub fn to_generic_event(event: &ChromeEvent) -> Option<BrowserEvent> {
78    match event {
79        ChromeEvent::Ipc(raw) => map_ipc_event_to_generic(raw),
80        ChromeEvent::BackendReady => Some(BrowserEvent::BackendReady),
81        ChromeEvent::BackendStopped { reason } => Some(BrowserEvent::BackendStopped {
82            reason: reason.clone(),
83        }),
84        ChromeEvent::BackendError {
85            info,
86            terminal_hint,
87        } => Some(BrowserEvent::BackendError {
88            info: info.clone(),
89            terminal_hint: *terminal_hint,
90        }),
91        ChromeEvent::ProfilesListed { profiles } => Some(BrowserEvent::ProfilesListed {
92            profiles: profiles.iter().cloned().map(Into::into).collect(),
93        }),
94    }
95}
96
97/// Maps IPC events into browser-generic events when possible.
98///
99/// Some IPC events are Chrome-specific implementation details and do not map
100/// to generic browser events. See each [`IpcEvent`] variant's documentation for
101/// conversion behavior.
102pub fn map_ipc_event_to_generic(event: &IpcEvent) -> Option<BrowserEvent> {
103    match event {
104        IpcEvent::SurfaceHandleUpdated { .. } => None,
105        IpcEvent::ExtensionPopupOpened {
106            profile_id,
107            browsing_context_id,
108            popup_id,
109            title,
110            ..
111        } => Some(BrowserEvent::TransientBrowsingContext {
112            profile_id: profile_id.clone(),
113            transient_browsing_context_id: PopupId::new(*popup_id)
114                .to_transient_browsing_context_id(),
115            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
116            event: Box::new(TransientBrowsingContextEvent::Opened {
117                kind: TransientBrowsingContextKind::Popup,
118                title: Some(title.clone()),
119            }),
120        }),
121        IpcEvent::ExtensionPopupSurfaceHandleUpdated { .. } => None,
122        IpcEvent::ExtensionPopupPreferredSizeChanged {
123            profile_id,
124            browsing_context_id,
125            popup_id,
126            width,
127            height,
128        } => Some(BrowserEvent::TransientBrowsingContext {
129            profile_id: profile_id.clone(),
130            transient_browsing_context_id: PopupId::new(*popup_id)
131                .to_transient_browsing_context_id(),
132            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
133            event: Box::new(TransientBrowsingContextEvent::Resized {
134                width: *width,
135                height: *height,
136            }),
137        }),
138        IpcEvent::ExtensionPopupContextMenuRequested {
139            profile_id,
140            browsing_context_id,
141            popup_id,
142            menu,
143        } => Some(BrowserEvent::TransientBrowsingContext {
144            profile_id: profile_id.clone(),
145            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
146            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
147            event: Box::new(TransientBrowsingContextEvent::ContextMenuRequested {
148                menu: menu.clone().into(),
149            }),
150        }),
151        IpcEvent::ExtensionPopupChoiceMenuRequested {
152            profile_id,
153            browsing_context_id,
154            popup_id,
155            request_id,
156            ..
157        } => Some(BrowserEvent::TransientBrowsingContext {
158            profile_id: profile_id.clone(),
159            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
160            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
161            event: Box::new(TransientBrowsingContextEvent::ChoiceMenuRequested {
162                request_id: *request_id,
163            }),
164        }),
165        IpcEvent::ExtensionPopupCursorChanged {
166            profile_id,
167            browsing_context_id,
168            popup_id,
169            cursor_type,
170        } => Some(BrowserEvent::TransientBrowsingContext {
171            profile_id: profile_id.clone(),
172            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
173            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
174            event: Box::new(TransientBrowsingContextEvent::CursorChanged {
175                cursor_type: *cursor_type,
176            }),
177        }),
178        IpcEvent::ExtensionPopupTitleUpdated {
179            profile_id,
180            browsing_context_id,
181            popup_id,
182            title,
183        } => Some(BrowserEvent::TransientBrowsingContext {
184            profile_id: profile_id.clone(),
185            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
186            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
187            event: Box::new(TransientBrowsingContextEvent::TitleUpdated {
188                title: title.clone(),
189            }),
190        }),
191        IpcEvent::ExtensionPopupJavaScriptDialogRequested {
192            profile_id,
193            browsing_context_id,
194            popup_id,
195            request_id,
196            r#type,
197            message,
198            default_prompt_text,
199            reason,
200        } => Some(BrowserEvent::TransientBrowsingContext {
201            profile_id: profile_id.clone(),
202            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
203            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
204            event: Box::new(TransientBrowsingContextEvent::JavaScriptDialogRequested {
205                request_id: *request_id,
206                message: message.clone(),
207                default_prompt_text: default_prompt_text.clone(),
208                r#type: *r#type,
209                beforeunload_reason: if *r#type == DialogType::BeforeUnload {
210                    Some((*reason).into())
211                } else {
212                    None
213                },
214            }),
215        }),
216        IpcEvent::ExtensionPopupCloseRequested {
217            profile_id,
218            browsing_context_id,
219            popup_id,
220        } => Some(BrowserEvent::TransientBrowsingContext {
221            profile_id: profile_id.clone(),
222            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
223            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
224            event: Box::new(TransientBrowsingContextEvent::CloseRequested),
225        }),
226        IpcEvent::ExtensionPopupRenderProcessGone {
227            profile_id,
228            browsing_context_id,
229            popup_id,
230            crashed,
231        } => Some(BrowserEvent::TransientBrowsingContext {
232            profile_id: profile_id.clone(),
233            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
234            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
235            event: Box::new(TransientBrowsingContextEvent::RenderProcessGone { crashed: *crashed }),
236        }),
237        IpcEvent::ExtensionPopupClosed {
238            profile_id,
239            browsing_context_id,
240            popup_id,
241        } => Some(BrowserEvent::TransientBrowsingContext {
242            profile_id: profile_id.clone(),
243            transient_browsing_context_id: PopupId::new(*popup_id)
244                .to_transient_browsing_context_id(),
245            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
246            event: Box::new(TransientBrowsingContextEvent::Closed {
247                reason: TransientBrowsingContextCloseReason::Unknown,
248            }),
249        }),
250        IpcEvent::TabCreated {
251            profile_id,
252            browsing_context_id,
253            request_id,
254        } => Some(BrowserEvent::BrowsingContext {
255            profile_id: profile_id.clone(),
256            browsing_context_id: browsing_context_id.to_browsing_context_id(),
257            event: Box::new(BrowsingContextEvent::Created {
258                request_id: *request_id,
259            }),
260        }),
261        IpcEvent::DevToolsOpened { .. } => None,
262        IpcEvent::ImeBoundsUpdated {
263            profile_id,
264            browsing_context_id,
265            update,
266        } => Some(BrowserEvent::BrowsingContext {
267            profile_id: profile_id.clone(),
268            browsing_context_id: browsing_context_id.to_browsing_context_id(),
269            event: Box::new(BrowsingContextEvent::ImeBoundsUpdated {
270                update: update.clone().into(),
271            }),
272        }),
273        IpcEvent::ExtensionPopupImeBoundsUpdated {
274            profile_id,
275            browsing_context_id,
276            popup_id,
277            update,
278        } => Some(BrowserEvent::TransientBrowsingContext {
279            profile_id: profile_id.clone(),
280            transient_browsing_context_id: popup_id.to_transient_browsing_context_id(),
281            parent_browsing_context_id: browsing_context_id.to_browsing_context_id(),
282            event: Box::new(TransientBrowsingContextEvent::ImeBoundsUpdated {
283                update: update.clone().into(),
284            }),
285        }),
286        IpcEvent::ContextMenuRequested {
287            profile_id,
288            browsing_context_id,
289            menu,
290        } => Some(BrowserEvent::BrowsingContext {
291            profile_id: profile_id.clone(),
292            browsing_context_id: browsing_context_id.to_browsing_context_id(),
293            event: Box::new(BrowsingContextEvent::ContextMenuRequested {
294                menu: menu.clone().into(),
295            }),
296        }),
297        IpcEvent::ChoiceMenuRequested {
298            profile_id,
299            browsing_context_id,
300            request_id,
301            ..
302        } => Some(BrowserEvent::BrowsingContext {
303            profile_id: profile_id.clone(),
304            browsing_context_id: browsing_context_id.to_browsing_context_id(),
305            event: Box::new(BrowsingContextEvent::ChoiceMenuRequested {
306                request_id: *request_id,
307            }),
308        }),
309        IpcEvent::TabOpenRequested {
310            profile_id,
311            request_id,
312            source_tab_id,
313            target_url,
314            open_hint,
315            user_gesture,
316        } => match open_hint {
317            TabOpenHint::NewWindow | TabOpenHint::Popup => {
318                Some(BrowserEvent::WindowOpenRequested {
319                    profile_id: profile_id.clone(),
320                    request: WindowOpenRequest {
321                        request_id: *request_id,
322                        reason: WindowOpenReason::Navigation,
323                        opener_window_id: None,
324                        opener_browsing_context_id: source_tab_id
325                            .map(|id| id.to_browsing_context_id()),
326                        target_url: Some(target_url.clone()),
327                        requested_kind: if matches!(open_hint, TabOpenHint::Popup) {
328                            WindowKind::Popup
329                        } else {
330                            WindowKind::Normal
331                        },
332                        user_gesture: *user_gesture,
333                    },
334                })
335            }
336            _ => Some(BrowserEvent::BrowsingContextOpenRequested {
337                profile_id: profile_id.clone(),
338                request_id: *request_id,
339                source_browsing_context_id: source_tab_id.map(|id| id.to_browsing_context_id()),
340                target_url: target_url.clone(),
341                open_hint: open_hint
342                    .to_browsing_context_open_hint()
343                    .map(Into::into)
344                    .unwrap_or_else(|| {
345                        unreachable!(
346                            "window-oriented tab-open hints are mapped to WindowOpenRequested"
347                        )
348                    }),
349                user_gesture: *user_gesture,
350            }),
351        },
352        IpcEvent::TabOpenResolved {
353            profile_id,
354            request_id,
355            result,
356        } => match result {
357            TabOpenResult::OpenedNewTab { tab_id } => Some(BrowserEvent::WindowOpenResolved {
358                profile_id: profile_id.clone(),
359                request_id: *request_id,
360                result: WindowOpenResult::OpenedNewWindow {
361                    window: synthetic_window_descriptor(
362                        WindowId::new(tab_id.get()),
363                        WindowKind::Normal,
364                        true,
365                    ),
366                },
367            }),
368            _ => Some(BrowserEvent::BrowsingContextOpenResolved {
369                profile_id: profile_id.clone(),
370                request_id: *request_id,
371                result: ChromeBrowsingContextOpenResult::from(*result).into(),
372            }),
373        },
374        IpcEvent::NavigationStateChanged {
375            profile_id,
376            browsing_context_id,
377            url,
378            can_go_back,
379            can_go_forward,
380            is_loading,
381        } => Some(BrowserEvent::BrowsingContext {
382            profile_id: profile_id.clone(),
383            browsing_context_id: browsing_context_id.to_browsing_context_id(),
384            event: Box::new(BrowsingContextEvent::NavigationStateChanged {
385                url: url.clone(),
386                can_go_back: *can_go_back,
387                can_go_forward: *can_go_forward,
388                is_loading: *is_loading,
389            }),
390        }),
391        IpcEvent::CursorChanged {
392            profile_id,
393            browsing_context_id,
394            cursor_type,
395        } => Some(BrowserEvent::BrowsingContext {
396            profile_id: profile_id.clone(),
397            browsing_context_id: browsing_context_id.to_browsing_context_id(),
398            event: Box::new(BrowsingContextEvent::CursorChanged {
399                cursor_type: *cursor_type,
400            }),
401        }),
402        IpcEvent::TitleUpdated {
403            profile_id,
404            browsing_context_id,
405            title,
406        } => Some(BrowserEvent::BrowsingContext {
407            profile_id: profile_id.clone(),
408            browsing_context_id: browsing_context_id.to_browsing_context_id(),
409            event: Box::new(BrowsingContextEvent::TitleUpdated {
410                title: title.clone(),
411            }),
412        }),
413        IpcEvent::FaviconUrlUpdated {
414            profile_id,
415            browsing_context_id,
416            url,
417        } => Some(BrowserEvent::BrowsingContext {
418            profile_id: profile_id.clone(),
419            browsing_context_id: browsing_context_id.to_browsing_context_id(),
420            event: Box::new(BrowsingContextEvent::FaviconUrlUpdated { url: url.clone() }),
421        }),
422        IpcEvent::BeforeUnloadDialogRequested {
423            profile_id,
424            browsing_context_id,
425            request_id,
426            reason,
427        } => Some(BrowserEvent::BrowsingContext {
428            profile_id: profile_id.clone(),
429            browsing_context_id: browsing_context_id.to_browsing_context_id(),
430            event: Box::new(BrowsingContextEvent::JavaScriptDialogRequested {
431                request_id: *request_id,
432                message: String::new(),
433                default_prompt_text: None,
434                r#type: cbf::data::dialog::DialogType::BeforeUnload,
435                beforeunload_reason: Some((*reason).into()),
436            }),
437        }),
438        IpcEvent::JavaScriptDialogRequested {
439            profile_id,
440            browsing_context_id,
441            request_id,
442            r#type,
443            message,
444            default_prompt_text,
445            reason,
446        } => Some(BrowserEvent::BrowsingContext {
447            profile_id: profile_id.clone(),
448            browsing_context_id: browsing_context_id.to_browsing_context_id(),
449            event: Box::new(BrowsingContextEvent::JavaScriptDialogRequested {
450                request_id: *request_id,
451                message: message.clone(),
452                default_prompt_text: default_prompt_text.clone(),
453                r#type: *r#type,
454                beforeunload_reason: if *r#type == DialogType::BeforeUnload {
455                    Some((*reason).into())
456                } else {
457                    None
458                },
459            }),
460        }),
461        IpcEvent::TabClosed {
462            profile_id,
463            browsing_context_id,
464        } => Some(BrowserEvent::BrowsingContext {
465            profile_id: profile_id.clone(),
466            browsing_context_id: browsing_context_id.to_browsing_context_id(),
467            event: Box::new(BrowsingContextEvent::Closed),
468        }),
469        IpcEvent::TabResizeAcknowledged { .. } => None,
470        IpcEvent::TabDomHtmlRead {
471            profile_id,
472            browsing_context_id,
473            request_id,
474            html,
475        } => Some(BrowserEvent::BrowsingContext {
476            profile_id: profile_id.clone(),
477            browsing_context_id: browsing_context_id.to_browsing_context_id(),
478            event: Box::new(BrowsingContextEvent::DomHtmlRead {
479                request_id: *request_id,
480                html: html.clone(),
481            }),
482        }),
483        IpcEvent::DragStartRequested {
484            profile_id,
485            browsing_context_id,
486            request,
487        } => Some(BrowserEvent::BrowsingContext {
488            profile_id: profile_id.clone(),
489            browsing_context_id: browsing_context_id.to_browsing_context_id(),
490            event: Box::new(BrowsingContextEvent::DragStartRequested {
491                request: request.clone().into(),
492            }),
493        }),
494        IpcEvent::ShutdownBlocked {
495            request_id,
496            dirty_browsing_context_ids,
497        } => Some(BrowserEvent::ShutdownBlocked {
498            request_id: *request_id,
499            dirty_browsing_context_ids: dirty_browsing_context_ids
500                .iter()
501                .copied()
502                .map(|id| id.to_browsing_context_id())
503                .collect(),
504        }),
505        IpcEvent::ShutdownProceeding { request_id } => Some(BrowserEvent::ShutdownProceeding {
506            request_id: *request_id,
507        }),
508        IpcEvent::ShutdownCancelled { request_id } => Some(BrowserEvent::ShutdownCancelled {
509            request_id: *request_id,
510        }),
511        IpcEvent::ExtensionsListed {
512            profile_id,
513            extensions,
514        } => Some(BrowserEvent::ExtensionsListed {
515            profile_id: profile_id.clone(),
516            extensions: extensions
517                .iter()
518                .cloned()
519                .map(ChromeExtensionInfo::into)
520                .collect(),
521        }),
522        IpcEvent::PromptUiOpenRequested {
523            profile_id,
524            source_tab_id,
525            request_id,
526            kind,
527        } => Some(BrowserEvent::AuxiliaryWindowOpenRequested {
528            profile_id: profile_id.clone(),
529            request_id: *request_id,
530            source_browsing_context_id: source_tab_id.map(|tab_id| tab_id.to_browsing_context_id()),
531            kind: prompt_ui_kind_to_auxiliary_window_kind(kind),
532        }),
533        IpcEvent::PromptUiResolved {
534            profile_id,
535            source_tab_id,
536            request_id,
537            resolution,
538        } => Some(BrowserEvent::AuxiliaryWindowResolved {
539            profile_id: profile_id.clone(),
540            request_id: *request_id,
541            source_browsing_context_id: source_tab_id.map(|tab_id| tab_id.to_browsing_context_id()),
542            resolution: prompt_ui_resolution_to_auxiliary_window_resolution(resolution),
543        }),
544        IpcEvent::ExtensionRuntimeWarning {
545            profile_id,
546            browsing_context_id,
547            detail,
548        } => Some(BrowserEvent::BrowsingContext {
549            profile_id: profile_id.clone(),
550            browsing_context_id: browsing_context_id.to_browsing_context_id(),
551            event: Box::new(BrowsingContextEvent::ExtensionRuntimeWarning {
552                detail: detail.clone(),
553            }),
554        }),
555        IpcEvent::PromptUiOpened {
556            profile_id,
557            source_tab_id,
558            prompt_ui_id,
559            kind,
560            title,
561            modal,
562        } => Some(BrowserEvent::AuxiliaryWindowOpened {
563            profile_id: profile_id.clone(),
564            source_browsing_context_id: source_tab_id.map(|tab_id| tab_id.to_browsing_context_id()),
565            window_id: prompt_ui_id_to_auxiliary_window_id(*prompt_ui_id),
566            kind: prompt_ui_kind_to_auxiliary_window_kind(kind),
567            title: title.clone(),
568            modal: *modal,
569        }),
570        IpcEvent::PromptUiClosed {
571            profile_id,
572            source_tab_id,
573            prompt_ui_id,
574            kind,
575            reason,
576        } => Some(BrowserEvent::AuxiliaryWindowClosed {
577            profile_id: profile_id.clone(),
578            source_browsing_context_id: source_tab_id.map(|tab_id| tab_id.to_browsing_context_id()),
579            window_id: prompt_ui_id_to_auxiliary_window_id(*prompt_ui_id),
580            kind: prompt_ui_kind_to_auxiliary_window_kind(kind),
581            reason: prompt_ui_close_reason_to_auxiliary_window_close_reason(reason),
582        }),
583        IpcEvent::DownloadCreated {
584            profile_id,
585            download,
586        } => Some(BrowserEvent::DownloadCreated {
587            profile_id: profile_id.clone(),
588            download_id: download.download_id.into(),
589            source_browsing_context_id: download
590                .source_tab_id
591                .map(|tab_id| tab_id.to_browsing_context_id()),
592            file_name: download.file_name.clone(),
593            total_bytes: download.total_bytes,
594            target_path: download.target_path.clone(),
595        }),
596        IpcEvent::DownloadUpdated {
597            profile_id,
598            download,
599        } => Some(BrowserEvent::DownloadUpdated {
600            profile_id: profile_id.clone(),
601            download_id: download.download_id.into(),
602            source_browsing_context_id: download
603                .source_tab_id
604                .map(|tab_id| tab_id.to_browsing_context_id()),
605            state: download.state.into(),
606            file_name: download.file_name.clone(),
607            received_bytes: download.received_bytes,
608            total_bytes: download.total_bytes,
609            target_path: download.target_path.clone(),
610            can_resume: download.can_resume,
611            is_paused: download.is_paused,
612        }),
613        IpcEvent::DownloadCompleted {
614            profile_id,
615            download,
616        } => Some(BrowserEvent::DownloadCompleted {
617            profile_id: profile_id.clone(),
618            download_id: download.download_id.into(),
619            source_browsing_context_id: download
620                .source_tab_id
621                .map(|tab_id| tab_id.to_browsing_context_id()),
622            outcome: download.outcome.into(),
623            file_name: download.file_name.clone(),
624            received_bytes: download.received_bytes,
625            total_bytes: download.total_bytes,
626            target_path: download.target_path.clone(),
627        }),
628    }
629}
630
631fn synthetic_window_descriptor(
632    window_id: WindowId,
633    kind: WindowKind,
634    focused: bool,
635) -> WindowDescriptor {
636    WindowDescriptor {
637        window_id,
638        kind,
639        state: WindowState::Normal,
640        focused,
641        incognito: false,
642        always_on_top: false,
643        bounds: WindowBounds {
644            left: 0,
645            top: 0,
646            width: 1280,
647            height: 720,
648        },
649    }
650}
651
652fn prompt_ui_permission_to_permission_prompt_type(
653    permission: &PromptUiPermissionType,
654    permission_key: Option<&str>,
655) -> PermissionPromptType {
656    match permission {
657        PromptUiPermissionType::Geolocation => PermissionPromptType::Geolocation,
658        PromptUiPermissionType::Notifications => PermissionPromptType::Notifications,
659        PromptUiPermissionType::AudioCapture => PermissionPromptType::AudioCapture,
660        PromptUiPermissionType::VideoCapture => PermissionPromptType::VideoCapture,
661        PromptUiPermissionType::Unknown => permission_key
662            .map(str::trim)
663            .filter(|value| !value.is_empty())
664            .map(|value| PermissionPromptType::Other(value.to_string()))
665            .unwrap_or(PermissionPromptType::Unknown),
666    }
667}
668
669fn prompt_ui_kind_to_auxiliary_window_kind(kind: &PromptUiKind) -> AuxiliaryWindowKind {
670    match kind {
671        PromptUiKind::PermissionPrompt {
672            permission,
673            permission_key,
674        } => AuxiliaryWindowKind::PermissionPrompt {
675            permission: prompt_ui_permission_to_permission_prompt_type(
676                permission,
677                permission_key.as_deref(),
678            ),
679        },
680        PromptUiKind::DownloadPrompt {
681            download_id,
682            file_name,
683            total_bytes,
684            suggested_path,
685            reason,
686        } => AuxiliaryWindowKind::DownloadPrompt {
687            download_id: (*download_id).into(),
688            file_name: file_name.clone(),
689            total_bytes: *total_bytes,
690            suggested_path: suggested_path.clone(),
691            action_hint: (*reason).into(),
692        },
693        PromptUiKind::ExtensionInstallPrompt {
694            extension_id,
695            extension_name,
696            permission_names,
697        } => AuxiliaryWindowKind::ExtensionInstallPrompt {
698            extension_id: extension_id.clone(),
699            extension_name: extension_name.clone(),
700            permission_names: permission_names.clone(),
701        },
702        PromptUiKind::ExtensionUninstallPrompt {
703            extension_id,
704            extension_name,
705            triggering_extension_name,
706            can_report_abuse,
707        } => AuxiliaryWindowKind::ExtensionUninstallPrompt {
708            extension_id: extension_id.clone(),
709            extension_name: extension_name.clone(),
710            triggering_extension_name: triggering_extension_name.clone(),
711            can_report_abuse: *can_report_abuse,
712        },
713        PromptUiKind::PrintPreviewDialog => AuxiliaryWindowKind::PrintPreviewDialog,
714        PromptUiKind::Unknown => AuxiliaryWindowKind::Unknown,
715    }
716}
717
718fn prompt_ui_resolution_to_auxiliary_window_resolution(
719    resolution: &PromptUiResolution,
720) -> AuxiliaryWindowResolution {
721    match resolution {
722        PromptUiResolution::PermissionPrompt {
723            permission,
724            permission_key,
725            result,
726        } => AuxiliaryWindowResolution::PermissionPrompt {
727            permission: prompt_ui_permission_to_permission_prompt_type(
728                permission,
729                permission_key.as_deref(),
730            ),
731            result: match result {
732                PromptUiResolutionResult::Allowed => PermissionPromptResult::Allowed,
733                PromptUiResolutionResult::Denied => PermissionPromptResult::Denied,
734                PromptUiResolutionResult::Aborted => PermissionPromptResult::Aborted,
735                PromptUiResolutionResult::Unknown => PermissionPromptResult::Unknown,
736            },
737        },
738        PromptUiResolution::DownloadPrompt {
739            download_id,
740            destination_path,
741            result,
742        } => AuxiliaryWindowResolution::DownloadPrompt {
743            download_id: (*download_id).into(),
744            destination_path: destination_path.clone(),
745            result: match result {
746                ChromeDownloadPromptResult::Allowed => DownloadPromptResult::Allowed,
747                ChromeDownloadPromptResult::Denied => DownloadPromptResult::Denied,
748                ChromeDownloadPromptResult::Aborted => DownloadPromptResult::Aborted,
749            },
750        },
751        PromptUiResolution::ExtensionInstallPrompt {
752            extension_id,
753            result,
754            detail,
755        } => AuxiliaryWindowResolution::ExtensionInstallPrompt {
756            extension_id: extension_id.clone(),
757            result: match result {
758                PromptUiExtensionInstallResult::Accepted => ExtensionInstallPromptResult::Accepted,
759                PromptUiExtensionInstallResult::AcceptedWithWithheldPermissions => {
760                    ExtensionInstallPromptResult::AcceptedWithWithheldPermissions
761                }
762                PromptUiExtensionInstallResult::UserCanceled => {
763                    ExtensionInstallPromptResult::UserCanceled
764                }
765                PromptUiExtensionInstallResult::Aborted => ExtensionInstallPromptResult::Aborted,
766            },
767            detail: detail.clone(),
768        },
769        PromptUiResolution::ExtensionUninstallPrompt {
770            extension_id,
771            result,
772            detail,
773            report_abuse,
774        } => AuxiliaryWindowResolution::ExtensionUninstallPrompt {
775            extension_id: extension_id.clone(),
776            result: match result {
777                PromptUiExtensionUninstallResult::Accepted => {
778                    ExtensionUninstallPromptResult::Accepted
779                }
780                PromptUiExtensionUninstallResult::UserCanceled => {
781                    ExtensionUninstallPromptResult::UserCanceled
782                }
783                PromptUiExtensionUninstallResult::Aborted => {
784                    ExtensionUninstallPromptResult::Aborted
785                }
786                PromptUiExtensionUninstallResult::Failed => ExtensionUninstallPromptResult::Failed,
787            },
788            detail: detail.clone(),
789            report_abuse: *report_abuse,
790        },
791        PromptUiResolution::PrintPreviewDialog { result } => match result {
792            PromptUiDialogResult::Proceeded => AuxiliaryWindowResolution::Unknown,
793            PromptUiDialogResult::Canceled => AuxiliaryWindowResolution::Unknown,
794            PromptUiDialogResult::Aborted => AuxiliaryWindowResolution::Unknown,
795            PromptUiDialogResult::Unknown => AuxiliaryWindowResolution::Unknown,
796        },
797        PromptUiResolution::Unknown => AuxiliaryWindowResolution::Unknown,
798    }
799}
800
801fn prompt_ui_id_to_auxiliary_window_id(value: PromptUiId) -> AuxiliaryWindowId {
802    AuxiliaryWindowId::new(value.get())
803}
804
805fn prompt_ui_close_reason_to_auxiliary_window_close_reason(
806    value: &PromptUiCloseReason,
807) -> AuxiliaryWindowCloseReason {
808    match value {
809        PromptUiCloseReason::UserCanceled => AuxiliaryWindowCloseReason::UserCanceled,
810        PromptUiCloseReason::HostForced => AuxiliaryWindowCloseReason::HostForced,
811        PromptUiCloseReason::SystemDismissed => AuxiliaryWindowCloseReason::SystemDismissed,
812        PromptUiCloseReason::Unknown => AuxiliaryWindowCloseReason::Unknown,
813    }
814}
815
816#[cfg(test)]
817mod tests {
818    use cbf::{
819        data::{
820            auxiliary_window::{
821                AuxiliaryWindowCloseReason, AuxiliaryWindowId, AuxiliaryWindowKind,
822                AuxiliaryWindowResolution, PermissionPromptResult, PermissionPromptType,
823            },
824            download::DownloadPromptActionHint,
825            extension::{ExtensionInstallPromptResult, ExtensionUninstallPromptResult},
826            ids::{BrowsingContextId, TransientBrowsingContextId},
827            transient_browsing_context::{
828                TransientBrowsingContextCloseReason, TransientBrowsingContextKind,
829            },
830        },
831        event::{BrowserEvent, BrowsingContextEvent, TransientBrowsingContextEvent},
832    };
833
834    use super::map_ipc_event_to_generic;
835    use crate::{
836        data::{
837            download::ChromeDownloadPromptReason,
838            ids::{PopupId, TabId},
839            prompt_ui::{
840                PromptUiCloseReason, PromptUiExtensionInstallResult,
841                PromptUiExtensionUninstallResult, PromptUiId, PromptUiKind, PromptUiResolution,
842            },
843        },
844        ffi::IpcEvent,
845    };
846
847    #[test]
848    fn tab_created_maps_tab_id_into_browsing_context_id() {
849        let event = IpcEvent::TabCreated {
850            profile_id: "default".to_string(),
851            browsing_context_id: TabId::new(7),
852            request_id: 1,
853        };
854
855        let mapped = map_ipc_event_to_generic(&event).unwrap();
856        assert!(matches!(
857            mapped,
858            BrowserEvent::BrowsingContext {
859                browsing_context_id,
860                event,
861                ..
862            } if browsing_context_id == BrowsingContextId::new(7)
863                && matches!(*event, BrowsingContextEvent::Created { request_id: 1 })
864        ));
865    }
866
867    #[test]
868    fn shutdown_blocked_maps_dirty_tab_ids_into_browsing_context_ids() {
869        let event = IpcEvent::ShutdownBlocked {
870            request_id: 9,
871            dirty_browsing_context_ids: vec![TabId::new(2), TabId::new(3)],
872        };
873
874        let mapped = map_ipc_event_to_generic(&event).unwrap();
875        assert!(matches!(
876            mapped,
877            BrowserEvent::ShutdownBlocked {
878                request_id: 9,
879                dirty_browsing_context_ids
880            } if dirty_browsing_context_ids
881                == vec![BrowsingContextId::new(2), BrowsingContextId::new(3)]
882        ));
883    }
884
885    #[test]
886    fn extension_popup_opened_maps_into_transient_browsing_context() {
887        let event = IpcEvent::ExtensionPopupOpened {
888            profile_id: "default".to_string(),
889            browsing_context_id: TabId::new(44),
890            popup_id: 88,
891            extension_id: "ext".to_string(),
892            title: "Popup".to_string(),
893        };
894
895        let mapped = map_ipc_event_to_generic(&event).unwrap();
896        assert!(matches!(
897            mapped,
898            BrowserEvent::TransientBrowsingContext {
899                transient_browsing_context_id,
900                parent_browsing_context_id,
901                event,
902                ..
903            } if transient_browsing_context_id == TransientBrowsingContextId::new(88)
904                && parent_browsing_context_id == BrowsingContextId::new(44)
905                && matches!(
906                    *event,
907                    TransientBrowsingContextEvent::Opened {
908                        kind: TransientBrowsingContextKind::Popup,
909                        title: Some(ref title),
910                    } if title == "Popup"
911                )
912        ));
913    }
914
915    #[test]
916    fn extension_popup_closed_maps_into_transient_browsing_context() {
917        let event = IpcEvent::ExtensionPopupClosed {
918            profile_id: "default".to_string(),
919            browsing_context_id: TabId::new(44),
920            popup_id: 88,
921        };
922
923        let mapped = map_ipc_event_to_generic(&event).unwrap();
924        assert!(matches!(
925            mapped,
926            BrowserEvent::TransientBrowsingContext {
927                transient_browsing_context_id,
928                parent_browsing_context_id,
929                event,
930                ..
931            } if transient_browsing_context_id == TransientBrowsingContextId::new(88)
932                && parent_browsing_context_id == BrowsingContextId::new(44)
933                && matches!(
934                    *event,
935                    TransientBrowsingContextEvent::Closed {
936                        reason: TransientBrowsingContextCloseReason::Unknown
937                    }
938                )
939        ));
940    }
941
942    #[test]
943    fn extension_popup_ime_bounds_maps_into_transient_browsing_context() {
944        let event = IpcEvent::ExtensionPopupImeBoundsUpdated {
945            profile_id: "default".to_string(),
946            browsing_context_id: TabId::new(44),
947            popup_id: PopupId::new(88),
948            update: crate::data::ime::ChromeImeBoundsUpdate {
949                composition: None,
950                selection: Some(crate::data::ime::ChromeTextSelectionBounds {
951                    range_start: 1,
952                    range_end: 1,
953                    caret_rect: crate::data::ime::ChromeImeRect {
954                        x: 10,
955                        y: 20,
956                        width: 2,
957                        height: 16,
958                    },
959                    first_selection_rect: crate::data::ime::ChromeImeRect {
960                        x: 10,
961                        y: 20,
962                        width: 2,
963                        height: 16,
964                    },
965                }),
966            },
967        };
968
969        let mapped = map_ipc_event_to_generic(&event).unwrap();
970        assert!(matches!(
971            mapped,
972            BrowserEvent::TransientBrowsingContext {
973                transient_browsing_context_id,
974                parent_browsing_context_id,
975                event,
976                ..
977            } if transient_browsing_context_id == TransientBrowsingContextId::new(88)
978                && parent_browsing_context_id == BrowsingContextId::new(44)
979                && matches!(
980                    *event,
981                    TransientBrowsingContextEvent::ImeBoundsUpdated { ref update }
982                        if update.selection.as_ref().is_some_and(|selection| selection.range_start == 1)
983                )
984        ));
985    }
986
987    #[test]
988    fn prompt_ui_requested_maps_into_permission_auxiliary_window() {
989        let event = IpcEvent::PromptUiOpenRequested {
990            profile_id: "default".to_string(),
991            source_tab_id: Some(TabId::new(44)),
992            request_id: 12,
993            kind: PromptUiKind::PermissionPrompt {
994                permission: crate::data::prompt_ui::PromptUiPermissionType::Geolocation,
995                permission_key: None,
996            },
997        };
998
999        let mapped = map_ipc_event_to_generic(&event).unwrap();
1000        assert!(matches!(
1001            mapped,
1002            BrowserEvent::AuxiliaryWindowOpenRequested {
1003                profile_id,
1004                request_id: 12,
1005                source_browsing_context_id: Some(source_browsing_context_id),
1006                kind: AuxiliaryWindowKind::PermissionPrompt {
1007                    permission: PermissionPromptType::Geolocation
1008                },
1009            } if profile_id == "default"
1010                && source_browsing_context_id == BrowsingContextId::new(44)
1011        ));
1012    }
1013
1014    #[test]
1015    fn prompt_ui_resolved_maps_into_permission_auxiliary_resolution() {
1016        let event = IpcEvent::PromptUiResolved {
1017            profile_id: "default".to_string(),
1018            source_tab_id: Some(TabId::new(44)),
1019            request_id: 12,
1020            resolution: crate::data::prompt_ui::PromptUiResolution::PermissionPrompt {
1021                permission: crate::data::prompt_ui::PromptUiPermissionType::Geolocation,
1022                permission_key: None,
1023                result: crate::data::prompt_ui::PromptUiResolutionResult::Denied,
1024            },
1025        };
1026
1027        let mapped = map_ipc_event_to_generic(&event).unwrap();
1028        assert!(matches!(
1029            mapped,
1030            BrowserEvent::AuxiliaryWindowResolved {
1031                profile_id,
1032                request_id: 12,
1033                source_browsing_context_id: Some(source_browsing_context_id),
1034                resolution: AuxiliaryWindowResolution::PermissionPrompt {
1035                    permission: PermissionPromptType::Geolocation,
1036                    result: PermissionPromptResult::Denied
1037                },
1038            } if profile_id == "default"
1039                && source_browsing_context_id == BrowsingContextId::new(44)
1040        ));
1041    }
1042
1043    #[test]
1044    fn prompt_ui_requested_maps_unknown_with_key_to_other_permission_prompt() {
1045        let event = IpcEvent::PromptUiOpenRequested {
1046            profile_id: "default".to_string(),
1047            source_tab_id: Some(TabId::new(44)),
1048            request_id: 12,
1049            kind: PromptUiKind::PermissionPrompt {
1050                permission: crate::data::prompt_ui::PromptUiPermissionType::Unknown,
1051                permission_key: Some("window_management".to_string()),
1052            },
1053        };
1054
1055        let mapped = map_ipc_event_to_generic(&event).unwrap();
1056        assert!(matches!(
1057            mapped,
1058            BrowserEvent::AuxiliaryWindowOpenRequested {
1059                source_browsing_context_id: Some(source_browsing_context_id),
1060                kind: AuxiliaryWindowKind::PermissionPrompt {
1061                    permission: PermissionPromptType::Other(ref key)
1062                },
1063                ..
1064            } if source_browsing_context_id == BrowsingContextId::new(44)
1065                && key == "window_management"
1066        ));
1067    }
1068
1069    #[test]
1070    fn prompt_ui_requested_maps_download_reason_into_auxiliary_window() {
1071        let event = IpcEvent::PromptUiOpenRequested {
1072            profile_id: "default".to_string(),
1073            source_tab_id: Some(TabId::new(12)),
1074            request_id: 71,
1075            kind: PromptUiKind::DownloadPrompt {
1076                download_id: crate::data::download::ChromeDownloadId::new(55),
1077                file_name: "file.bin".to_string(),
1078                total_bytes: Some(42),
1079                suggested_path: Some("/tmp/file.bin".to_string()),
1080                reason: ChromeDownloadPromptReason::SaveAs,
1081            },
1082        };
1083
1084        let mapped = map_ipc_event_to_generic(&event).unwrap();
1085        assert!(matches!(
1086            mapped,
1087            BrowserEvent::AuxiliaryWindowOpenRequested {
1088                request_id: 71,
1089                source_browsing_context_id: Some(source_browsing_context_id),
1090                kind: AuxiliaryWindowKind::DownloadPrompt {
1091                    action_hint: DownloadPromptActionHint::SelectDestination,
1092                    ..
1093                },
1094                ..
1095            } if source_browsing_context_id == BrowsingContextId::new(12)
1096        ));
1097    }
1098
1099    #[test]
1100    fn prompt_ui_requested_maps_dlp_reason_into_deny_action_hint() {
1101        let event = IpcEvent::PromptUiOpenRequested {
1102            profile_id: "default".to_string(),
1103            source_tab_id: Some(TabId::new(12)),
1104            request_id: 72,
1105            kind: PromptUiKind::DownloadPrompt {
1106                download_id: crate::data::download::ChromeDownloadId::new(56),
1107                file_name: "file.bin".to_string(),
1108                total_bytes: Some(42),
1109                suggested_path: Some("/tmp/file.bin".to_string()),
1110                reason: ChromeDownloadPromptReason::DlpBlocked,
1111            },
1112        };
1113
1114        let mapped = map_ipc_event_to_generic(&event).unwrap();
1115        assert!(matches!(
1116            mapped,
1117            BrowserEvent::AuxiliaryWindowOpenRequested {
1118                request_id: 72,
1119                source_browsing_context_id: Some(source_browsing_context_id),
1120                kind: AuxiliaryWindowKind::DownloadPrompt {
1121                    action_hint: DownloadPromptActionHint::Deny,
1122                    ..
1123                },
1124                ..
1125            } if source_browsing_context_id == BrowsingContextId::new(12)
1126        ));
1127    }
1128
1129    #[test]
1130    fn prompt_ui_open_requested_maps_extension_install_kind() {
1131        let event = IpcEvent::PromptUiOpenRequested {
1132            profile_id: "default".to_string(),
1133            source_tab_id: Some(TabId::new(9)),
1134            request_id: 2,
1135            kind: PromptUiKind::ExtensionInstallPrompt {
1136                extension_id: "ext".to_string(),
1137                extension_name: "ExtName".to_string(),
1138                permission_names: vec!["tabs".to_string()],
1139            },
1140        };
1141
1142        let mapped = map_ipc_event_to_generic(&event).unwrap();
1143        assert!(matches!(
1144            mapped,
1145            BrowserEvent::AuxiliaryWindowOpenRequested {
1146                request_id: 2,
1147                source_browsing_context_id: Some(source_browsing_context_id),
1148                kind: AuxiliaryWindowKind::ExtensionInstallPrompt {
1149                    ref extension_id,
1150                    ref extension_name,
1151                    ref permission_names,
1152                },
1153                ..
1154            } if source_browsing_context_id == BrowsingContextId::new(9)
1155                && extension_id == "ext"
1156                && extension_name == "ExtName"
1157                && permission_names == &vec!["tabs".to_string()]
1158        ));
1159    }
1160
1161    #[test]
1162    fn prompt_ui_resolved_maps_extension_install_resolution() {
1163        let event = IpcEvent::PromptUiResolved {
1164            profile_id: "default".to_string(),
1165            source_tab_id: Some(TabId::new(9)),
1166            request_id: 5,
1167            resolution: PromptUiResolution::ExtensionInstallPrompt {
1168                extension_id: "ext".to_string(),
1169                result: PromptUiExtensionInstallResult::UserCanceled,
1170                detail: Some("user dismissed".to_string()),
1171            },
1172        };
1173
1174        let mapped = map_ipc_event_to_generic(&event).unwrap();
1175        assert!(matches!(
1176            mapped,
1177            BrowserEvent::AuxiliaryWindowResolved {
1178                request_id: 5,
1179                source_browsing_context_id: Some(source_browsing_context_id),
1180                resolution: AuxiliaryWindowResolution::ExtensionInstallPrompt {
1181                    ref extension_id,
1182                    result: ExtensionInstallPromptResult::UserCanceled,
1183                    detail: Some(ref detail),
1184                },
1185                ..
1186            } if source_browsing_context_id == BrowsingContextId::new(9)
1187                && extension_id == "ext"
1188                && detail == "user dismissed"
1189        ));
1190    }
1191
1192    #[test]
1193    fn prompt_ui_open_requested_maps_extension_uninstall_kind() {
1194        let event = IpcEvent::PromptUiOpenRequested {
1195            profile_id: "default".to_string(),
1196            source_tab_id: None,
1197            request_id: 6,
1198            kind: PromptUiKind::ExtensionUninstallPrompt {
1199                extension_id: "ext".to_string(),
1200                extension_name: "ExtName".to_string(),
1201                triggering_extension_name: Some("Trigger".to_string()),
1202                can_report_abuse: true,
1203            },
1204        };
1205
1206        let mapped = map_ipc_event_to_generic(&event).unwrap();
1207        assert!(matches!(
1208            mapped,
1209            BrowserEvent::AuxiliaryWindowOpenRequested {
1210                request_id: 6,
1211                source_browsing_context_id: None,
1212                kind: AuxiliaryWindowKind::ExtensionUninstallPrompt {
1213                    ref extension_id,
1214                    ref extension_name,
1215                    ref triggering_extension_name,
1216                    can_report_abuse: true,
1217                },
1218                ..
1219            } if extension_id == "ext"
1220                && extension_name == "ExtName"
1221                && triggering_extension_name.as_deref() == Some("Trigger")
1222        ));
1223    }
1224
1225    #[test]
1226    fn prompt_ui_resolved_maps_extension_uninstall_resolution() {
1227        let event = IpcEvent::PromptUiResolved {
1228            profile_id: "default".to_string(),
1229            source_tab_id: None,
1230            request_id: 7,
1231            resolution: PromptUiResolution::ExtensionUninstallPrompt {
1232                extension_id: "ext".to_string(),
1233                result: PromptUiExtensionUninstallResult::Failed,
1234                detail: Some("uninstall_failed".to_string()),
1235                report_abuse: true,
1236            },
1237        };
1238
1239        let mapped = map_ipc_event_to_generic(&event).unwrap();
1240        assert!(matches!(
1241            mapped,
1242            BrowserEvent::AuxiliaryWindowResolved {
1243                request_id: 7,
1244                source_browsing_context_id: None,
1245                resolution: AuxiliaryWindowResolution::ExtensionUninstallPrompt {
1246                    ref extension_id,
1247                    result: ExtensionUninstallPromptResult::Failed,
1248                    detail: Some(ref detail),
1249                    report_abuse: true,
1250                },
1251                ..
1252            } if extension_id == "ext" && detail == "uninstall_failed"
1253        ));
1254    }
1255
1256    #[test]
1257    fn prompt_ui_opened_maps_into_auxiliary_window_opened() {
1258        let event = IpcEvent::PromptUiOpened {
1259            profile_id: "default".to_string(),
1260            source_tab_id: Some(TabId::new(11)),
1261            prompt_ui_id: PromptUiId::new(88),
1262            kind: PromptUiKind::PrintPreviewDialog,
1263            title: Some("Print".to_string()),
1264            modal: true,
1265        };
1266
1267        let mapped = map_ipc_event_to_generic(&event).unwrap();
1268        assert!(matches!(
1269            mapped,
1270            BrowserEvent::AuxiliaryWindowOpened {
1271                source_browsing_context_id: Some(source_browsing_context_id),
1272                window_id,
1273                kind: AuxiliaryWindowKind::PrintPreviewDialog,
1274                title: Some(ref title),
1275                modal: true,
1276                ..
1277            } if source_browsing_context_id == BrowsingContextId::new(11)
1278                && window_id == AuxiliaryWindowId::new(88)
1279                && title == "Print"
1280        ));
1281    }
1282
1283    #[test]
1284    fn prompt_ui_closed_maps_into_auxiliary_window_closed() {
1285        let event = IpcEvent::PromptUiClosed {
1286            profile_id: "default".to_string(),
1287            source_tab_id: Some(TabId::new(11)),
1288            prompt_ui_id: PromptUiId::new(88),
1289            kind: PromptUiKind::PrintPreviewDialog,
1290            reason: PromptUiCloseReason::SystemDismissed,
1291        };
1292
1293        let mapped = map_ipc_event_to_generic(&event).unwrap();
1294        assert!(matches!(
1295            mapped,
1296            BrowserEvent::AuxiliaryWindowClosed {
1297                source_browsing_context_id: Some(source_browsing_context_id),
1298                window_id,
1299                kind: AuxiliaryWindowKind::PrintPreviewDialog,
1300                reason: AuxiliaryWindowCloseReason::SystemDismissed,
1301                ..
1302            } if source_browsing_context_id == BrowsingContextId::new(11)
1303                && window_id == AuxiliaryWindowId::new(88)
1304        ));
1305    }
1306}