1use chromiumoxide_cdp::cdp::browser_protocol::emulation::{
2 MediaFeature, SetEmulatedMediaParams, SetGeolocationOverrideParams, SetLocaleOverrideParams,
3 SetTimezoneOverrideParams,
4};
5use tokio::sync::oneshot;
6
7use crate::error::NightFuryError;
8use crate::session::BrowserSession;
9use crate::worker::WorkerState;
10
11#[non_exhaustive]
17#[allow(clippy::enum_variant_names)]
18pub enum EmulationCmd {
19 SetGeolocation {
20 latitude: f64,
21 longitude: f64,
22 reply: oneshot::Sender<Result<String, String>>,
23 },
24 SetMediaFeature {
25 name: String,
26 value: String,
27 reply: oneshot::Sender<Result<String, String>>,
28 },
29 SetMediaType {
30 media: String,
31 reply: oneshot::Sender<Result<String, String>>,
32 },
33 SetTimezone {
34 timezone_id: String,
35 reply: oneshot::Sender<Result<String, String>>,
36 },
37 SetLocale {
38 locale: String,
39 reply: oneshot::Sender<Result<String, String>>,
40 },
41}
42
43impl EmulationCmd {
48 pub(crate) async fn dispatch(self, state: &mut WorkerState) {
49 match self {
50 EmulationCmd::SetGeolocation {
51 latitude,
52 longitude,
53 reply,
54 } => handle_set_geolocation(state, latitude, longitude, reply).await,
55 EmulationCmd::SetMediaFeature { name, value, reply } => {
56 handle_emulate_media_feature(state, name, value, reply).await
57 }
58 EmulationCmd::SetMediaType { media, reply } => {
59 handle_emulate_media_type(state, media, reply).await
60 }
61 EmulationCmd::SetTimezone { timezone_id, reply } => {
62 handle_emulate_timezone(state, timezone_id, reply).await
63 }
64 EmulationCmd::SetLocale { locale, reply } => {
65 handle_emulate_locale(state, locale, reply).await
66 }
67 }
68 }
69}
70
71async fn handle_set_geolocation(
76 state: &mut WorkerState,
77 latitude: f64,
78 longitude: f64,
79 reply: oneshot::Sender<Result<String, String>>,
80) {
81 let result: Result<String, String> = async {
82 let page = &state.tabs[state.active_tab].page;
83 let params = SetGeolocationOverrideParams::builder()
84 .latitude(latitude)
85 .longitude(longitude)
86 .accuracy(1.0)
87 .build();
88 page.raw_page()
89 .execute(params)
90 .await
91 .map_err(|e| format!("Emulation.setGeolocationOverride failed: {e}"))?;
92 Ok(format!("Geolocation set to ({latitude}, {longitude})"))
93 }
94 .await;
95 let _ = reply.send(result);
96}
97
98async fn handle_emulate_media_feature(
99 state: &mut WorkerState,
100 name: String,
101 value: String,
102 reply: oneshot::Sender<Result<String, String>>,
103) {
104 let result: Result<String, String> = async {
105 let page = &state.tabs[state.active_tab].page;
106 let params = SetEmulatedMediaParams::builder()
107 .feature(MediaFeature::new(name.as_str(), value.as_str()))
108 .build();
109 page.raw_page()
110 .execute(params)
111 .await
112 .map_err(|e| format!("Emulation.setEmulatedMedia failed: {e}"))?;
113 Ok(format!("Media feature {name} set to {value}"))
114 }
115 .await;
116 let _ = reply.send(result);
117}
118
119async fn handle_emulate_media_type(
120 state: &mut WorkerState,
121 media: String,
122 reply: oneshot::Sender<Result<String, String>>,
123) {
124 let result: Result<String, String> = async {
125 let page = &state.tabs[state.active_tab].page;
126 let params = SetEmulatedMediaParams::builder()
127 .media(media.as_str())
128 .build();
129 page.raw_page()
130 .execute(params)
131 .await
132 .map_err(|e| format!("Emulation.setEmulatedMedia failed: {e}"))?;
133 if media.is_empty() {
134 Ok("Media type override disabled".to_string())
135 } else {
136 Ok(format!("Media type set to {media}"))
137 }
138 }
139 .await;
140 let _ = reply.send(result);
141}
142
143async fn handle_emulate_timezone(
144 state: &mut WorkerState,
145 timezone_id: String,
146 reply: oneshot::Sender<Result<String, String>>,
147) {
148 let result: Result<String, String> = async {
149 let page = &state.tabs[state.active_tab].page;
150 let params = SetTimezoneOverrideParams::new(timezone_id.as_str());
151 page.raw_page()
152 .execute(params)
153 .await
154 .map_err(|e| format!("Emulation.setTimezoneOverride failed: {e}"))?;
155 if timezone_id.is_empty() {
156 Ok("Timezone override disabled".to_string())
157 } else {
158 Ok(format!("Timezone set to {timezone_id}"))
159 }
160 }
161 .await;
162 let _ = reply.send(result);
163}
164
165async fn handle_emulate_locale(
166 state: &mut WorkerState,
167 locale: String,
168 reply: oneshot::Sender<Result<String, String>>,
169) {
170 let result: Result<String, String> = async {
171 let page = &state.tabs[state.active_tab].page;
172 let params = SetLocaleOverrideParams::builder()
173 .locale(locale.as_str())
174 .build();
175 page.raw_page()
176 .execute(params)
177 .await
178 .map_err(|e| format!("Emulation.setLocaleOverride failed: {e}"))?;
179 if locale.is_empty() {
180 Ok("Locale override disabled".to_string())
181 } else {
182 Ok(format!("Locale set to {locale}"))
183 }
184 }
185 .await;
186 let _ = reply.send(result);
187}
188
189impl BrowserSession {
194 pub async fn set_geolocation(
196 &self,
197 latitude: f64,
198 longitude: f64,
199 ) -> Result<String, NightFuryError> {
200 send_cmd!(
201 self,
202 |tx| crate::cmd::BrowserCmd::Emulation(EmulationCmd::SetGeolocation {
203 latitude,
204 longitude,
205 reply: tx,
206 }),
207 NightFuryError::OperationFailed
208 )
209 }
210
211 pub async fn emulate_media_feature(
216 &self,
217 name: &str,
218 value: &str,
219 ) -> Result<String, NightFuryError> {
220 send_cmd!(
221 self,
222 |tx| crate::cmd::BrowserCmd::Emulation(EmulationCmd::SetMediaFeature {
223 name: name.to_string(),
224 value: value.to_string(),
225 reply: tx,
226 }),
227 NightFuryError::OperationFailed
228 )
229 }
230
231 pub async fn emulate_media_type(&self, media: &str) -> Result<String, NightFuryError> {
234 send_cmd!(
235 self,
236 |tx| crate::cmd::BrowserCmd::Emulation(EmulationCmd::SetMediaType {
237 media: media.to_string(),
238 reply: tx,
239 }),
240 NightFuryError::OperationFailed
241 )
242 }
243
244 pub async fn emulate_timezone(&self, timezone_id: &str) -> Result<String, NightFuryError> {
248 send_cmd!(
249 self,
250 |tx| crate::cmd::BrowserCmd::Emulation(EmulationCmd::SetTimezone {
251 timezone_id: timezone_id.to_string(),
252 reply: tx,
253 }),
254 NightFuryError::OperationFailed
255 )
256 }
257
258 pub async fn emulate_locale(&self, locale: &str) -> Result<String, NightFuryError> {
261 send_cmd!(
262 self,
263 |tx| crate::cmd::BrowserCmd::Emulation(EmulationCmd::SetLocale {
264 locale: locale.to_string(),
265 reply: tx,
266 }),
267 NightFuryError::OperationFailed
268 )
269 }
270}