1use cbf::data::dialog::DialogResponse;
2use cbf::{
3 command::{BrowserCommand, BrowserOperation},
4 data::edit::EditAction,
5};
6
7use crate::data::{
8 browsing_context_open::ChromeBrowsingContextOpenResponse,
9 download::ChromeDownloadId,
10 drag::{ChromeDragDrop, ChromeDragUpdate},
11 extension::ChromeAuxiliaryWindowResponse,
12 ids::{PopupId, TabId},
13 ime::{
14 ChromeConfirmCompositionBehavior, ChromeImeCommitText, ChromeImeComposition,
15 ChromeTransientImeCommitText, ChromeTransientImeComposition,
16 },
17 input::{ChromeKeyEvent, ChromeMouseWheelEvent},
18 mouse::ChromeMouseEvent,
19 prompt_ui::{PromptUiId, PromptUiResponse},
20 window_open::ChromeWindowOpenResponse,
21};
22
23#[derive(Debug, Clone, PartialEq)]
30pub enum ChromeCommand {
31 RequestShutdown {
32 request_id: u64,
33 },
34 ConfirmShutdown {
35 request_id: u64,
36 proceed: bool,
37 },
38 ForceShutdown,
39 ConfirmBeforeUnload {
40 browsing_context_id: TabId,
41 request_id: u64,
42 proceed: bool,
43 },
44 RespondJavaScriptDialog {
45 browsing_context_id: TabId,
46 request_id: u64,
47 response: DialogResponse,
48 },
49 RespondExtensionPopupJavaScriptDialog {
50 popup_id: PopupId,
51 request_id: u64,
52 response: DialogResponse,
53 },
54 ConfirmPermission {
55 browsing_context_id: TabId,
56 request_id: u64,
57 allow: bool,
58 },
59 CreateTab {
60 request_id: u64,
61 initial_url: Option<String>,
62 profile_id: String,
63 },
64 ListProfiles,
65 RequestCloseTab {
66 browsing_context_id: TabId,
67 },
68 SetTabSize {
69 browsing_context_id: TabId,
70 width: u32,
71 height: u32,
72 },
73 Navigate {
74 browsing_context_id: TabId,
75 url: String,
76 },
77 GoBack {
78 browsing_context_id: TabId,
79 },
80 GoForward {
81 browsing_context_id: TabId,
82 },
83 Reload {
84 browsing_context_id: TabId,
85 ignore_cache: bool,
86 },
87 PrintPreview {
88 browsing_context_id: TabId,
89 },
90 OpenDevTools {
91 browsing_context_id: TabId,
92 },
93 InspectElement {
94 browsing_context_id: TabId,
95 x: i32,
96 y: i32,
97 },
98 GetTabDomHtml {
99 browsing_context_id: TabId,
100 request_id: u64,
101 },
102 SetTabFocus {
103 browsing_context_id: TabId,
104 focused: bool,
105 },
106 SendKeyEvent {
107 browsing_context_id: TabId,
108 event: ChromeKeyEvent,
109 commands: Vec<String>,
110 },
111 ExecuteEditAction {
112 browsing_context_id: TabId,
113 action: EditAction,
114 },
115 SendMouseEvent {
116 browsing_context_id: TabId,
117 event: ChromeMouseEvent,
118 },
119 SendMouseWheelEvent {
120 browsing_context_id: TabId,
121 event: ChromeMouseWheelEvent,
122 },
123 SendDragUpdate {
124 update: ChromeDragUpdate,
125 },
126 SendDragDrop {
127 drop: ChromeDragDrop,
128 },
129 SendDragCancel {
130 session_id: u64,
131 browsing_context_id: TabId,
132 },
133 SetImeComposition {
134 composition: ChromeImeComposition,
135 },
136 CommitImeText {
137 commit: ChromeImeCommitText,
138 },
139 FinishComposingText {
140 browsing_context_id: TabId,
141 behavior: ChromeConfirmCompositionBehavior,
142 },
143 ExecuteContextMenuCommand {
144 menu_id: u64,
145 command_id: i32,
146 event_flags: i32,
147 },
148 AcceptChoiceMenuSelection {
149 request_id: u64,
150 indices: Vec<i32>,
151 },
152 DismissChoiceMenu {
153 request_id: u64,
154 },
155 DismissContextMenu {
156 menu_id: u64,
157 },
158 PauseDownload {
159 download_id: ChromeDownloadId,
160 },
161 ResumeDownload {
162 download_id: ChromeDownloadId,
163 },
164 CancelDownload {
165 download_id: ChromeDownloadId,
166 },
167 ListExtensions {
168 profile_id: String,
169 },
170 ActivateExtensionAction {
171 browsing_context_id: TabId,
172 extension_id: String,
173 },
174 CloseExtensionPopup {
175 popup_id: PopupId,
176 },
177 SetExtensionPopupSize {
178 popup_id: PopupId,
179 width: u32,
180 height: u32,
181 },
182 SetExtensionPopupFocus {
183 popup_id: PopupId,
184 focused: bool,
185 },
186 SendExtensionPopupKeyEvent {
187 popup_id: PopupId,
188 event: ChromeKeyEvent,
189 commands: Vec<String>,
190 },
191 ExecuteExtensionPopupEditAction {
192 popup_id: PopupId,
193 action: EditAction,
194 },
195 SendExtensionPopupMouseEvent {
196 popup_id: PopupId,
197 event: ChromeMouseEvent,
198 },
199 SendExtensionPopupMouseWheelEvent {
200 popup_id: PopupId,
201 event: ChromeMouseWheelEvent,
202 },
203 SetExtensionPopupComposition {
204 composition: ChromeTransientImeComposition,
205 },
206 CommitExtensionPopupText {
207 commit: ChromeTransientImeCommitText,
208 },
209 FinishExtensionPopupComposingText {
210 popup_id: PopupId,
211 behavior: ChromeConfirmCompositionBehavior,
212 },
213 OpenDefaultPromptUi {
214 profile_id: String,
215 request_id: u64,
216 },
217 RespondPromptUi {
218 profile_id: String,
219 request_id: u64,
220 response: PromptUiResponse,
221 },
222 ClosePromptUi {
223 profile_id: String,
224 prompt_ui_id: PromptUiId,
225 },
226 RespondTabOpen {
227 request_id: u64,
228 response: ChromeBrowsingContextOpenResponse,
229 },
230 RespondWindowOpen {
231 request_id: u64,
232 response: ChromeWindowOpenResponse,
233 },
234 UnsupportedGenericCommand {
235 operation: BrowserOperation,
236 },
237}
238
239impl From<BrowserCommand> for ChromeCommand {
240 fn from(value: BrowserCommand) -> Self {
241 match value {
242 BrowserCommand::Shutdown { request_id } => Self::RequestShutdown { request_id },
243 BrowserCommand::ConfirmShutdown {
244 request_id,
245 proceed,
246 } => Self::ConfirmShutdown {
247 request_id,
248 proceed,
249 },
250 BrowserCommand::ForceShutdown => Self::ForceShutdown,
251 BrowserCommand::ConfirmBeforeUnload {
252 browsing_context_id,
253 request_id,
254 proceed,
255 } => Self::ConfirmBeforeUnload {
256 browsing_context_id: browsing_context_id.into(),
257 request_id,
258 proceed,
259 },
260 BrowserCommand::RespondJavaScriptDialog {
261 browsing_context_id,
262 request_id,
263 response,
264 } => Self::RespondJavaScriptDialog {
265 browsing_context_id: browsing_context_id.into(),
266 request_id,
267 response,
268 },
269 BrowserCommand::RespondJavaScriptDialogInTransientBrowsingContext {
270 transient_browsing_context_id,
271 request_id,
272 response,
273 } => Self::RespondExtensionPopupJavaScriptDialog {
274 popup_id: transient_browsing_context_id.into(),
275 request_id,
276 response,
277 },
278 BrowserCommand::ConfirmPermission {
279 browsing_context_id,
280 request_id,
281 allow,
282 } => Self::ConfirmPermission {
283 browsing_context_id: browsing_context_id.into(),
284 request_id,
285 allow,
286 },
287 BrowserCommand::CreateBrowsingContext {
288 request_id,
289 initial_url,
290 profile_id,
291 } => Self::CreateTab {
292 request_id,
293 initial_url,
294 profile_id,
295 },
296 BrowserCommand::ListProfiles => Self::ListProfiles,
297 BrowserCommand::RequestCloseBrowsingContext {
298 browsing_context_id,
299 } => Self::RequestCloseTab {
300 browsing_context_id: browsing_context_id.into(),
301 },
302 BrowserCommand::CloseTransientBrowsingContext {
303 transient_browsing_context_id,
304 } => Self::CloseExtensionPopup {
305 popup_id: transient_browsing_context_id.into(),
306 },
307 BrowserCommand::ResizeBrowsingContext {
308 browsing_context_id,
309 width,
310 height,
311 } => Self::SetTabSize {
312 browsing_context_id: browsing_context_id.into(),
313 width,
314 height,
315 },
316 BrowserCommand::ResizeTransientBrowsingContext {
317 transient_browsing_context_id,
318 width,
319 height,
320 } => Self::SetExtensionPopupSize {
321 popup_id: transient_browsing_context_id.into(),
322 width,
323 height,
324 },
325 BrowserCommand::Navigate {
326 browsing_context_id,
327 url,
328 } => Self::Navigate {
329 browsing_context_id: browsing_context_id.into(),
330 url,
331 },
332 BrowserCommand::GoBack {
333 browsing_context_id,
334 } => Self::GoBack {
335 browsing_context_id: browsing_context_id.into(),
336 },
337 BrowserCommand::GoForward {
338 browsing_context_id,
339 } => Self::GoForward {
340 browsing_context_id: browsing_context_id.into(),
341 },
342 BrowserCommand::Reload {
343 browsing_context_id,
344 ignore_cache,
345 } => Self::Reload {
346 browsing_context_id: browsing_context_id.into(),
347 ignore_cache,
348 },
349 BrowserCommand::PrintPreview {
350 browsing_context_id,
351 } => Self::PrintPreview {
352 browsing_context_id: browsing_context_id.into(),
353 },
354 BrowserCommand::GetBrowsingContextDomHtml {
355 browsing_context_id,
356 request_id,
357 } => Self::GetTabDomHtml {
358 browsing_context_id: browsing_context_id.into(),
359 request_id,
360 },
361 BrowserCommand::SetBrowsingContextFocus {
362 browsing_context_id,
363 focused,
364 } => Self::SetTabFocus {
365 browsing_context_id: browsing_context_id.into(),
366 focused,
367 },
368 BrowserCommand::SetTransientBrowsingContextFocus {
369 transient_browsing_context_id,
370 focused,
371 } => Self::SetExtensionPopupFocus {
372 popup_id: transient_browsing_context_id.into(),
373 focused,
374 },
375 BrowserCommand::SendKeyEvent {
376 browsing_context_id,
377 event,
378 commands,
379 } => Self::SendKeyEvent {
380 browsing_context_id: browsing_context_id.into(),
381 event: event.into(),
382 commands,
383 },
384 BrowserCommand::ExecuteEditAction {
385 browsing_context_id,
386 action,
387 } => Self::ExecuteEditAction {
388 browsing_context_id: browsing_context_id.into(),
389 action,
390 },
391 BrowserCommand::SendKeyEventToTransientBrowsingContext {
392 transient_browsing_context_id,
393 event,
394 commands,
395 } => Self::SendExtensionPopupKeyEvent {
396 popup_id: transient_browsing_context_id.into(),
397 event: event.into(),
398 commands,
399 },
400 BrowserCommand::ExecuteEditActionInTransientBrowsingContext {
401 transient_browsing_context_id,
402 action,
403 } => Self::ExecuteExtensionPopupEditAction {
404 popup_id: transient_browsing_context_id.into(),
405 action,
406 },
407 BrowserCommand::SendMouseEvent {
408 browsing_context_id,
409 event,
410 } => Self::SendMouseEvent {
411 browsing_context_id: browsing_context_id.into(),
412 event: event.into(),
413 },
414 BrowserCommand::SendMouseEventToTransientBrowsingContext {
415 transient_browsing_context_id,
416 event,
417 } => Self::SendExtensionPopupMouseEvent {
418 popup_id: transient_browsing_context_id.into(),
419 event: event.into(),
420 },
421 BrowserCommand::SendMouseWheelEvent {
422 browsing_context_id,
423 event,
424 } => Self::SendMouseWheelEvent {
425 browsing_context_id: browsing_context_id.into(),
426 event: event.into(),
427 },
428 BrowserCommand::SendMouseWheelEventToTransientBrowsingContext {
429 transient_browsing_context_id,
430 event,
431 } => Self::SendExtensionPopupMouseWheelEvent {
432 popup_id: transient_browsing_context_id.into(),
433 event: event.into(),
434 },
435 BrowserCommand::SendDragUpdate { update } => Self::SendDragUpdate {
436 update: update.into(),
437 },
438 BrowserCommand::SendDragDrop { drop } => Self::SendDragDrop { drop: drop.into() },
439 BrowserCommand::SendDragCancel {
440 session_id,
441 browsing_context_id,
442 } => Self::SendDragCancel {
443 session_id,
444 browsing_context_id: browsing_context_id.into(),
445 },
446 BrowserCommand::SetComposition { composition } => Self::SetImeComposition {
447 composition: composition.into(),
448 },
449 BrowserCommand::CommitText { commit } => Self::CommitImeText {
450 commit: commit.into(),
451 },
452 BrowserCommand::SetTransientComposition { composition } => {
453 Self::SetExtensionPopupComposition {
454 composition: composition.into(),
455 }
456 }
457 BrowserCommand::CommitTransientText { commit } => Self::CommitExtensionPopupText {
458 commit: commit.into(),
459 },
460 BrowserCommand::FinishComposingText {
461 browsing_context_id,
462 behavior,
463 } => Self::FinishComposingText {
464 browsing_context_id: browsing_context_id.into(),
465 behavior: behavior.into(),
466 },
467 BrowserCommand::FinishComposingTextInTransientBrowsingContext {
468 transient_browsing_context_id,
469 behavior,
470 } => Self::FinishExtensionPopupComposingText {
471 popup_id: transient_browsing_context_id.into(),
472 behavior: behavior.into(),
473 },
474 BrowserCommand::ExecuteContextMenuCommand {
475 menu_id,
476 command_id,
477 event_flags,
478 } => Self::ExecuteContextMenuCommand {
479 menu_id,
480 command_id,
481 event_flags,
482 },
483 BrowserCommand::AcceptChoiceMenuSelection {
484 request_id,
485 indices,
486 } => Self::AcceptChoiceMenuSelection {
487 request_id,
488 indices,
489 },
490 BrowserCommand::DismissChoiceMenu { request_id } => {
491 Self::DismissChoiceMenu { request_id }
492 }
493 BrowserCommand::DismissContextMenu { menu_id } => Self::DismissContextMenu { menu_id },
494 BrowserCommand::PauseDownload { download_id } => Self::PauseDownload {
495 download_id: download_id.into(),
496 },
497 BrowserCommand::ResumeDownload { download_id } => Self::ResumeDownload {
498 download_id: download_id.into(),
499 },
500 BrowserCommand::CancelDownload { download_id } => Self::CancelDownload {
501 download_id: download_id.into(),
502 },
503 BrowserCommand::ListExtensions { profile_id } => Self::ListExtensions { profile_id },
504 BrowserCommand::OpenDefaultAuxiliaryWindow {
505 profile_id,
506 request_id,
507 } => Self::OpenDefaultPromptUi {
508 profile_id,
509 request_id,
510 },
511 BrowserCommand::RespondAuxiliaryWindow {
512 profile_id,
513 request_id,
514 response,
515 } => match ChromeAuxiliaryWindowResponse::from(response) {
516 ChromeAuxiliaryWindowResponse::PermissionPrompt { allow } => {
517 Self::RespondPromptUi {
518 profile_id,
519 request_id,
520 response: PromptUiResponse::PermissionPrompt { allow },
521 }
522 }
523 ChromeAuxiliaryWindowResponse::DownloadPrompt {
524 allow,
525 destination_path,
526 } => Self::RespondPromptUi {
527 profile_id,
528 request_id,
529 response: PromptUiResponse::DownloadPrompt {
530 allow,
531 destination_path,
532 },
533 },
534 ChromeAuxiliaryWindowResponse::ExtensionInstallPrompt { proceed } => {
535 Self::RespondPromptUi {
536 profile_id,
537 request_id,
538 response: PromptUiResponse::ExtensionInstallPrompt { proceed },
539 }
540 }
541 ChromeAuxiliaryWindowResponse::ExtensionUninstallPrompt {
542 proceed,
543 report_abuse,
544 } => Self::RespondPromptUi {
545 profile_id,
546 request_id,
547 response: PromptUiResponse::ExtensionUninstallPrompt {
548 proceed,
549 report_abuse,
550 },
551 },
552 ChromeAuxiliaryWindowResponse::Unknown => Self::RespondPromptUi {
553 profile_id,
554 request_id,
555 response: PromptUiResponse::Unknown,
556 },
557 },
558 BrowserCommand::CloseAuxiliaryWindow {
559 profile_id,
560 window_id,
561 } => Self::ClosePromptUi {
562 profile_id,
563 prompt_ui_id: PromptUiId::new(window_id.get()),
564 },
565 BrowserCommand::RespondBrowsingContextOpen {
566 request_id,
567 response,
568 } => Self::RespondTabOpen {
569 request_id,
570 response: response.into(),
571 },
572 BrowserCommand::RespondWindowOpen {
573 request_id,
574 response,
575 } => Self::RespondWindowOpen {
576 request_id,
577 response,
578 },
579 }
580 }
581}
582
583#[cfg(test)]
584mod tests {
585 use cbf::{
586 command::BrowserCommand,
587 data::{
588 auxiliary_window::{AuxiliaryWindowId, AuxiliaryWindowResponse},
589 edit::EditAction,
590 ids::{BrowsingContextId, TransientBrowsingContextId},
591 },
592 };
593
594 use super::ChromeCommand;
595 use crate::data::{
596 ids::{PopupId, TabId},
597 prompt_ui::{PromptUiId, PromptUiResponse},
598 };
599
600 #[test]
601 fn create_close_command_converts_browsing_context_id_into_tab_id() {
602 let command = BrowserCommand::RequestCloseBrowsingContext {
603 browsing_context_id: BrowsingContextId::new(42),
604 };
605
606 let raw: ChromeCommand = command.into();
607 assert!(matches!(
608 raw,
609 ChromeCommand::RequestCloseTab {
610 browsing_context_id
611 } if browsing_context_id == TabId::new(42)
612 ));
613 }
614
615 #[test]
616 fn confirm_permission_maps_to_prompt_ui_response() {
617 let command = BrowserCommand::ConfirmPermission {
618 browsing_context_id: BrowsingContextId::new(9),
619 request_id: 77,
620 allow: true,
621 };
622
623 let raw: ChromeCommand = command.into();
624 assert!(matches!(
625 raw,
626 ChromeCommand::ConfirmPermission {
627 browsing_context_id,
628 request_id,
629 allow: true,
630 } if browsing_context_id == TabId::new(9) && request_id == 77
631 ));
632 }
633
634 #[test]
635 fn permission_auxiliary_response_maps_to_prompt_ui_response() {
636 let command = BrowserCommand::RespondAuxiliaryWindow {
637 profile_id: "profile-a".to_string(),
638 request_id: 81,
639 response: AuxiliaryWindowResponse::PermissionPrompt { allow: false },
640 };
641
642 let raw: ChromeCommand = command.into();
643 assert!(matches!(
644 raw,
645 ChromeCommand::RespondPromptUi {
646 profile_id,
647 request_id,
648 response: PromptUiResponse::PermissionPrompt { allow: false },
649 } if profile_id == "profile-a" && request_id == 81
650 ));
651 }
652
653 #[test]
654 fn extension_auxiliary_response_maps_to_prompt_ui_response() {
655 let command = BrowserCommand::RespondAuxiliaryWindow {
656 profile_id: "profile-b".to_string(),
657 request_id: 82,
658 response: AuxiliaryWindowResponse::ExtensionInstallPrompt { proceed: true },
659 };
660
661 let raw: ChromeCommand = command.into();
662 assert!(matches!(
663 raw,
664 ChromeCommand::RespondPromptUi {
665 profile_id,
666 request_id,
667 response: PromptUiResponse::ExtensionInstallPrompt { proceed: true },
668 } if profile_id == "profile-b" && request_id == 82
669 ));
670 }
671
672 #[test]
673 fn uninstall_auxiliary_response_maps_to_prompt_ui_response() {
674 let command = BrowserCommand::RespondAuxiliaryWindow {
675 profile_id: "profile-b".to_string(),
676 request_id: 83,
677 response: AuxiliaryWindowResponse::ExtensionUninstallPrompt {
678 proceed: true,
679 report_abuse: true,
680 },
681 };
682
683 let raw: ChromeCommand = command.into();
684 assert!(matches!(
685 raw,
686 ChromeCommand::RespondPromptUi {
687 profile_id,
688 request_id,
689 response: PromptUiResponse::ExtensionUninstallPrompt {
690 proceed: true,
691 report_abuse: true,
692 },
693 } if profile_id == "profile-b" && request_id == 83
694 ));
695 }
696
697 #[test]
698 fn close_auxiliary_window_maps_to_prompt_ui_close() {
699 let command = BrowserCommand::CloseAuxiliaryWindow {
700 profile_id: "profile-c".to_string(),
701 window_id: AuxiliaryWindowId::new(33),
702 };
703
704 let raw: ChromeCommand = command.into();
705 assert!(matches!(
706 raw,
707 ChromeCommand::ClosePromptUi {
708 profile_id,
709 prompt_ui_id,
710 } if profile_id == "profile-c" && prompt_ui_id == PromptUiId::new(33)
711 ));
712 }
713
714 #[test]
715 fn transient_close_command_maps_to_extension_popup_close() {
716 let command = BrowserCommand::CloseTransientBrowsingContext {
717 transient_browsing_context_id: TransientBrowsingContextId::new(99),
718 };
719
720 let raw: ChromeCommand = command.into();
721 assert!(matches!(
722 raw,
723 ChromeCommand::CloseExtensionPopup { popup_id }
724 if popup_id == PopupId::new(99)
725 ));
726 }
727
728 #[test]
729 fn edit_action_command_maps_to_chrome_edit_action() {
730 let command = BrowserCommand::ExecuteEditAction {
731 browsing_context_id: BrowsingContextId::new(11),
732 action: EditAction::Paste,
733 };
734
735 let raw: ChromeCommand = command.into();
736 assert!(matches!(
737 raw,
738 ChromeCommand::ExecuteEditAction {
739 browsing_context_id,
740 action: EditAction::Paste,
741 } if browsing_context_id == TabId::new(11)
742 ));
743 }
744
745 #[test]
746 fn transient_edit_action_command_maps_to_extension_popup_edit_action() {
747 let command = BrowserCommand::ExecuteEditActionInTransientBrowsingContext {
748 transient_browsing_context_id: TransientBrowsingContextId::new(12),
749 action: EditAction::SelectAll,
750 };
751
752 let raw: ChromeCommand = command.into();
753 assert!(matches!(
754 raw,
755 ChromeCommand::ExecuteExtensionPopupEditAction {
756 popup_id,
757 action: EditAction::SelectAll,
758 } if popup_id == PopupId::new(12)
759 ));
760 }
761}