viewpoint_core/context/emulation/
mod.rs1use std::collections::HashMap;
6
7use tracing::{debug, instrument};
8
9use viewpoint_cdp::protocol::emulation::{
10 ClearGeolocationOverrideParams, SetGeolocationOverrideParams,
11};
12use viewpoint_cdp::protocol::network::{
13 EmulateNetworkConditionsParams, SetExtraHTTPHeadersParams,
14};
15
16use super::BrowserContext;
17use crate::error::ContextError;
18
19impl BrowserContext {
20 #[instrument(level = "debug", skip(self))]
26 pub async fn clear_geolocation(&self) -> Result<(), ContextError> {
27 if self.is_closed() {
28 return Err(ContextError::Closed);
29 }
30
31 let pages = self.pages.read().await;
33 for page in pages.iter() {
34 if !page.session_id.is_empty() {
35 self.connection()
36 .send_command::<_, serde_json::Value>(
37 "Emulation.clearGeolocationOverride",
38 Some(ClearGeolocationOverrideParams::default()),
39 Some(&page.session_id),
40 )
41 .await?;
42 }
43 }
44
45 Ok(())
46 }
47
48 #[instrument(level = "debug", skip(self, headers))]
68 pub async fn set_extra_http_headers(
69 &self,
70 headers: HashMap<String, String>,
71 ) -> Result<(), ContextError> {
72 if self.is_closed() {
73 return Err(ContextError::Closed);
74 }
75
76 debug!(count = headers.len(), "Setting extra HTTP headers");
77
78 let pages = self.pages.read().await;
80 for page in pages.iter() {
81 if !page.session_id.is_empty() {
82 self.connection()
83 .send_command::<_, serde_json::Value>(
84 "Network.setExtraHTTPHeaders",
85 Some(SetExtraHTTPHeadersParams {
86 headers: headers.clone(),
87 }),
88 Some(&page.session_id),
89 )
90 .await?;
91 }
92 }
93
94 Ok(())
95 }
96
97 #[instrument(level = "debug", skip(self))]
118 pub async fn set_offline(&self, offline: bool) -> Result<(), ContextError> {
119 if self.is_closed() {
120 return Err(ContextError::Closed);
121 }
122
123 debug!(offline = offline, "Setting offline mode");
124
125 let params = if offline {
126 EmulateNetworkConditionsParams::offline()
127 } else {
128 EmulateNetworkConditionsParams::online()
129 };
130
131 let pages = self.pages.read().await;
133 for page in pages.iter() {
134 if !page.session_id.is_empty() {
135 self.connection()
136 .send_command::<_, serde_json::Value>(
137 "Network.emulateNetworkConditions",
138 Some(params.clone()),
139 Some(&page.session_id),
140 )
141 .await?;
142 }
143 }
144
145 Ok(())
146 }
147}
148
149#[derive(Debug)]
155pub struct SetGeolocationBuilder<'a> {
156 context: &'a BrowserContext,
157 latitude: f64,
158 longitude: f64,
159 accuracy: f64,
160}
161
162impl<'a> SetGeolocationBuilder<'a> {
163 pub(crate) fn new(context: &'a BrowserContext, latitude: f64, longitude: f64) -> Self {
164 Self {
165 context,
166 latitude,
167 longitude,
168 accuracy: 0.0,
169 }
170 }
171
172 #[must_use]
174 pub fn accuracy(mut self, accuracy: f64) -> Self {
175 self.accuracy = accuracy;
176 self
177 }
178
179 pub async fn await_(self) -> Result<(), ContextError> {
185 if self.context.is_closed() {
186 return Err(ContextError::Closed);
187 }
188
189 debug!(
190 latitude = self.latitude,
191 longitude = self.longitude,
192 accuracy = self.accuracy,
193 "Setting geolocation"
194 );
195
196 let params = SetGeolocationOverrideParams::with_accuracy(
197 self.latitude,
198 self.longitude,
199 self.accuracy,
200 );
201
202 let pages = self.context.pages.read().await;
204 for page in pages.iter() {
205 if !page.session_id.is_empty() {
206 self.context
207 .connection()
208 .send_command::<_, serde_json::Value>(
209 "Emulation.setGeolocationOverride",
210 Some(params.clone()),
211 Some(&page.session_id),
212 )
213 .await?;
214 }
215 }
216
217 Ok(())
218 }
219}
220
221impl<'a> std::future::IntoFuture for SetGeolocationBuilder<'a> {
223 type Output = Result<(), ContextError>;
224 type IntoFuture = std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'a>>;
225
226 fn into_future(self) -> Self::IntoFuture {
227 Box::pin(self.await_())
228 }
229}