Skip to main content

cbf_chrome/
command.rs

1//! Chromium-specific command transport models and conversions.
2//!
3//! This module defines [`ChromeCommand`], the command vocabulary sent across
4//! the Chromium bridge, plus conversion logic from generic
5//! [`cbf::command::BrowserCommand`] values into Chrome-specific transport
6//! commands. It is the boundary where browser-generic concepts are mapped onto
7//! Chromium tab, popup, IME, download, and extension operations.
8
9use cbf::data::dialog::DialogResponse;
10use cbf::{
11    command::{BrowserCommand, BrowserOperation},
12    data::edit::EditAction,
13};
14
15use crate::data::{
16    background::ChromeBackgroundPolicy,
17    browsing_context_open::ChromeBrowsingContextOpenResponse,
18    custom_scheme::ChromeCustomSchemeResponse,
19    download::ChromeDownloadId,
20    drag::{
21        ChromeDragDrop, ChromeDragUpdate, ChromeExternalDragDrop, ChromeExternalDragEnter,
22        ChromeExternalDragUpdate,
23    },
24    execution::ChromeTabExecutionState,
25    extension::ChromeAuxiliaryWindowResponse,
26    find::{ChromeFindInPageOptions, ChromeStopFindAction},
27    ids::{PopupId, TabId},
28    ime::{
29        ChromeConfirmCompositionBehavior, ChromeImeCommitText, ChromeImeComposition,
30        ChromeTransientImeCommitText, ChromeTransientImeComposition,
31    },
32    input::{ChromeKeyEvent, ChromeMouseWheelEvent},
33    ipc::{TabIpcConfig, TabIpcMessage},
34    mouse::ChromeMouseEvent,
35    navigation::ChromeNavigationEntryId,
36    policy::ChromeBrowsingContextPolicy,
37    prompt_ui::{PromptUiId, PromptUiResponse},
38    visibility::ChromeTabVisibility,
39    window_open::ChromeWindowOpenResponse,
40};
41
42/// Chromium-specific transport command vocabulary.
43///
44/// Transient browsing context operations are currently transported through
45/// Chromium's extension popup plumbing. The public `cbf` API remains generic,
46/// and this layer performs the boundary translation into the current Chrome
47/// implementation model.
48#[derive(Debug, Clone, PartialEq)]
49pub enum ChromeCommand {
50    RequestShutdown {
51        request_id: u64,
52    },
53    ConfirmShutdown {
54        request_id: u64,
55        proceed: bool,
56    },
57    ForceShutdown,
58    ConfirmBeforeUnload {
59        browsing_context_id: TabId,
60        request_id: u64,
61        proceed: bool,
62    },
63    RespondJavaScriptDialog {
64        browsing_context_id: TabId,
65        request_id: u64,
66        response: DialogResponse,
67    },
68    RespondExtensionPopupJavaScriptDialog {
69        popup_id: PopupId,
70        request_id: u64,
71        response: DialogResponse,
72    },
73    ConfirmPermission {
74        browsing_context_id: TabId,
75        request_id: u64,
76        allow: bool,
77    },
78    CreateTab {
79        request_id: u64,
80        initial_url: Option<String>,
81        profile_id: String,
82        policy: Option<ChromeBrowsingContextPolicy>,
83    },
84    ListProfiles,
85    RespondCustomSchemeRequest {
86        response: ChromeCustomSchemeResponse,
87    },
88    RequestCloseTab {
89        browsing_context_id: TabId,
90    },
91    BeginCloseTabsTransaction {
92        request_id: u64,
93        browsing_context_ids: Vec<TabId>,
94    },
95    CommitCloseTabsTransaction {
96        request_id: u64,
97    },
98    CancelCloseTabsTransaction {
99        request_id: u64,
100    },
101    SetTabSize {
102        browsing_context_id: TabId,
103        width: u32,
104        height: u32,
105    },
106    SetTabBackgroundPolicy {
107        browsing_context_id: TabId,
108        policy: ChromeBackgroundPolicy,
109    },
110    Navigate {
111        browsing_context_id: TabId,
112        url: String,
113    },
114    GoBack {
115        browsing_context_id: TabId,
116    },
117    GoForward {
118        browsing_context_id: TabId,
119    },
120    GetNavigationHistory {
121        browsing_context_id: TabId,
122        request_id: u64,
123    },
124    TraverseHistoryToEntry {
125        browsing_context_id: TabId,
126        entry_id: ChromeNavigationEntryId,
127    },
128    TraverseHistoryByOffset {
129        browsing_context_id: TabId,
130        delta: i32,
131    },
132    Reload {
133        browsing_context_id: TabId,
134        ignore_cache: bool,
135    },
136    PrintPreview {
137        browsing_context_id: TabId,
138    },
139    OpenDevTools {
140        browsing_context_id: TabId,
141    },
142    InspectElement {
143        browsing_context_id: TabId,
144        x: i32,
145        y: i32,
146    },
147    GetTabDomHtml {
148        browsing_context_id: TabId,
149        request_id: u64,
150    },
151    FindInPage {
152        browsing_context_id: TabId,
153        request_id: u64,
154        options: ChromeFindInPageOptions,
155    },
156    StopFinding {
157        browsing_context_id: TabId,
158        action: ChromeStopFindAction,
159    },
160    SetTabFocus {
161        browsing_context_id: TabId,
162        focused: bool,
163    },
164    SetTabVisibility {
165        browsing_context_id: TabId,
166        visibility: ChromeTabVisibility,
167    },
168    SetTabExecutionState {
169        browsing_context_id: TabId,
170        state: ChromeTabExecutionState,
171    },
172    EnableTabIpc {
173        browsing_context_id: TabId,
174        config: TabIpcConfig,
175    },
176    DisableTabIpc {
177        browsing_context_id: TabId,
178    },
179    PostTabIpcMessage {
180        browsing_context_id: TabId,
181        message: TabIpcMessage,
182    },
183    SendKeyEvent {
184        browsing_context_id: TabId,
185        event: ChromeKeyEvent,
186        commands: Vec<String>,
187    },
188    ExecuteEditAction {
189        browsing_context_id: TabId,
190        action: EditAction,
191    },
192    SendMouseEvent {
193        browsing_context_id: TabId,
194        event: ChromeMouseEvent,
195    },
196    SendMouseWheelEvent {
197        browsing_context_id: TabId,
198        event: ChromeMouseWheelEvent,
199    },
200    SendDragUpdate {
201        update: ChromeDragUpdate,
202    },
203    SendDragDrop {
204        drop: ChromeDragDrop,
205    },
206    SendDragCancel {
207        session_id: u64,
208        browsing_context_id: TabId,
209    },
210    SendExternalDragEnter {
211        event: ChromeExternalDragEnter,
212    },
213    SendExternalDragUpdate {
214        event: ChromeExternalDragUpdate,
215    },
216    SendExternalDragLeave {
217        browsing_context_id: TabId,
218    },
219    SendExternalDragDrop {
220        event: ChromeExternalDragDrop,
221    },
222    SetImeComposition {
223        composition: ChromeImeComposition,
224    },
225    CommitImeText {
226        commit: ChromeImeCommitText,
227    },
228    FinishComposingText {
229        browsing_context_id: TabId,
230        behavior: ChromeConfirmCompositionBehavior,
231    },
232    ExecuteContextMenuCommand {
233        menu_id: u64,
234        command_id: i32,
235        event_flags: i32,
236    },
237    AcceptChoiceMenuSelection {
238        request_id: u64,
239        indices: Vec<i32>,
240    },
241    DismissChoiceMenu {
242        request_id: u64,
243    },
244    DismissContextMenu {
245        menu_id: u64,
246    },
247    PauseDownload {
248        download_id: ChromeDownloadId,
249    },
250    ResumeDownload {
251        download_id: ChromeDownloadId,
252    },
253    CancelDownload {
254        download_id: ChromeDownloadId,
255    },
256    ListExtensions {
257        profile_id: String,
258    },
259    ActivateExtensionAction {
260        browsing_context_id: TabId,
261        extension_id: String,
262    },
263    CloseExtensionPopup {
264        popup_id: PopupId,
265    },
266    SetExtensionPopupSize {
267        popup_id: PopupId,
268        width: u32,
269        height: u32,
270    },
271    SetExtensionPopupBackgroundPolicy {
272        popup_id: PopupId,
273        policy: ChromeBackgroundPolicy,
274    },
275    SetExtensionPopupFocus {
276        popup_id: PopupId,
277        focused: bool,
278    },
279    SendExtensionPopupKeyEvent {
280        popup_id: PopupId,
281        event: ChromeKeyEvent,
282        commands: Vec<String>,
283    },
284    ExecuteExtensionPopupEditAction {
285        popup_id: PopupId,
286        action: EditAction,
287    },
288    SendExtensionPopupMouseEvent {
289        popup_id: PopupId,
290        event: ChromeMouseEvent,
291    },
292    SendExtensionPopupMouseWheelEvent {
293        popup_id: PopupId,
294        event: ChromeMouseWheelEvent,
295    },
296    SetExtensionPopupComposition {
297        composition: ChromeTransientImeComposition,
298    },
299    CommitExtensionPopupText {
300        commit: ChromeTransientImeCommitText,
301    },
302    FinishExtensionPopupComposingText {
303        popup_id: PopupId,
304        behavior: ChromeConfirmCompositionBehavior,
305    },
306    OpenDefaultPromptUi {
307        profile_id: String,
308        request_id: u64,
309    },
310    RespondPromptUi {
311        profile_id: String,
312        request_id: u64,
313        response: PromptUiResponse,
314    },
315    ClosePromptUi {
316        profile_id: String,
317        prompt_ui_id: PromptUiId,
318    },
319    RespondTabOpen {
320        request_id: u64,
321        response: ChromeBrowsingContextOpenResponse,
322    },
323    RespondWindowOpen {
324        request_id: u64,
325        response: ChromeWindowOpenResponse,
326    },
327    UnsupportedGenericCommand {
328        operation: BrowserOperation,
329    },
330}
331
332impl From<BrowserCommand> for ChromeCommand {
333    fn from(value: BrowserCommand) -> Self {
334        match value {
335            BrowserCommand::Shutdown { request_id } => Self::RequestShutdown { request_id },
336            BrowserCommand::ConfirmShutdown {
337                request_id,
338                proceed,
339            } => Self::ConfirmShutdown {
340                request_id,
341                proceed,
342            },
343            BrowserCommand::ForceShutdown => Self::ForceShutdown,
344            BrowserCommand::ConfirmBeforeUnload {
345                browsing_context_id,
346                request_id,
347                proceed,
348            } => Self::ConfirmBeforeUnload {
349                browsing_context_id: browsing_context_id.into(),
350                request_id,
351                proceed,
352            },
353            BrowserCommand::RespondJavaScriptDialog {
354                browsing_context_id,
355                request_id,
356                response,
357            } => Self::RespondJavaScriptDialog {
358                browsing_context_id: browsing_context_id.into(),
359                request_id,
360                response,
361            },
362            BrowserCommand::RespondJavaScriptDialogInTransientBrowsingContext {
363                transient_browsing_context_id,
364                request_id,
365                response,
366            } => Self::RespondExtensionPopupJavaScriptDialog {
367                popup_id: transient_browsing_context_id.into(),
368                request_id,
369                response,
370            },
371            BrowserCommand::ConfirmPermission {
372                browsing_context_id,
373                request_id,
374                allow,
375            } => Self::ConfirmPermission {
376                browsing_context_id: browsing_context_id.into(),
377                request_id,
378                allow,
379            },
380            BrowserCommand::CreateBrowsingContext {
381                request_id,
382                initial_url,
383                profile_id,
384                policy,
385            } => Self::CreateTab {
386                request_id,
387                initial_url,
388                profile_id,
389                policy: policy.map(Into::into),
390            },
391            BrowserCommand::ListProfiles => Self::ListProfiles,
392            BrowserCommand::RequestCloseBrowsingContext {
393                browsing_context_id,
394            } => Self::RequestCloseTab {
395                browsing_context_id: browsing_context_id.into(),
396            },
397            BrowserCommand::BeginCloseBrowsingContextsTransaction {
398                request_id,
399                browsing_context_ids,
400            } => Self::BeginCloseTabsTransaction {
401                request_id,
402                browsing_context_ids: browsing_context_ids.into_iter().map(Into::into).collect(),
403            },
404            BrowserCommand::CommitCloseBrowsingContextsTransaction { request_id } => {
405                Self::CommitCloseTabsTransaction { request_id }
406            }
407            BrowserCommand::CancelCloseBrowsingContextsTransaction { request_id } => {
408                Self::CancelCloseTabsTransaction { request_id }
409            }
410            BrowserCommand::CloseTransientBrowsingContext {
411                transient_browsing_context_id,
412            } => Self::CloseExtensionPopup {
413                popup_id: transient_browsing_context_id.into(),
414            },
415            BrowserCommand::ResizeBrowsingContext {
416                browsing_context_id,
417                width,
418                height,
419            } => Self::SetTabSize {
420                browsing_context_id: browsing_context_id.into(),
421                width,
422                height,
423            },
424            BrowserCommand::ResizeTransientBrowsingContext {
425                transient_browsing_context_id,
426                width,
427                height,
428            } => Self::SetExtensionPopupSize {
429                popup_id: transient_browsing_context_id.into(),
430                width,
431                height,
432            },
433            BrowserCommand::SetBrowsingContextBackgroundPolicy {
434                browsing_context_id,
435                policy,
436            } => Self::SetTabBackgroundPolicy {
437                browsing_context_id: browsing_context_id.into(),
438                policy: policy.into(),
439            },
440            BrowserCommand::SetTransientBrowsingContextBackgroundPolicy {
441                transient_browsing_context_id,
442                policy,
443            } => Self::SetExtensionPopupBackgroundPolicy {
444                popup_id: transient_browsing_context_id.into(),
445                policy: policy.into(),
446            },
447            BrowserCommand::Navigate {
448                browsing_context_id,
449                url,
450            } => Self::Navigate {
451                browsing_context_id: browsing_context_id.into(),
452                url,
453            },
454            BrowserCommand::GoBack {
455                browsing_context_id,
456            } => Self::GoBack {
457                browsing_context_id: browsing_context_id.into(),
458            },
459            BrowserCommand::GoForward {
460                browsing_context_id,
461            } => Self::GoForward {
462                browsing_context_id: browsing_context_id.into(),
463            },
464            BrowserCommand::GetNavigationHistory {
465                browsing_context_id,
466                request_id,
467            } => Self::GetNavigationHistory {
468                browsing_context_id: browsing_context_id.into(),
469                request_id,
470            },
471            BrowserCommand::TraverseHistoryToEntry {
472                browsing_context_id,
473                entry_id,
474            } => Self::TraverseHistoryToEntry {
475                browsing_context_id: browsing_context_id.into(),
476                entry_id: entry_id.into(),
477            },
478            BrowserCommand::TraverseHistoryByOffset {
479                browsing_context_id,
480                delta,
481            } => Self::TraverseHistoryByOffset {
482                browsing_context_id: browsing_context_id.into(),
483                delta,
484            },
485            BrowserCommand::Reload {
486                browsing_context_id,
487                ignore_cache,
488            } => Self::Reload {
489                browsing_context_id: browsing_context_id.into(),
490                ignore_cache,
491            },
492            BrowserCommand::PrintPreview {
493                browsing_context_id,
494            } => Self::PrintPreview {
495                browsing_context_id: browsing_context_id.into(),
496            },
497            BrowserCommand::GetBrowsingContextDomHtml {
498                browsing_context_id,
499                request_id,
500            } => Self::GetTabDomHtml {
501                browsing_context_id: browsing_context_id.into(),
502                request_id,
503            },
504            BrowserCommand::SetBrowsingContextFocus {
505                browsing_context_id,
506                focused,
507            } => Self::SetTabFocus {
508                browsing_context_id: browsing_context_id.into(),
509                focused,
510            },
511            BrowserCommand::SetTransientBrowsingContextFocus {
512                transient_browsing_context_id,
513                focused,
514            } => Self::SetExtensionPopupFocus {
515                popup_id: transient_browsing_context_id.into(),
516                focused,
517            },
518            BrowserCommand::SetBrowsingContextVisibility {
519                browsing_context_id,
520                visibility,
521            } => Self::SetTabVisibility {
522                browsing_context_id: browsing_context_id.into(),
523                visibility: visibility.into(),
524            },
525            BrowserCommand::SetBrowsingContextExecutionState {
526                browsing_context_id,
527                state,
528            } => Self::SetTabExecutionState {
529                browsing_context_id: browsing_context_id.into(),
530                state: state.into(),
531            },
532            BrowserCommand::EnableIpc {
533                browsing_context_id,
534                config,
535            } => Self::EnableTabIpc {
536                browsing_context_id: browsing_context_id.into(),
537                config: config.into(),
538            },
539            BrowserCommand::DisableIpc {
540                browsing_context_id,
541            } => Self::DisableTabIpc {
542                browsing_context_id: browsing_context_id.into(),
543            },
544            BrowserCommand::PostBrowsingContextIpcMessage {
545                browsing_context_id,
546                message,
547            } => Self::PostTabIpcMessage {
548                browsing_context_id: browsing_context_id.into(),
549                message: message.into(),
550            },
551            BrowserCommand::SendKeyEvent {
552                browsing_context_id,
553                event,
554                commands,
555            } => Self::SendKeyEvent {
556                browsing_context_id: browsing_context_id.into(),
557                event: event.into(),
558                commands,
559            },
560            BrowserCommand::ExecuteEditAction {
561                browsing_context_id,
562                action,
563            } => Self::ExecuteEditAction {
564                browsing_context_id: browsing_context_id.into(),
565                action,
566            },
567            BrowserCommand::SendKeyEventToTransientBrowsingContext {
568                transient_browsing_context_id,
569                event,
570                commands,
571            } => Self::SendExtensionPopupKeyEvent {
572                popup_id: transient_browsing_context_id.into(),
573                event: event.into(),
574                commands,
575            },
576            BrowserCommand::ExecuteEditActionInTransientBrowsingContext {
577                transient_browsing_context_id,
578                action,
579            } => Self::ExecuteExtensionPopupEditAction {
580                popup_id: transient_browsing_context_id.into(),
581                action,
582            },
583            BrowserCommand::SendMouseEvent {
584                browsing_context_id,
585                event,
586            } => Self::SendMouseEvent {
587                browsing_context_id: browsing_context_id.into(),
588                event: event.into(),
589            },
590            BrowserCommand::SendMouseEventToTransientBrowsingContext {
591                transient_browsing_context_id,
592                event,
593            } => Self::SendExtensionPopupMouseEvent {
594                popup_id: transient_browsing_context_id.into(),
595                event: event.into(),
596            },
597            BrowserCommand::SendMouseWheelEvent {
598                browsing_context_id,
599                event,
600            } => Self::SendMouseWheelEvent {
601                browsing_context_id: browsing_context_id.into(),
602                event: event.into(),
603            },
604            BrowserCommand::SendMouseWheelEventToTransientBrowsingContext {
605                transient_browsing_context_id,
606                event,
607            } => Self::SendExtensionPopupMouseWheelEvent {
608                popup_id: transient_browsing_context_id.into(),
609                event: event.into(),
610            },
611            BrowserCommand::SendDragUpdate { update } => Self::SendDragUpdate {
612                update: update.into(),
613            },
614            BrowserCommand::SendDragDrop { drop } => Self::SendDragDrop { drop: drop.into() },
615            BrowserCommand::SendDragCancel {
616                session_id,
617                browsing_context_id,
618            } => Self::SendDragCancel {
619                session_id,
620                browsing_context_id: browsing_context_id.into(),
621            },
622            BrowserCommand::SendExternalDragEnter { event } => Self::SendExternalDragEnter {
623                event: event.into(),
624            },
625            BrowserCommand::SendExternalDragUpdate { event } => Self::SendExternalDragUpdate {
626                event: event.into(),
627            },
628            BrowserCommand::SendExternalDragLeave {
629                browsing_context_id,
630            } => Self::SendExternalDragLeave {
631                browsing_context_id: browsing_context_id.into(),
632            },
633            BrowserCommand::SendExternalDragDrop { event } => Self::SendExternalDragDrop {
634                event: event.into(),
635            },
636            BrowserCommand::SetComposition { composition } => Self::SetImeComposition {
637                composition: composition.into(),
638            },
639            BrowserCommand::CommitText { commit } => Self::CommitImeText {
640                commit: commit.into(),
641            },
642            BrowserCommand::SetTransientComposition { composition } => {
643                Self::SetExtensionPopupComposition {
644                    composition: composition.into(),
645                }
646            }
647            BrowserCommand::CommitTransientText { commit } => Self::CommitExtensionPopupText {
648                commit: commit.into(),
649            },
650            BrowserCommand::FinishComposingText {
651                browsing_context_id,
652                behavior,
653            } => Self::FinishComposingText {
654                browsing_context_id: browsing_context_id.into(),
655                behavior: behavior.into(),
656            },
657            BrowserCommand::FinishComposingTextInTransientBrowsingContext {
658                transient_browsing_context_id,
659                behavior,
660            } => Self::FinishExtensionPopupComposingText {
661                popup_id: transient_browsing_context_id.into(),
662                behavior: behavior.into(),
663            },
664            BrowserCommand::ExecuteContextMenuCommand {
665                menu_id,
666                command_id,
667                event_flags,
668            } => Self::ExecuteContextMenuCommand {
669                menu_id,
670                command_id,
671                event_flags,
672            },
673            BrowserCommand::AcceptChoiceMenuSelection {
674                request_id,
675                indices,
676            } => Self::AcceptChoiceMenuSelection {
677                request_id,
678                indices,
679            },
680            BrowserCommand::DismissChoiceMenu { request_id } => {
681                Self::DismissChoiceMenu { request_id }
682            }
683            BrowserCommand::DismissContextMenu { menu_id } => Self::DismissContextMenu { menu_id },
684            BrowserCommand::PauseDownload { download_id } => Self::PauseDownload {
685                download_id: download_id.into(),
686            },
687            BrowserCommand::ResumeDownload { download_id } => Self::ResumeDownload {
688                download_id: download_id.into(),
689            },
690            BrowserCommand::CancelDownload { download_id } => Self::CancelDownload {
691                download_id: download_id.into(),
692            },
693            BrowserCommand::ListExtensions { profile_id } => Self::ListExtensions { profile_id },
694            BrowserCommand::OpenDefaultAuxiliaryWindow {
695                profile_id,
696                request_id,
697            } => Self::OpenDefaultPromptUi {
698                profile_id,
699                request_id,
700            },
701            BrowserCommand::RespondAuxiliaryWindow {
702                profile_id,
703                request_id,
704                response,
705            } => match ChromeAuxiliaryWindowResponse::from(response) {
706                ChromeAuxiliaryWindowResponse::PermissionPrompt { allow } => {
707                    Self::RespondPromptUi {
708                        profile_id,
709                        request_id,
710                        response: PromptUiResponse::PermissionPrompt { allow },
711                    }
712                }
713                ChromeAuxiliaryWindowResponse::DownloadPrompt {
714                    allow,
715                    destination_path,
716                } => Self::RespondPromptUi {
717                    profile_id,
718                    request_id,
719                    response: PromptUiResponse::DownloadPrompt {
720                        allow,
721                        destination_path,
722                    },
723                },
724                ChromeAuxiliaryWindowResponse::ExtensionInstallPrompt { proceed } => {
725                    Self::RespondPromptUi {
726                        profile_id,
727                        request_id,
728                        response: PromptUiResponse::ExtensionInstallPrompt { proceed },
729                    }
730                }
731                ChromeAuxiliaryWindowResponse::ExtensionUninstallPrompt {
732                    proceed,
733                    report_abuse,
734                } => Self::RespondPromptUi {
735                    profile_id,
736                    request_id,
737                    response: PromptUiResponse::ExtensionUninstallPrompt {
738                        proceed,
739                        report_abuse,
740                    },
741                },
742                ChromeAuxiliaryWindowResponse::FormResubmissionPrompt { proceed } => {
743                    Self::RespondPromptUi {
744                        profile_id,
745                        request_id,
746                        response: PromptUiResponse::FormResubmissionPrompt { proceed },
747                    }
748                }
749                ChromeAuxiliaryWindowResponse::Unknown => Self::RespondPromptUi {
750                    profile_id,
751                    request_id,
752                    response: PromptUiResponse::Unknown,
753                },
754            },
755            BrowserCommand::CloseAuxiliaryWindow {
756                profile_id,
757                window_id,
758            } => Self::ClosePromptUi {
759                profile_id,
760                prompt_ui_id: PromptUiId::new(window_id.get()),
761            },
762            BrowserCommand::RespondBrowsingContextOpen {
763                request_id,
764                response,
765            } => Self::RespondTabOpen {
766                request_id,
767                response: response.into(),
768            },
769            BrowserCommand::RespondWindowOpen {
770                request_id,
771                response,
772            } => Self::RespondWindowOpen {
773                request_id,
774                response,
775            },
776        }
777    }
778}
779
780#[cfg(test)]
781mod tests {
782    use cbf::{
783        command::BrowserCommand,
784        data::{
785            auxiliary_window::{AuxiliaryWindowId, AuxiliaryWindowResponse},
786            background::BackgroundPolicy,
787            edit::EditAction,
788            execution::BrowsingContextExecutionState,
789            ids::{BrowsingContextId, TransientBrowsingContextId},
790            policy::{BrowsingContextPolicy, CapabilityPolicy, IpcPolicy},
791            visibility::BrowsingContextVisibility,
792        },
793    };
794
795    use super::ChromeCommand;
796    use crate::data::{
797        background::ChromeBackgroundPolicy,
798        execution::ChromeTabExecutionState,
799        find::{ChromeFindInPageOptions, ChromeStopFindAction},
800        ids::{PopupId, TabId},
801        policy::{ChromeBrowsingContextPolicy, ChromeCapabilityPolicy, ChromeIpcPolicy},
802        prompt_ui::{PromptUiId, PromptUiResponse},
803        visibility::ChromeTabVisibility,
804    };
805
806    #[test]
807    fn create_close_command_converts_browsing_context_id_into_tab_id() {
808        let command = BrowserCommand::RequestCloseBrowsingContext {
809            browsing_context_id: BrowsingContextId::new(42),
810        };
811
812        let raw: ChromeCommand = command.into();
813        assert!(matches!(
814            raw,
815            ChromeCommand::RequestCloseTab {
816                browsing_context_id
817            } if browsing_context_id == TabId::new(42)
818        ));
819    }
820
821    #[test]
822    fn confirm_permission_maps_to_prompt_ui_response() {
823        let command = BrowserCommand::ConfirmPermission {
824            browsing_context_id: BrowsingContextId::new(9),
825            request_id: 77,
826            allow: true,
827        };
828
829        let raw: ChromeCommand = command.into();
830        assert!(matches!(
831            raw,
832            ChromeCommand::ConfirmPermission {
833                browsing_context_id,
834                request_id,
835                allow: true,
836            } if browsing_context_id == TabId::new(9) && request_id == 77
837        ));
838    }
839
840    #[test]
841    fn close_transaction_commands_map_browsing_context_ids_into_tab_ids() {
842        let begin = BrowserCommand::BeginCloseBrowsingContextsTransaction {
843            request_id: 4,
844            browsing_context_ids: vec![BrowsingContextId::new(3), BrowsingContextId::new(8)],
845        };
846        let commit = BrowserCommand::CommitCloseBrowsingContextsTransaction { request_id: 4 };
847        let cancel = BrowserCommand::CancelCloseBrowsingContextsTransaction { request_id: 4 };
848
849        let begin_raw: ChromeCommand = begin.into();
850        let commit_raw: ChromeCommand = commit.into();
851        let cancel_raw: ChromeCommand = cancel.into();
852
853        assert!(matches!(
854            begin_raw,
855            ChromeCommand::BeginCloseTabsTransaction {
856                request_id: 4,
857                browsing_context_ids,
858            } if browsing_context_ids == vec![TabId::new(3), TabId::new(8)]
859        ));
860        assert!(matches!(
861            commit_raw,
862            ChromeCommand::CommitCloseTabsTransaction { request_id: 4 }
863        ));
864        assert!(matches!(
865            cancel_raw,
866            ChromeCommand::CancelCloseTabsTransaction { request_id: 4 }
867        ));
868    }
869
870    #[test]
871    fn permission_auxiliary_response_maps_to_prompt_ui_response() {
872        let command = BrowserCommand::RespondAuxiliaryWindow {
873            profile_id: "profile-a".to_string(),
874            request_id: 81,
875            response: AuxiliaryWindowResponse::PermissionPrompt { allow: false },
876        };
877
878        let raw: ChromeCommand = command.into();
879        assert!(matches!(
880            raw,
881            ChromeCommand::RespondPromptUi {
882                profile_id,
883                request_id,
884                response: PromptUiResponse::PermissionPrompt { allow: false },
885            } if profile_id == "profile-a" && request_id == 81
886        ));
887    }
888
889    #[test]
890    fn extension_auxiliary_response_maps_to_prompt_ui_response() {
891        let command = BrowserCommand::RespondAuxiliaryWindow {
892            profile_id: "profile-b".to_string(),
893            request_id: 82,
894            response: AuxiliaryWindowResponse::ExtensionInstallPrompt { proceed: true },
895        };
896
897        let raw: ChromeCommand = command.into();
898        assert!(matches!(
899            raw,
900            ChromeCommand::RespondPromptUi {
901                profile_id,
902                request_id,
903                response: PromptUiResponse::ExtensionInstallPrompt { proceed: true },
904            } if profile_id == "profile-b" && request_id == 82
905        ));
906    }
907
908    #[test]
909    fn uninstall_auxiliary_response_maps_to_prompt_ui_response() {
910        let command = BrowserCommand::RespondAuxiliaryWindow {
911            profile_id: "profile-b".to_string(),
912            request_id: 83,
913            response: AuxiliaryWindowResponse::ExtensionUninstallPrompt {
914                proceed: true,
915                report_abuse: true,
916            },
917        };
918
919        let raw: ChromeCommand = command.into();
920        assert!(matches!(
921            raw,
922            ChromeCommand::RespondPromptUi {
923                profile_id,
924                request_id,
925                response: PromptUiResponse::ExtensionUninstallPrompt {
926                    proceed: true,
927                    report_abuse: true,
928                },
929            } if profile_id == "profile-b" && request_id == 83
930        ));
931    }
932
933    #[test]
934    fn close_auxiliary_window_maps_to_prompt_ui_close() {
935        let command = BrowserCommand::CloseAuxiliaryWindow {
936            profile_id: "profile-c".to_string(),
937            window_id: AuxiliaryWindowId::new(33),
938        };
939
940        let raw: ChromeCommand = command.into();
941        assert!(matches!(
942            raw,
943            ChromeCommand::ClosePromptUi {
944                profile_id,
945                prompt_ui_id,
946            } if profile_id == "profile-c" && prompt_ui_id == PromptUiId::new(33)
947        ));
948    }
949
950    #[test]
951    fn transient_close_command_maps_to_extension_popup_close() {
952        let command = BrowserCommand::CloseTransientBrowsingContext {
953            transient_browsing_context_id: TransientBrowsingContextId::new(99),
954        };
955
956        let raw: ChromeCommand = command.into();
957        assert!(matches!(
958            raw,
959            ChromeCommand::CloseExtensionPopup { popup_id }
960                if popup_id == PopupId::new(99)
961        ));
962    }
963
964    #[test]
965    fn edit_action_command_maps_to_chrome_edit_action() {
966        let command = BrowserCommand::ExecuteEditAction {
967            browsing_context_id: BrowsingContextId::new(11),
968            action: EditAction::Paste,
969        };
970
971        let raw: ChromeCommand = command.into();
972        assert!(matches!(
973            raw,
974            ChromeCommand::ExecuteEditAction {
975                browsing_context_id,
976                action: EditAction::Paste,
977            } if browsing_context_id == TabId::new(11)
978        ));
979    }
980
981    #[test]
982    fn transient_edit_action_command_maps_to_extension_popup_edit_action() {
983        let command = BrowserCommand::ExecuteEditActionInTransientBrowsingContext {
984            transient_browsing_context_id: TransientBrowsingContextId::new(12),
985            action: EditAction::SelectAll,
986        };
987
988        let raw: ChromeCommand = command.into();
989        assert!(matches!(
990            raw,
991            ChromeCommand::ExecuteExtensionPopupEditAction {
992                popup_id,
993                action: EditAction::SelectAll,
994            } if popup_id == PopupId::new(12)
995        ));
996    }
997
998    #[test]
999    fn find_in_page_command_preserves_find_options() {
1000        let raw = ChromeCommand::FindInPage {
1001            browsing_context_id: TabId::new(31),
1002            request_id: 14,
1003            options: ChromeFindInPageOptions {
1004                query: "needle".into(),
1005                forward: false,
1006                match_case: true,
1007                new_session: false,
1008                find_match: true,
1009            },
1010        };
1011
1012        assert!(matches!(
1013            raw,
1014            ChromeCommand::FindInPage {
1015                browsing_context_id,
1016                request_id,
1017                options,
1018            } if browsing_context_id == TabId::new(31)
1019                && request_id == 14
1020                && options.query == "needle"
1021                && !options.forward
1022                && options.match_case
1023                && !options.new_session
1024                && options.find_match
1025        ));
1026    }
1027
1028    #[test]
1029    fn stop_finding_command_preserves_action() {
1030        let raw = ChromeCommand::StopFinding {
1031            browsing_context_id: TabId::new(41),
1032            action: ChromeStopFindAction::ActivateSelection,
1033        };
1034
1035        assert!(matches!(
1036            raw,
1037            ChromeCommand::StopFinding {
1038                browsing_context_id,
1039                action: ChromeStopFindAction::ActivateSelection,
1040            } if browsing_context_id == TabId::new(41)
1041        ));
1042    }
1043
1044    #[test]
1045    fn set_visibility_command_converts_browsing_context_id_into_tab_id() {
1046        let command = BrowserCommand::SetBrowsingContextVisibility {
1047            browsing_context_id: BrowsingContextId::new(24),
1048            visibility: BrowsingContextVisibility::Hidden,
1049        };
1050
1051        let raw: ChromeCommand = command.into();
1052        assert!(matches!(
1053            raw,
1054            ChromeCommand::SetTabVisibility {
1055                browsing_context_id,
1056                visibility: ChromeTabVisibility::Hidden,
1057            } if browsing_context_id == TabId::new(24)
1058        ));
1059    }
1060
1061    #[test]
1062    fn set_execution_state_command_converts_browsing_context_id_into_tab_id() {
1063        let command = BrowserCommand::SetBrowsingContextExecutionState {
1064            browsing_context_id: BrowsingContextId::new(24),
1065            state: BrowsingContextExecutionState::Suspended,
1066        };
1067
1068        let raw: ChromeCommand = command.into();
1069        assert!(matches!(
1070            raw,
1071            ChromeCommand::SetTabExecutionState {
1072                browsing_context_id,
1073                state: ChromeTabExecutionState::Suspended,
1074            } if browsing_context_id == TabId::new(24)
1075        ));
1076    }
1077
1078    #[test]
1079    fn set_background_policy_command_converts_browsing_context_id_into_tab_id() {
1080        let command = BrowserCommand::SetBrowsingContextBackgroundPolicy {
1081            browsing_context_id: BrowsingContextId::new(25),
1082            policy: BackgroundPolicy::Transparent,
1083        };
1084
1085        let raw: ChromeCommand = command.into();
1086        assert!(matches!(
1087            raw,
1088            ChromeCommand::SetTabBackgroundPolicy {
1089                browsing_context_id,
1090                policy: ChromeBackgroundPolicy::Transparent,
1091            } if browsing_context_id == TabId::new(25)
1092        ));
1093    }
1094
1095    #[test]
1096    fn transient_background_policy_command_maps_to_extension_popup_policy() {
1097        let command = BrowserCommand::SetTransientBrowsingContextBackgroundPolicy {
1098            transient_browsing_context_id: TransientBrowsingContextId::new(26),
1099            policy: BackgroundPolicy::Opaque,
1100        };
1101
1102        let raw: ChromeCommand = command.into();
1103        assert!(matches!(
1104            raw,
1105            ChromeCommand::SetExtensionPopupBackgroundPolicy {
1106                popup_id,
1107                policy: ChromeBackgroundPolicy::Opaque,
1108            } if popup_id == PopupId::new(26)
1109        ));
1110    }
1111
1112    #[test]
1113    fn create_browsing_context_policy_converts_to_chrome_policy() {
1114        let command = BrowserCommand::CreateBrowsingContext {
1115            request_id: 91,
1116            initial_url: Some("app://simpleapp/ui.html".to_string()),
1117            profile_id: "default".to_string(),
1118            policy: Some(BrowsingContextPolicy {
1119                ipc: IpcPolicy::Allow {
1120                    allowed_origins: vec!["app://simpleapp".to_string()],
1121                },
1122                extensions: CapabilityPolicy::Deny,
1123            }),
1124        };
1125
1126        let raw: ChromeCommand = command.into();
1127
1128        assert!(matches!(
1129            raw,
1130            ChromeCommand::CreateTab {
1131                request_id,
1132                initial_url,
1133                profile_id,
1134                policy: Some(ChromeBrowsingContextPolicy {
1135                    ipc: ChromeIpcPolicy::Allow { allowed_origins },
1136                    extensions: ChromeCapabilityPolicy::Deny,
1137                }),
1138            } if request_id == 91
1139                && initial_url.as_deref() == Some("app://simpleapp/ui.html")
1140                && profile_id == "default"
1141                && allowed_origins == vec!["app://simpleapp".to_string()]
1142        ));
1143    }
1144}