1use std::sync::{Arc, Mutex, RwLock, Weak};
2use std::thread;
3use std::time::Duration;
4use std::{
5 collections::HashMap,
6 sync::atomic::{AtomicBool, Ordering},
7};
8
9use anyhow::{Error, Result};
10
11use thiserror::Error;
12
13use log::{debug, error, info, trace, warn};
14
15use serde::de::DeserializeOwned;
16use serde::Serialize;
17use serde_json::{json, Value as Json};
18
19use dialog::Dialog;
20use element::Element;
21use point::Point;
22
23use crate::protocol::cdp::{
24 types::{Event, Method},
25 Browser, Debugger, Emulation, Fetch, Input, Log, Network, Page, Profiler, Runtime, Target, DOM,
26};
27
28use Runtime::AddBinding;
29
30use base64::Engine;
31
32use Input::DispatchKeyEvent;
33
34use Page::{AddScriptToEvaluateOnNewDocument, Navigate, SetInterceptFileChooserDialog};
35
36use Target::AttachToTarget;
37
38use DOM::{Node, NodeId};
39
40use Target::{TargetID, TargetInfo};
41
42use Log::ViolationSetting;
43
44use Fetch::{
45 events::RequestPausedEvent, AuthChallengeResponse, ContinueRequest, ContinueWithAuth,
46 FailRequest, FulfillRequest,
47};
48
49use Network::{
50 events::LoadingFailedEventParams, events::ResponseReceivedEventParams, Cookie, GetResponseBody,
51 GetResponseBodyReturnObject, SetExtraHTTPHeaders, SetUserAgentOverride,
52};
53
54use crate::util;
55
56use crate::types::{Bounds, CurrentBounds, PrintToPdfOptions, RemoteError};
57
58use super::transport::SessionId;
59use crate::browser::transport::Transport;
60use std::thread::sleep;
61
62pub mod dialog;
63pub mod element;
64mod keys;
65pub mod point;
66
67#[derive(Debug, Copy, Clone)]
68pub enum ModifierKey {
69 Alt = 1,
70 Ctrl = 2,
71 Meta = 4, Shift = 8,
73}
74
75#[derive(Debug)]
76pub enum RequestPausedDecision {
77 Fulfill(FulfillRequest),
78 Fail(FailRequest),
79 Continue(Option<ContinueRequest>),
80}
81
82#[rustfmt::skip]
83pub type ResponseHandler = Box<
84 dyn Fn(
85 ResponseReceivedEventParams,
86 &dyn Fn() -> Result<
87 GetResponseBodyReturnObject,
88 Error,
89 >,
90 ) + Send
91 + Sync,
92>;
93
94#[rustfmt::skip]
95pub type LoadingFailedHandler = Box<
96 dyn Fn(
97 ResponseReceivedEventParams,
98 LoadingFailedEventParams
99 ) + Send
100 + Sync,
101>;
102
103type SyncSendEvent = dyn EventListener<Event> + Send + Sync;
104pub trait RequestInterceptor {
105 fn intercept(
106 &self,
107 transport: Arc<Transport>,
108 session_id: SessionId,
109 event: RequestPausedEvent,
110 ) -> RequestPausedDecision;
111}
112
113impl<F> RequestInterceptor for F
114where
115 F: Fn(Arc<Transport>, SessionId, RequestPausedEvent) -> RequestPausedDecision + Send + Sync,
116{
117 fn intercept(
118 &self,
119 transport: Arc<Transport>,
120 session_id: SessionId,
121 event: RequestPausedEvent,
122 ) -> RequestPausedDecision {
123 self(transport, session_id, event)
124 }
125}
126
127type RequestIntercept = dyn RequestInterceptor + Send + Sync;
128
129pub trait EventListener<T> {
130 fn on_event(&self, event: &T);
131}
132
133impl<T, F: Fn(&T) + Send + Sync> EventListener<T> for F {
134 fn on_event(&self, event: &T) {
135 self(event);
136 }
137}
138
139pub trait Binding {
140 fn call_binding(&self, data: Json);
141}
142
143impl<T: Fn(Json) + Send + Sync> Binding for T {
144 fn call_binding(&self, data: Json) {
145 self(data);
146 }
147}
148
149pub type SafeBinding = dyn Binding + Send + Sync;
150
151pub type FunctionBinding = HashMap<String, Arc<SafeBinding>>;
152
153pub struct Tab {
158 target_id: TargetID,
159 transport: Arc<Transport>,
160 session_id: SessionId,
161 navigating: Arc<AtomicBool>,
162 target_info: Arc<Mutex<TargetInfo>>,
163 request_interceptor: Arc<Mutex<Arc<RequestIntercept>>>,
164 response_handler: Arc<Mutex<HashMap<String, ResponseHandler>>>,
165 loading_failed_handler: Arc<Mutex<HashMap<String, LoadingFailedHandler>>>,
166 auth_handler: Arc<Mutex<AuthChallengeResponse>>,
167 default_timeout: Arc<RwLock<Duration>>,
168 page_bindings: Arc<Mutex<FunctionBinding>>,
169 event_listeners: Arc<Mutex<Vec<Arc<SyncSendEvent>>>>,
170 slow_motion_multiplier: Arc<RwLock<f64>>, }
172
173#[derive(Debug, Error)]
174#[error("No element found")]
175pub struct NoElementFound {}
176
177#[derive(Debug, Error)]
178#[error("Navigate failed: {}", error_text)]
179pub struct NavigationFailed {
180 error_text: String,
181}
182
183#[derive(Debug, Error)]
184#[error("No LocalStorage item was found")]
185pub struct NoLocalStorageItemFound {}
186
187#[derive(Debug, Error)]
188#[error("No UserAgent evaluated")]
189pub struct NoUserAgentEvaluated {}
190
191impl NoElementFound {
192 pub fn map(error: Error) -> Error {
193 match error.downcast::<RemoteError>() {
194 Ok(remote_error) => {
195 match remote_error.message.as_ref() {
196 "Could not find node with given id" => Self {}.into(),
199
200 _ => remote_error.into(),
202 }
203 }
204 Err(original_error) => original_error,
206 }
207 }
208}
209
210impl Tab {
211 pub fn new(target_info: TargetInfo, transport: Arc<Transport>) -> Result<Self> {
212 let target_id = target_info.target_id.clone();
213
214 let session_id = transport
215 .call_method_on_browser(AttachToTarget {
216 target_id: target_id.clone(),
217 flatten: None,
218 })?
219 .session_id
220 .into();
221
222 debug!("New tab attached with session ID: {:?}", session_id);
223
224 let target_info_mutex = Arc::new(Mutex::new(target_info));
225
226 let tab = Self {
227 target_id,
228 transport,
229 session_id,
230 navigating: Arc::new(AtomicBool::new(false)),
231 target_info: target_info_mutex,
232 page_bindings: Arc::new(Mutex::new(HashMap::new())),
233 request_interceptor: Arc::new(Mutex::new(Arc::new(
234 |_transport, _session_id, _interception| RequestPausedDecision::Continue(None),
235 ))),
236 response_handler: Arc::new(Mutex::new(HashMap::new())),
237 loading_failed_handler: Arc::new(Mutex::new(HashMap::new())),
238 auth_handler: Arc::new(Mutex::new(AuthChallengeResponse {
239 response: Fetch::AuthChallengeResponseResponse::Default,
240 username: None,
241 password: None,
242 })),
243 default_timeout: Arc::new(RwLock::new(Duration::from_secs(20))),
244 event_listeners: Arc::new(Mutex::new(Vec::new())),
245 slow_motion_multiplier: Arc::new(RwLock::new(0.0)),
246 };
247
248 tab.call_method(Page::Enable(None))?;
249 tab.call_method(Page::SetLifecycleEventsEnabled { enabled: true })?;
250
251 tab.start_event_handler_thread();
252
253 Ok(tab)
254 }
255
256 pub fn update_target_info(&self, target_info: TargetInfo) {
257 let mut info = self.target_info.lock().unwrap();
258 *info = target_info;
259 }
260
261 pub fn get_target_id(&self) -> &TargetID {
262 &self.target_id
263 }
264
265 pub fn get_target_info(&self) -> Result<TargetInfo> {
267 Ok(self
268 .call_method(Target::GetTargetInfo {
269 target_id: Some(self.get_target_id().to_string()),
270 })?
271 .target_info)
272 }
273
274 pub fn get_browser_context_id(&self) -> Result<Option<String>> {
275 Ok(self.get_target_info()?.browser_context_id)
276 }
277
278 pub fn get_url(&self) -> String {
279 let info = self.target_info.lock().unwrap();
280 info.url.clone()
281 }
282
283 pub fn set_user_agent(
285 &self,
286 user_agent: &str,
287 accept_language: Option<&str>,
288 platform: Option<&str>,
289 ) -> Result<()> {
290 self.call_method(SetUserAgentOverride {
291 user_agent: user_agent.to_string(),
292 accept_language: accept_language.map(std::string::ToString::to_string),
293 platform: platform.map(std::string::ToString::to_string),
294 user_agent_metadata: None,
295 })
296 .map(|_| ())
297 }
298
299 fn start_event_handler_thread(&self) {
300 let transport: Arc<Transport> = Arc::clone(&self.transport);
301 let incoming_events_rx = self
302 .transport
303 .listen_to_target_events(self.session_id.clone());
304 let navigating = Arc::clone(&self.navigating);
305 let interceptor_mutex = Arc::clone(&self.request_interceptor);
306 let response_handler_mutex = self.response_handler.clone();
307 let loading_failed_handler_mutex = self.loading_failed_handler.clone();
308 let auth_handler_mutex = self.auth_handler.clone();
309 let session_id = self.session_id.clone();
310 let listeners_mutex = Arc::clone(&self.event_listeners);
311
312 let bindings_mutex = Arc::clone(&self.page_bindings);
313 let received_event_params = Arc::new(Mutex::new(HashMap::new()));
314
315 thread::spawn(move || {
316 for event in incoming_events_rx {
317 let listeners = listeners_mutex.lock().unwrap();
318 listeners.iter().for_each(|listener| {
319 listener.on_event(&event);
320 });
321
322 match event {
323 Event::PageLifecycleEvent(lifecycle_event) => {
324 let event_name = lifecycle_event.params.name.as_ref();
325 trace!("Lifecycle event: {}", event_name);
326 match event_name {
327 "networkAlmostIdle" => {
328 navigating.store(false, Ordering::SeqCst);
329 }
330 "init" => {
331 navigating.store(true, Ordering::SeqCst);
332 }
333 _ => {}
334 }
335 }
336 Event::RuntimeBindingCalled(binding) => {
337 let bindings = bindings_mutex.lock().unwrap().clone();
338
339 let name = binding.params.name;
340 let payload = binding.params.payload;
341
342 let func = &Arc::clone(bindings.get(&name).unwrap());
343
344 func.call_binding(json!(payload));
345 }
346 Event::FetchRequestPaused(event) => {
347 let interceptor = interceptor_mutex.lock().unwrap();
348 let decision = interceptor.intercept(
349 Arc::clone(&transport),
350 session_id.clone(),
351 event.clone(),
352 );
353 let result = match decision {
354 RequestPausedDecision::Continue(continue_request) => {
355 if let Some(continue_request) = continue_request {
356 transport
357 .call_method_on_target(session_id.clone(), continue_request)
358 .map(|_| ())
359 } else {
360 transport
361 .call_method_on_target(
362 session_id.clone(),
363 ContinueRequest {
364 request_id: event.params.request_id,
365 url: None,
366 method: None,
367 post_data: None,
368 headers: None,
369 intercept_response: None,
370 },
371 )
372 .map(|_| ())
373 }
374 }
375 RequestPausedDecision::Fulfill(fulfill_request) => transport
376 .call_method_on_target(session_id.clone(), fulfill_request)
377 .map(|_| ()),
378 RequestPausedDecision::Fail(fail_request) => transport
379 .call_method_on_target(session_id.clone(), fail_request)
380 .map(|_| ()),
381 };
382 if result.is_err() {
383 warn!("Tried to handle request after connection was closed");
384 }
385 }
386 Event::FetchAuthRequired(event) => {
387 let auth_challenge_response = auth_handler_mutex.lock().unwrap().clone();
388
389 let request_id = event.params.request_id;
390 let method = ContinueWithAuth {
391 request_id,
392 auth_challenge_response,
393 };
394 let result = transport.call_method_on_target(session_id.clone(), method);
395 if result.is_err() {
396 warn!("Tried to handle request after connection was closed");
397 }
398 }
399 Event::NetworkResponseReceived(ev) => {
400 let request_id = ev.params.request_id.clone();
401 received_event_params
402 .lock()
403 .unwrap()
404 .insert(request_id, ev.params);
405 }
406 Event::NetworkLoadingFinished(ev) => {
407 response_handler_mutex.lock().unwrap().iter().for_each(
408 |(_name, handler)| {
409 let request_id = ev.params.request_id.clone();
410 let retrieve_body = || {
411 let method = GetResponseBody {
412 request_id: request_id.clone(),
413 };
414 transport.call_method_on_target(session_id.clone(), method)
415 };
416 if let Some(params) =
417 received_event_params.lock().unwrap().get(&request_id)
418 {
419 handler(params.clone(), &retrieve_body);
420 } else {
421 warn!("Request id does not exist");
422 }
423 },
424 );
425 }
426 Event::NetworkLoadingFailed(ev) => loading_failed_handler_mutex
427 .lock()
428 .unwrap()
429 .iter()
430 .for_each(|(_name, handler)| {
431 let request_id = ev.params.request_id.clone();
432
433 if let Some(params) =
434 received_event_params.lock().unwrap().get(&request_id)
435 {
436 handler(params.clone(), ev.params.clone());
437 } else {
438 warn!("Request id does not exist");
439 }
440 }),
441 _ => {
442 let raw_event = format!("{event:?}");
443 trace!(
444 "Unhandled event: {}",
445 raw_event.chars().take(50).collect::<String>()
446 );
447 }
448 }
449 }
450 info!("finished tab's event handling loop");
451 });
452 }
453
454 pub fn expose_function(&self, name: &str, func: Arc<SafeBinding>) -> Result<()> {
455 let bindings_mutex = Arc::clone(&self.page_bindings);
456
457 let mut bindings = bindings_mutex.lock().unwrap();
458
459 bindings.insert(name.to_string(), func);
460
461 let expression = r"
462 (function addPageBinding(bindingName) {
463 const binding = window[bindingName];
464 window[bindingName] = (...args) => {
465 const me = window[bindingName];
466 let callbacks = me['callbacks'];
467 if (!callbacks) {
468 callbacks = new Map();
469 me['callbacks'] = callbacks;
470 }
471 const seq = (me['lastSeq'] || 0) + 1;
472 me['lastSeq'] = seq;
473 const promise = new Promise((resolve, reject) => callbacks.set(seq, {resolve, reject}));
474 binding(JSON.stringify({name: bindingName, seq, args}));
475 return promise;
476 };
477 })()
478 "; self.call_method(AddBinding {
481 name: name.to_string(),
482 execution_context_id: None,
483 execution_context_name: None,
484 })?;
485
486 self.call_method(AddScriptToEvaluateOnNewDocument {
487 source: expression.to_string(),
488 world_name: None,
489 include_command_line_api: None,
490 run_immediately: None,
491 })?;
492
493 Ok(())
494 }
495
496 pub fn remove_function(&self, name: &str) -> Result<()> {
497 let bindings_mutex = Arc::clone(&self.page_bindings);
498
499 let mut bindings = bindings_mutex.lock().unwrap();
500
501 bindings.remove(name).unwrap();
502
503 Ok(())
504 }
505
506 pub fn call_method<C>(&self, method: C) -> Result<C::ReturnObject>
507 where
508 C: Method + serde::Serialize + std::fmt::Debug,
509 {
510 trace!("Calling method: {:?}", method);
511 let result = self
512 .transport
513 .call_method_on_target(self.session_id.clone(), method);
514 let result_string = format!("{result:?}");
515 trace!(
516 "Got result: {:?}",
517 result_string.chars().take(70).collect::<String>()
518 );
519 result
520 }
521
522 pub fn wait_until_navigated(&self) -> Result<&Self> {
523 let navigating = Arc::clone(&self.navigating);
524 let timeout = *self.default_timeout.read().unwrap();
525
526 util::Wait::with_timeout(timeout).until(|| {
527 if navigating.load(Ordering::SeqCst) {
528 None
529 } else {
530 Some(true)
531 }
532 })?;
533 debug!("A tab finished navigating");
534
535 Ok(self)
536 }
537
538 pub fn bring_to_front(&self) -> Result<Page::BringToFrontReturnObject> {
540 self.call_method(Page::BringToFront(None))
541 }
542
543 pub fn navigate_to(&self, url: &str) -> Result<&Self> {
544 let return_object = self.call_method(Navigate {
545 url: url.to_string(),
546 referrer: None,
547 transition_Type: None,
548 frame_id: None,
549 referrer_policy: None,
550 })?;
551 if let Some(error_text) = return_object.error_text {
552 return Err(NavigationFailed { error_text }.into());
553 }
554
555 let navigating = Arc::clone(&self.navigating);
556 navigating.store(true, Ordering::SeqCst);
557
558 info!("Navigating a tab to {}", url);
559
560 Ok(self)
561 }
562
563 pub fn set_default_timeout(&self, timeout: Duration) -> &Self {
579 let mut current_timeout = self.default_timeout.write().unwrap();
580 *current_timeout = timeout;
581 self
582 }
583
584 pub fn set_slow_motion_multiplier(&self, multiplier: f64) -> &Self {
605 let mut slow_motion_multiplier = self.slow_motion_multiplier.write().unwrap();
606 *slow_motion_multiplier = multiplier;
607 self
608 }
609
610 fn optional_slow_motion_sleep(&self, millis: u64) {
611 let multiplier = self.slow_motion_multiplier.read().unwrap();
612 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
613 let scaled_millis = millis * *multiplier as u64;
614 sleep(Duration::from_millis(scaled_millis));
615 }
616
617 pub fn wait_for_element(&self, selector: &str) -> Result<Element<'_>> {
618 self.wait_for_element_with_custom_timeout(selector, *self.default_timeout.read().unwrap())
619 }
620
621 pub fn wait_for_xpath(&self, selector: &str) -> Result<Element<'_>> {
622 self.wait_for_xpath_with_custom_timeout(selector, *self.default_timeout.read().unwrap())
623 }
624
625 pub fn wait_for_element_with_custom_timeout(
626 &self,
627 selector: &str,
628 timeout: std::time::Duration,
629 ) -> Result<Element<'_>> {
630 debug!("Waiting for element with selector: {:?}", selector);
631 util::Wait::with_timeout(timeout).strict_until(
632 || self.find_element(selector),
633 Error::downcast::<NoElementFound>,
634 )
635 }
636
637 pub fn wait_for_xpath_with_custom_timeout(
638 &self,
639 selector: &str,
640 timeout: std::time::Duration,
641 ) -> Result<Element<'_>> {
642 debug!("Waiting for element with selector: {:?}", selector);
643 util::Wait::with_timeout(timeout).strict_until(
644 || self.find_element_by_xpath(selector),
645 Error::downcast::<NoElementFound>,
646 )
647 }
648
649 pub fn wait_for_elements(&self, selector: &str) -> Result<Vec<Element<'_>>> {
650 debug!("Waiting for element with selector: {:?}", selector);
651 util::Wait::with_timeout(*self.default_timeout.read().unwrap()).strict_until(
652 || self.find_elements(selector),
653 Error::downcast::<NoElementFound>,
654 )
655 }
656
657 pub fn wait_for_elements_by_xpath(&self, selector: &str) -> Result<Vec<Element<'_>>> {
658 debug!("Waiting for element with selector: {:?}", selector);
659 util::Wait::with_timeout(*self.default_timeout.read().unwrap()).strict_until(
660 || self.find_elements_by_xpath(selector),
661 Error::downcast::<NoElementFound>,
662 )
663 }
664
665 pub fn find_element(&self, selector: &str) -> Result<Element<'_>> {
697 let root_node_id = self.get_document()?.node_id;
698 trace!("Looking up element via selector: {}", selector);
699
700 self.run_query_selector_on_node(root_node_id, selector)
701 }
702
703 pub fn find_element_by_xpath(&self, query: &str) -> Result<Element<'_>> {
704 self.get_document()?;
705
706 self.call_method(DOM::PerformSearch {
707 query: query.to_string(),
708 include_user_agent_shadow_dom: None,
709 })
710 .map(|o| {
711 match self.call_method(DOM::GetSearchResults {
712 search_id: o.search_id,
713 from_index: 0,
714 to_index: o.result_count,
715 }) {
716 Ok(res) => res.node_ids.first().copied().unwrap_or(0),
717 Err(_) => 0,
718 }
719 })
720 .and_then(|id| {
721 if id == 0 {
722 Err(NoElementFound {}.into())
723 } else {
724 Ok(Element::new(self, id)?)
725 }
726 })
727 }
728
729 pub fn run_query_selector_on_node(
730 &self,
731 node_id: NodeId,
732 selector: &str,
733 ) -> Result<Element<'_>> {
734 let node_id = self
735 .call_method(DOM::QuerySelector {
736 node_id,
737 selector: selector.to_string(),
738 })
739 .map_err(NoElementFound::map)?
740 .node_id;
741
742 Element::new(self, node_id)
743 }
744
745 pub fn run_query_selector_all_on_node(
746 &self,
747 node_id: NodeId,
748 selector: &str,
749 ) -> Result<Vec<Element<'_>>> {
750 let node_ids = self
751 .call_method(DOM::QuerySelectorAll {
752 node_id,
753 selector: selector.to_string(),
754 })
755 .map_err(NoElementFound::map)?
756 .node_ids;
757
758 node_ids
759 .iter()
760 .map(|node_id| Element::new(self, *node_id))
761 .collect()
762 }
763
764 pub fn get_document(&self) -> Result<Node> {
765 Ok(self
766 .call_method(DOM::GetDocument {
767 depth: Some(0),
768 pierce: Some(false),
769 })?
770 .root)
771 }
772
773 pub fn get_content(&self) -> Result<String> {
775 let func = "
776 (function () {
777 let retVal = '';
778 if (document.doctype)
779 retVal = new XMLSerializer().serializeToString(document.doctype);
780 if (document.documentElement)
781 retVal += document.documentElement.outerHTML;
782 return retVal;
783 })();";
784 let html = self.evaluate(func, false)?.value.unwrap();
785 Ok(String::from(html.as_str().unwrap()))
786 }
787
788 pub fn find_elements(&self, selector: &str) -> Result<Vec<Element<'_>>> {
789 trace!("Looking up elements via selector: {}", selector);
790
791 let root_node_id = self.get_document()?.node_id;
792 let node_ids = self
793 .call_method(DOM::QuerySelectorAll {
794 node_id: root_node_id,
795 selector: selector.to_string(),
796 })
797 .map_err(NoElementFound::map)?
798 .node_ids;
799
800 if node_ids.is_empty() {
801 return Err(NoElementFound {}.into());
802 }
803
804 node_ids
805 .into_iter()
806 .map(|node_id| Element::new(self, node_id))
807 .collect()
808 }
809
810 pub fn find_elements_by_xpath(&self, query: &str) -> Result<Vec<Element<'_>>> {
811 self.get_document()?;
812
813 self.call_method(DOM::PerformSearch {
814 query: query.to_string(),
815 include_user_agent_shadow_dom: None,
816 })
817 .and_then(|o| {
818 Ok(self
819 .call_method(DOM::GetSearchResults {
820 search_id: o.search_id,
821 from_index: 0,
822 to_index: o.result_count,
823 })?
824 .node_ids)
825 })
826 .and_then(|ids| {
827 ids.iter()
828 .filter(|id| **id != 0)
829 .map(|id| Element::new(self, *id))
830 .collect()
831 })
832 }
833
834 pub fn describe_node(&self, node_id: NodeId) -> Result<Node> {
835 let node = self
836 .call_method(DOM::DescribeNode {
837 node_id: Some(node_id),
838 backend_node_id: None,
839 depth: Some(100),
840 object_id: None,
841 pierce: None,
842 })?
843 .node;
844 Ok(node)
845 }
846
847 pub fn type_str(&self, string_to_type: &str) -> Result<&Self> {
848 for c in string_to_type.split("") {
849 if c.is_empty() {
851 continue;
852 }
853 let definition = keys::get_key_definition(c);
854 match definition {
856 Ok(key) => {
857 let v: DispatchKeyEvent = key.into();
858
859 self.call_method(v.clone())?;
860 self.call_method(DispatchKeyEvent {
861 Type: Input::DispatchKeyEventTypeOption::KeyUp,
862 ..v
863 })?;
864 }
865 Err(_) => {
866 self.send_character(c)?;
867 }
868 }
869 }
870 Ok(self)
871 }
872
873 pub fn send_character(&self, char_to_send: &str) -> Result<&Self> {
879 self.call_method(Input::InsertText {
880 text: char_to_send.to_string(),
881 })?;
882 Ok(self)
883 }
884
885 pub fn press_key_with_modifiers(
890 &self,
891 key: &str,
892 modifiers: Option<&[ModifierKey]>,
893 ) -> Result<&Self> {
894 let definition = keys::get_key_definition(key)?;
896
897 let text = definition
898 .text
899 .or({
900 if definition.key.len() == 1 {
901 Some(definition.key)
902 } else {
903 None
904 }
905 })
906 .map(std::string::ToString::to_string);
907
908 let key_down_event_type = if text.is_some() {
910 Input::DispatchKeyEventTypeOption::KeyDown
911 } else {
912 Input::DispatchKeyEventTypeOption::RawKeyDown
913 };
914
915 let key = Some(definition.key.to_string());
916 let code = Some(definition.code.to_string());
917
918 let modifiers = modifiers.map(|v| v.iter().fold(0, |acc, e| acc | *e as u32));
919
920 self.optional_slow_motion_sleep(25);
921
922 self.call_method(Input::DispatchKeyEvent {
923 Type: key_down_event_type,
924 key: key.clone(),
925 text: text.clone(),
926 code: code.clone(),
927 windows_virtual_key_code: Some(definition.key_code),
928 native_virtual_key_code: Some(definition.key_code),
929 modifiers,
930 timestamp: None,
931 unmodified_text: None,
932 key_identifier: None,
933 auto_repeat: None,
934 is_keypad: None,
935 is_system_key: None,
936 location: None,
937 commands: None,
938 })?;
939 self.call_method(Input::DispatchKeyEvent {
940 Type: Input::DispatchKeyEventTypeOption::KeyUp,
941 key,
942 text,
943 code,
944 windows_virtual_key_code: Some(definition.key_code),
945 native_virtual_key_code: Some(definition.key_code),
946 modifiers,
947 timestamp: None,
948 unmodified_text: None,
949 key_identifier: None,
950 auto_repeat: None,
951 is_keypad: None,
952 is_system_key: None,
953 location: None,
954 commands: None,
955 })?;
956 Ok(self)
957 }
958
959 pub fn press_key(&self, key: &str) -> Result<&Self> {
963 self.press_key_with_modifiers(key, None)
964 }
965
966 pub fn move_mouse_to_point(&self, point: Point) -> Result<&Self> {
968 if point.x == 0.0 && point.y == 0.0 {
969 warn!("Midpoint of element shouldn't be 0,0. Something is probably wrong.");
970 }
971
972 self.optional_slow_motion_sleep(100);
973
974 self.call_method(Input::DispatchMouseEvent {
975 Type: Input::DispatchMouseEventTypeOption::MouseMoved,
976 x: point.x,
977 y: point.y,
978 modifiers: None,
979 timestamp: None,
980 button: None,
981 buttons: None,
982 click_count: None,
983 force: None,
984 tangential_pressure: None,
985 tilt_x: None,
986 tilt_y: None,
987 twist: None,
988 delta_x: None,
989 delta_y: None,
990 pointer_Type: None,
991 })?;
992
993 Ok(self)
994 }
995
996 pub fn click_point(&self, point: Point) -> Result<&Self> {
997 trace!("Clicking point: {:?}", point);
998 if point.x == 0.0 && point.y == 0.0 {
999 warn!("Midpoint of element shouldn't be 0,0. Something is probably wrong.");
1000 }
1001
1002 self.move_mouse_to_point(point)?;
1003
1004 self.optional_slow_motion_sleep(250);
1005 self.call_method(Input::DispatchMouseEvent {
1006 Type: Input::DispatchMouseEventTypeOption::MousePressed,
1007 x: point.x,
1008 y: point.y,
1009 button: Some(Input::MouseButton::Left),
1010 click_count: Some(1),
1011 modifiers: None,
1012 timestamp: None,
1013 buttons: None,
1014 force: None,
1015 tangential_pressure: None,
1016 tilt_x: None,
1017 tilt_y: None,
1018 twist: None,
1019 delta_x: None,
1020 delta_y: None,
1021 pointer_Type: None,
1022 })?;
1023 self.call_method(Input::DispatchMouseEvent {
1024 Type: Input::DispatchMouseEventTypeOption::MouseReleased,
1025 x: point.x,
1026 y: point.y,
1027 button: Some(Input::MouseButton::Left),
1028 click_count: Some(1),
1029 modifiers: None,
1030 timestamp: None,
1031 buttons: None,
1032 force: None,
1033 tangential_pressure: None,
1034 tilt_x: None,
1035 tilt_y: None,
1036 twist: None,
1037 delta_x: None,
1038 delta_y: None,
1039 pointer_Type: None,
1040 })?;
1041 Ok(self)
1042 }
1043
1044 pub fn capture_screenshot(
1070 &self,
1071 format: Page::CaptureScreenshotFormatOption,
1072 quality: Option<u32>,
1073 clip: Option<Page::Viewport>,
1074 from_surface: bool,
1075 ) -> Result<Vec<u8>> {
1076 let data = self
1077 .call_method(Page::CaptureScreenshot {
1078 format: Some(format),
1079 clip,
1080 quality,
1081 from_surface: Some(from_surface),
1082 capture_beyond_viewport: None,
1083 optimize_for_speed: None,
1084 })?
1085 .data;
1086 base64::prelude::BASE64_STANDARD
1087 .decode(data)
1088 .map_err(Into::into)
1089 }
1090
1091 pub fn print_to_pdf(&self, options: Option<PrintToPdfOptions>) -> Result<Vec<u8>> {
1092 if let Some(options) = options {
1093 let transfer_mode: Option<Page::PrintToPDFTransfer_modeOption> =
1094 options.transfer_mode.and_then(std::convert::Into::into);
1095 let data = self
1096 .call_method(Page::PrintToPDF {
1097 landscape: options.landscape,
1098 display_header_footer: options.display_header_footer,
1099 print_background: options.print_background,
1100 scale: options.scale,
1101 paper_width: options.paper_width,
1102 paper_height: options.paper_height,
1103 margin_top: options.margin_top,
1104 margin_bottom: options.margin_bottom,
1105 margin_left: options.margin_left,
1106 margin_right: options.margin_right,
1107 page_ranges: options.page_ranges,
1108 generate_tagged_pdf: options.generate_tagged_pdf,
1109 generate_document_outline: options.generate_document_outline,
1110 header_template: options.header_template,
1111 footer_template: options.footer_template,
1112 prefer_css_page_size: options.prefer_css_page_size,
1113 transfer_mode,
1114 })?
1115 .data;
1116 base64::prelude::BASE64_STANDARD
1117 .decode(data)
1118 .map_err(Into::into)
1119 } else {
1120 let data = self
1121 .call_method(Page::PrintToPDF {
1122 ..Default::default()
1123 })?
1124 .data;
1125
1126 base64::prelude::BASE64_STANDARD
1127 .decode(data)
1128 .map_err(Into::into)
1129 }
1130 }
1131
1132 pub fn reload(
1138 &self,
1139 ignore_cache: bool,
1140 script_to_evaluate_on_load: Option<&str>,
1141 ) -> Result<&Self> {
1142 self.optional_slow_motion_sleep(100);
1143 self.call_method(Page::Reload {
1144 loader_id: None,
1145 ignore_cache: Some(ignore_cache),
1146 script_to_evaluate_on_load: script_to_evaluate_on_load
1147 .map(std::string::ToString::to_string),
1148 })?;
1149 Ok(self)
1150 }
1151
1152 pub fn set_transparent_background_color(&self) -> Result<&Self> {
1170 self.call_method(Emulation::SetDefaultBackgroundColorOverride {
1171 color: Some(DOM::RGBA {
1172 r: 0,
1173 g: 0,
1174 b: 0,
1175 a: Some(0.0),
1176 }),
1177 })?;
1178 Ok(self)
1179 }
1180
1181 pub fn set_background_color(&self, color: DOM::RGBA) -> Result<&Self> {
1199 self.call_method(Emulation::SetDefaultBackgroundColorOverride { color: Some(color) })?;
1200 Ok(self)
1201 }
1202
1203 pub fn enable_profiler(&self) -> Result<&Self> {
1205 self.call_method(Profiler::Enable(None))?;
1206
1207 Ok(self)
1208 }
1209
1210 pub fn disable_profiler(&self) -> Result<&Self> {
1212 self.call_method(Profiler::Disable(None))?;
1213
1214 Ok(self)
1215 }
1216
1217 pub fn start_js_coverage(&self) -> Result<&Self> {
1228 self.call_method(Profiler::StartPreciseCoverage {
1229 call_count: Some(true),
1230 detailed: Some(true),
1231 allow_triggered_updates: None,
1232 })?;
1233 Ok(self)
1234 }
1235
1236 pub fn stop_js_coverage(&self) -> Result<&Self> {
1239 self.call_method(Profiler::StopPreciseCoverage(None))?;
1240 Ok(self)
1241 }
1242
1243 pub fn take_precise_js_coverage(&self) -> Result<Vec<Profiler::ScriptCoverage>> {
1255 let script_coverages = self
1256 .call_method(Profiler::TakePreciseCoverage(None))?
1257 .result;
1258 Ok(script_coverages)
1259 }
1260
1261 pub fn enable_fetch(
1263 &self,
1264 patterns: Option<&[Fetch::RequestPattern]>,
1265 handle_auth_requests: Option<bool>,
1266 ) -> Result<&Self> {
1267 self.call_method(Fetch::Enable {
1268 patterns: patterns.map(Vec::from),
1269 handle_auth_requests,
1270 })?;
1271 Ok(self)
1272 }
1273
1274 pub fn disable_fetch(&self) -> Result<&Self> {
1276 self.call_method(Fetch::Disable(None))?;
1277 Ok(self)
1278 }
1279
1280 pub fn enable_request_interception(&self, interceptor: Arc<RequestIntercept>) -> Result<()> {
1288 let mut current_interceptor = self.request_interceptor.lock().unwrap();
1289 *current_interceptor = interceptor;
1290 Ok(())
1291 }
1292
1293 pub fn authenticate(
1294 &self,
1295 username: Option<String>,
1296 password: Option<String>,
1297 ) -> Result<&Self> {
1298 let mut current_auth_handler = self.auth_handler.lock().unwrap();
1299 *current_auth_handler = AuthChallengeResponse {
1300 response: Fetch::AuthChallengeResponseResponse::ProvideCredentials,
1301 username,
1302 password,
1303 };
1304 Ok(self)
1305 }
1306
1307 pub fn register_response_handling<S: ToString>(
1320 &self,
1321 handler_name: S,
1322 handler: ResponseHandler,
1323 ) -> Result<Option<ResponseHandler>> {
1324 self.call_method(Network::Enable {
1325 max_total_buffer_size: None,
1326 max_resource_buffer_size: None,
1327 max_post_data_size: None,
1328 })?;
1329 Ok(self
1330 .response_handler
1331 .lock()
1332 .unwrap()
1333 .insert(handler_name.to_string(), handler))
1334 }
1335
1336 pub fn register_loading_failed_handling<S: ToString>(
1337 &self,
1338 handler_name: S,
1339 handler: LoadingFailedHandler,
1340 ) -> Result<Option<LoadingFailedHandler>> {
1341 self.call_method(Network::Enable {
1342 max_total_buffer_size: None,
1343 max_resource_buffer_size: None,
1344 max_post_data_size: None,
1345 })?;
1346 Ok(self
1347 .loading_failed_handler
1348 .lock()
1349 .unwrap()
1350 .insert(handler_name.to_string(), handler))
1351 }
1352
1353 pub fn deregister_response_handling(
1357 &self,
1358 handler_name: &str,
1359 ) -> Result<Option<ResponseHandler>> {
1360 Ok(self.response_handler.lock().unwrap().remove(handler_name))
1361 }
1362
1363 pub fn deregister_response_handling_all(&self) -> Result<()> {
1365 self.response_handler.lock().unwrap().clear();
1366 Ok(())
1367 }
1368
1369 pub fn enable_runtime(&self) -> Result<&Self> {
1371 self.call_method(Runtime::Enable(None))?;
1372 Ok(self)
1373 }
1374
1375 pub fn disable_runtime(&self) -> Result<&Self> {
1377 self.call_method(Runtime::Disable(None))?;
1378 Ok(self)
1379 }
1380
1381 pub fn enable_debugger(&self) -> Result<()> {
1383 self.call_method(Debugger::Enable {
1384 max_scripts_cache_size: None,
1385 })?;
1386 Ok(())
1387 }
1388
1389 pub fn disable_debugger(&self) -> Result<()> {
1391 self.call_method(Debugger::Disable(None))?;
1392 Ok(())
1393 }
1394
1395 pub fn get_script_source(&self, script_id: &str) -> Result<String> {
1399 Ok(self
1400 .call_method(Debugger::GetScriptSource {
1401 script_id: script_id.to_string(),
1402 })?
1403 .script_source)
1404 }
1405
1406 pub fn enable_log(&self) -> Result<&Self> {
1412 self.call_method(Log::Enable(None))?;
1413
1414 Ok(self)
1415 }
1416
1417 pub fn disable_log(&self) -> Result<&Self> {
1423 self.call_method(Log::Disable(None))?;
1424
1425 Ok(self)
1426 }
1427
1428 pub fn start_violations_report(&self, config: Vec<ViolationSetting>) -> Result<&Self> {
1432 self.call_method(Log::StartViolationsReport { config })?;
1433 Ok(self)
1434 }
1435
1436 pub fn stop_violations_report(&self) -> Result<&Self> {
1440 self.call_method(Log::StopViolationsReport(None))?;
1441 Ok(self)
1442 }
1443
1444 pub fn evaluate(&self, expression: &str, await_promise: bool) -> Result<Runtime::RemoteObject> {
1446 let result = self
1447 .call_method(Runtime::Evaluate {
1448 expression: expression.to_string(),
1449 return_by_value: Some(false),
1450 generate_preview: Some(true),
1451 silent: Some(false),
1452 await_promise: Some(await_promise),
1453 include_command_line_api: Some(false),
1454 user_gesture: Some(false),
1455 object_group: None,
1456 context_id: None,
1457 throw_on_side_effect: None,
1458 timeout: None,
1459 disable_breaks: None,
1460 repl_mode: None,
1461 allow_unsafe_eval_blocked_by_csp: None,
1462 unique_context_id: None,
1463 serialization_options: None,
1464 })?
1465 .result;
1466 Ok(result)
1467 }
1468
1469 pub fn add_event_listener(&self, listener: Arc<SyncSendEvent>) -> Result<Weak<SyncSendEvent>> {
1499 let mut listeners = self.event_listeners.lock().unwrap();
1500 listeners.push(listener);
1501 Ok(Arc::downgrade(listeners.last().unwrap()))
1502 }
1503
1504 pub fn remove_event_listener(&self, listener: &Weak<SyncSendEvent>) -> Result<()> {
1505 let listener = listener.upgrade();
1506 if listener.is_none() {
1507 return Ok(());
1508 }
1509 let listener = listener.unwrap();
1510 let mut listeners = self.event_listeners.lock().unwrap();
1511 let pos = listeners.iter().position(|x| Arc::ptr_eq(x, &listener));
1512 if let Some(idx) = pos {
1513 listeners.remove(idx);
1514 }
1515
1516 Ok(())
1517 }
1518
1519 pub fn close_target(&self) -> Result<bool> {
1521 self.call_method(Target::CloseTarget {
1522 target_id: self.get_target_id().to_string(),
1523 })
1524 .map(|r| r.success)
1525 }
1526
1527 pub fn close_with_unload(&self) -> Result<bool> {
1529 self.call_method(Page::Close(None)).map(|_| true)
1530 }
1531
1532 pub fn close(&self, fire_unload: bool) -> Result<bool> {
1534 self.optional_slow_motion_sleep(50);
1535
1536 if fire_unload {
1537 return self.close_with_unload();
1538 }
1539 self.close_target()
1540 }
1541
1542 pub fn activate(&self) -> Result<&Self> {
1544 self.call_method(Target::ActivateTarget {
1545 target_id: self.get_target_id().clone(),
1546 })
1547 .map(|_| self)
1548 }
1549
1550 pub fn get_bounds(&self) -> Result<CurrentBounds, Error> {
1556 self.transport
1557 .call_method_on_browser(Browser::GetWindowForTarget {
1558 target_id: Some(self.get_target_id().to_string()),
1559 })
1560 .map(|r| r.bounds.into())
1561 }
1562
1563 pub fn set_bounds(&self, bounds: Bounds) -> Result<&Self, Error> {
1568 let window_id = self
1569 .transport
1570 .call_method_on_browser(Browser::GetWindowForTarget {
1571 target_id: Some(self.get_target_id().to_string()),
1572 })?
1573 .window_id;
1574 if let Bounds::Normal { .. } = &bounds {
1577 self.transport
1578 .call_method_on_browser(Browser::SetWindowBounds {
1579 window_id,
1580 bounds: Browser::Bounds {
1581 left: None,
1582 top: None,
1583 width: None,
1584 height: None,
1585 window_state: Some(Browser::WindowState::Normal),
1586 },
1587 })?;
1588 }
1589 self.transport
1590 .call_method_on_browser(Browser::SetWindowBounds {
1591 window_id,
1592 bounds: bounds.into(),
1593 })?;
1594 Ok(self)
1595 }
1596
1597 pub fn get_cookies(&self) -> Result<Vec<Cookie>> {
1599 Ok(self
1600 .call_method(Network::GetCookies { urls: None })?
1601 .cookies)
1602 }
1603
1604 pub fn set_cookies(&self, cs: Vec<Network::CookieParam>) -> Result<()> {
1606 use Network::SetCookies;
1608 let url = self.get_url();
1609 let starts_with_http = url.starts_with("http");
1610 let cookies: Vec<Network::CookieParam> = cs
1611 .into_iter()
1612 .map(|c| {
1613 if c.url.is_none() && starts_with_http {
1614 Network::CookieParam {
1615 url: Some(url.clone()),
1616 ..c
1617 }
1618 } else {
1619 c
1620 }
1621 })
1622 .collect();
1623 self.delete_cookies(
1624 cookies
1625 .clone()
1626 .into_iter()
1627 .map(std::convert::Into::into)
1628 .collect(),
1629 )?;
1630 self.call_method(SetCookies { cookies })?;
1631 Ok(())
1632 }
1633
1634 pub fn delete_cookies(&self, cs: Vec<Network::DeleteCookies>) -> Result<()> {
1636 let url = self.get_url();
1638 let starts_with_http = url.starts_with("http");
1639 cs.into_iter()
1640 .map(|c| {
1641 if c.url.is_none() && starts_with_http {
1643 Network::DeleteCookies {
1644 url: Some(url.clone()),
1645 ..c
1646 }
1647 } else {
1648 c
1649 }
1650 })
1651 .try_for_each(|c| -> Result<(), anyhow::Error> {
1652 let _ = self.call_method(c)?;
1653 Ok(())
1654 })?;
1655 Ok(())
1656 }
1657
1658 pub fn get_title(&self) -> Result<String> {
1676 let remote_object = self.evaluate("document.title", false)?;
1677 Ok(serde_json::from_value(remote_object.value.unwrap())?)
1678 }
1679
1680 pub fn set_file_chooser_dialog_interception(&self, enabled: bool) -> Result<()> {
1684 self.call_method(SetInterceptFileChooserDialog { enabled })?;
1685 Ok(())
1686 }
1687
1688 pub fn handle_file_chooser(&self, files: Vec<String>, node_id: u32) -> Result<()> {
1696 self.call_method(DOM::SetFileInputFiles {
1697 files,
1698 node_id: Some(node_id),
1699 backend_node_id: None,
1700 object_id: None,
1701 })?;
1702 Ok(())
1703 }
1704
1705 pub fn set_extra_http_headers(&self, headers: HashMap<&str, &str>) -> Result<()> {
1706 self.call_method(Network::Enable {
1707 max_total_buffer_size: None,
1708 max_resource_buffer_size: None,
1709 max_post_data_size: None,
1710 })?;
1711 self.call_method(SetExtraHTTPHeaders {
1712 headers: Network::Headers(Some(json!(headers))),
1713 })?;
1714 Ok(())
1715 }
1716
1717 pub fn set_storage<T>(&self, item_name: &str, item: T) -> Result<()>
1718 where
1719 T: Serialize,
1720 {
1721 let value = json!(item).to_string();
1722
1723 self.evaluate(
1724 &format!(r#"localStorage.setItem("{item_name}",JSON.stringify({value}))"#),
1725 false,
1726 )?;
1727
1728 Ok(())
1729 }
1730
1731 pub fn get_storage<T>(&self, item_name: &str) -> Result<T>
1732 where
1733 T: DeserializeOwned,
1734 {
1735 let object = self.evaluate(&format!(r#"localStorage.getItem("{item_name}")"#), false)?;
1736
1737 let json: Option<T> = object.value.and_then(|v| match v {
1738 serde_json::Value::String(ref s) => {
1739 let result = serde_json::from_str(s);
1740
1741 if let Ok(r) = result {
1742 Some(r)
1743 } else {
1744 Some(serde_json::from_value(v).unwrap())
1745 }
1746 }
1747 _ => None,
1748 });
1749
1750 match json {
1751 Some(v) => Ok(v),
1752 None => Err(NoLocalStorageItemFound {}.into()),
1753 }
1754 }
1755
1756 pub fn remove_storage(&self, item_name: &str) -> Result<()> {
1757 self.evaluate(&format!(r#"localStorage.removeItem("{item_name}")"#), false)?;
1758
1759 Ok(())
1760 }
1761
1762 pub fn stop_loading(&self) -> Result<bool> {
1763 self.call_method(Page::StopLoading(None)).map(|_| true)
1764 }
1765
1766 fn bypass_user_agent(&self) -> Result<()> {
1767 let object = self.evaluate("window.navigator.userAgent", true)?;
1768
1769 match object.value.map(|x| x.to_string()) {
1770 Some(mut ua) => {
1771 ua = ua.replace("HeadlessChrome/", "Chrome/");
1772
1773 let re = regex::Regex::new(r"\(([^)]+)\)")?;
1774 ua = re.replace(&ua, "(Windows NT 10.0; Win64; x64)").to_string();
1775
1776 self.set_user_agent(&ua, None, None)?;
1777 Ok(())
1778 }
1779 None => Err(NoUserAgentEvaluated {}.into()),
1780 }
1781 }
1782
1783 fn bypass_wedriver(&self) -> Result<()> {
1784 self.call_method(Page::AddScriptToEvaluateOnNewDocument {
1785 source: "Object.defineProperty(navigator, 'webdriver', {get: () => undefined});"
1786 .to_string(),
1787 world_name: None,
1788 include_command_line_api: None,
1789 run_immediately: None,
1790 })?;
1791 Ok(())
1792 }
1793
1794 fn bypass_chrome(&self) -> Result<()> {
1795 self.call_method(Page::AddScriptToEvaluateOnNewDocument {
1796 source: "window.chrome = { runtime: {} };".to_string(),
1797 world_name: None,
1798 include_command_line_api: None,
1799 run_immediately: None,
1800 })?;
1801 Ok(())
1802 }
1803
1804 fn bypass_permissions(&self) -> Result<()> {
1805 let r = "const originalQuery = window.navigator.permissions.query;
1806 window.navigator.permissions.__proto__.query = parameters =>
1807 parameters.name === 'notifications'
1808 ? Promise.resolve({state: Notification.permission})
1809 : originalQuery(parameters);";
1810
1811 self.call_method(Page::AddScriptToEvaluateOnNewDocument {
1812 source: r.to_string(),
1813 world_name: None,
1814 include_command_line_api: None,
1815 run_immediately: None,
1816 })?;
1817 Ok(())
1818 }
1819
1820 fn bypass_plugins(&self) -> Result<()> {
1821 self.call_method(Page::AddScriptToEvaluateOnNewDocument {
1822 source: "Object.defineProperty(navigator, 'plugins', { get: () => [
1823 {filename:'internal-pdf-viewer'},
1824 {filename:'adsfkjlkjhalkh'},
1825 {filename:'internal-nacl-plugin'}
1826 ], });"
1827 .to_string(),
1828 world_name: None,
1829 include_command_line_api: None,
1830 run_immediately: None,
1831 })?;
1832 Ok(())
1833 }
1834
1835 fn bypass_webgl_vendor(&self) -> Result<()> {
1836 let r = "const getParameter = WebGLRenderingContext.getParameter;
1837 WebGLRenderingContext.prototype.getParameter = function(parameter) {
1838 // UNMASKED_VENDOR_WEBGL
1839 if (parameter === 37445) {
1840 return 'Google Inc. (NVIDIA)';
1841 }
1842 // UNMASKED_RENDERER_WEBGL
1843 if (parameter === 37446) {
1844 return 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Direct3D11 vs_5_0 ps_5_0, D3D11-27.21.14.5671)';
1845 }
1846
1847 return getParameter(parameter);
1848 };";
1849
1850 self.call_method(Page::AddScriptToEvaluateOnNewDocument {
1851 source: r.to_string(),
1852 world_name: None,
1853 include_command_line_api: None,
1854 run_immediately: None,
1855 })?;
1856 Ok(())
1857 }
1858
1859 pub fn enable_stealth_mode(&self) -> Result<()> {
1860 self.bypass_user_agent()?;
1861 self.bypass_wedriver()?;
1862 self.bypass_chrome()?;
1863 self.bypass_permissions()?;
1864 self.bypass_plugins()?;
1865 self.bypass_webgl_vendor()?;
1866 Ok(())
1867 }
1868
1869 pub fn start_screencast(
1870 &self,
1871 format: Option<Page::StartScreencastFormatOption>,
1872 quality: Option<u32>,
1873 max_width: Option<u32>,
1874 max_height: Option<u32>,
1875 every_nth_frame: Option<u32>,
1876 ) -> Result<()> {
1877 self.call_method(Page::StartScreencast {
1878 format,
1879 quality,
1880 max_width,
1881 max_height,
1882 every_nth_frame,
1883 })?;
1884 Ok(())
1885 }
1886
1887 pub fn stop_screencast(&self) -> Result<()> {
1888 self.call_method(Page::StopScreencast(None))?;
1889 Ok(())
1890 }
1891
1892 pub fn ack_screencast(&self, session_id: u32) -> Result<()> {
1893 self.call_method(Page::ScreencastFrameAck { session_id })?;
1894 Ok(())
1895 }
1896
1897 pub fn get_dialog(&self) -> Dialog {
1899 Dialog::new(self.session_id.clone(), self.transport.clone())
1900 }
1901}