Skip to main content

cbf_chrome/
browser.rs

1//! Chrome-specific `BrowserHandle` extension methods.
2//!
3//! This module adds convenience methods on top of
4//! [`cbf::browser::BrowserHandle`] for Chrome-only operations that are not part
5//! of the browser-generic `cbf` API surface.
6
7use cbf::{browser::BrowserHandle, data::ids::BrowsingContextId, error::Error};
8
9use crate::{
10    backend::ChromiumBackend,
11    command::ChromeCommand,
12    data::{
13        find::{ChromeFindInPageOptions, ChromeStopFindAction},
14        ids::TabId,
15    },
16};
17
18/// Extension trait that adds Chrome-specific commands to
19/// [`BrowserHandle<ChromiumBackend>`].
20///
21/// These helpers wrap raw [`crate::command::ChromeCommand`] dispatch while
22/// accepting browser-generic [`BrowsingContextId`] values at the API boundary.
23pub trait ChromiumBrowserHandleExt {
24    /// Activates an extension action for the given browsing context.
25    fn activate_extension_action(
26        &self,
27        browsing_context_id: BrowsingContextId,
28        extension_id: impl Into<String>,
29    ) -> Result<(), Error>;
30
31    /// Starts or updates a Chrome find-in-page request with explicit options.
32    fn find_in_page(
33        &self,
34        browsing_context_id: BrowsingContextId,
35        request_id: u64,
36        options: ChromeFindInPageOptions,
37    ) -> Result<(), Error>;
38
39    /// Advances an existing find session to the next match for `query`.
40    fn find_next(
41        &self,
42        browsing_context_id: BrowsingContextId,
43        request_id: u64,
44        query: impl Into<String>,
45        match_case: bool,
46    ) -> Result<(), Error>;
47
48    /// Advances an existing find session to the previous match for `query`.
49    fn find_previous(
50        &self,
51        browsing_context_id: BrowsingContextId,
52        request_id: u64,
53        query: impl Into<String>,
54        match_case: bool,
55    ) -> Result<(), Error>;
56
57    /// Stops the active find-in-page session using the requested stop action.
58    fn stop_finding(
59        &self,
60        browsing_context_id: BrowsingContextId,
61        action: ChromeStopFindAction,
62    ) -> Result<(), Error>;
63}
64
65impl ChromiumBrowserHandleExt for BrowserHandle<ChromiumBackend> {
66    fn activate_extension_action(
67        &self,
68        browsing_context_id: BrowsingContextId,
69        extension_id: impl Into<String>,
70    ) -> Result<(), Error> {
71        self.send_raw(ChromeCommand::ActivateExtensionAction {
72            browsing_context_id: TabId::from(browsing_context_id),
73            extension_id: extension_id.into(),
74        })
75    }
76
77    fn find_in_page(
78        &self,
79        browsing_context_id: BrowsingContextId,
80        request_id: u64,
81        options: ChromeFindInPageOptions,
82    ) -> Result<(), Error> {
83        self.send_raw(ChromeCommand::FindInPage {
84            browsing_context_id: TabId::from(browsing_context_id),
85            request_id,
86            options,
87        })
88    }
89
90    fn find_next(
91        &self,
92        browsing_context_id: BrowsingContextId,
93        request_id: u64,
94        query: impl Into<String>,
95        match_case: bool,
96    ) -> Result<(), Error> {
97        self.find_in_page(
98            browsing_context_id,
99            request_id,
100            ChromeFindInPageOptions {
101                query: query.into(),
102                forward: true,
103                match_case,
104                new_session: false,
105                find_match: true,
106            },
107        )
108    }
109
110    fn find_previous(
111        &self,
112        browsing_context_id: BrowsingContextId,
113        request_id: u64,
114        query: impl Into<String>,
115        match_case: bool,
116    ) -> Result<(), Error> {
117        self.find_in_page(
118            browsing_context_id,
119            request_id,
120            ChromeFindInPageOptions {
121                query: query.into(),
122                forward: false,
123                match_case,
124                new_session: false,
125                find_match: true,
126            },
127        )
128    }
129
130    fn stop_finding(
131        &self,
132        browsing_context_id: BrowsingContextId,
133        action: ChromeStopFindAction,
134    ) -> Result<(), Error> {
135        self.send_raw(ChromeCommand::StopFinding {
136            browsing_context_id: TabId::from(browsing_context_id),
137            action,
138        })
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use async_channel::unbounded;
146    use cbf::browser::{CommandEnvelope, CommandSender};
147
148    fn build_sender() -> CommandSender<ChromiumBackend> {
149        let (tx, _rx) = unbounded::<CommandEnvelope<ChromiumBackend>>();
150        CommandSender::from_raw_sender(tx)
151    }
152
153    #[test]
154    fn find_options_default_to_forward_new_session() {
155        let options = ChromeFindInPageOptions::new("needle");
156        assert_eq!(options.query, "needle");
157        assert!(options.forward);
158        assert!(!options.match_case);
159        assert!(options.new_session);
160        assert!(options.find_match);
161    }
162
163    #[test]
164    fn stop_find_action_maps_to_expected_ffi_value() {
165        assert_eq!(
166            ChromeStopFindAction::ClearSelection.to_ffi(),
167            cbf_chrome_sys::ffi::CbfStopFindAction_kCbfStopFindActionClearSelection as u8
168        );
169        assert_eq!(
170            ChromeStopFindAction::KeepSelection.to_ffi(),
171            cbf_chrome_sys::ffi::CbfStopFindAction_kCbfStopFindActionKeepSelection as u8
172        );
173        assert_eq!(
174            ChromeStopFindAction::ActivateSelection.to_ffi(),
175            cbf_chrome_sys::ffi::CbfStopFindAction_kCbfStopFindActionActivateSelection as u8
176        );
177    }
178
179    #[test]
180    fn command_sender_can_be_constructed_for_find_helpers() {
181        let _sender = build_sender();
182    }
183}