glass/browser/session/
navigate.rs1use super::*;
7
8impl BrowserSession {
9 pub async fn page_info(&self) -> BrowserResult<PageInfo> {
14 self.cdp.with_current_route(async {
15 let raw = self
16 .cdp
17 .evaluate(
18 "JSON.stringify({url: location.href, title: document.title, ready_state: document.readyState})",
19 )
20 .await?;
21 let value = runtime_value(&raw)?;
22 let json = value
23 .as_str()
24 .ok_or("document state evaluation returned a non-string value")?;
25 let mut page: PageInfo = serde_json::from_str(json)?;
26 (page.target_id, page.frame_id) = self.route_identity().await?;
27 Ok(page)
28 }).await
29 }
30
31 pub async fn navigate(&self, url: &str) -> BrowserResult<PageInfo> {
36 self.navigate_with_deadline(url, Duration::from_secs(20))
37 .await
38 }
39
40 pub async fn navigate_with_deadline(
45 &self,
46 url: &str,
47 deadline: Duration,
48 ) -> BrowserResult<PageInfo> {
49 self.cdp
50 .with_current_target_route(async {
51 validate_wait_deadline(deadline)?;
52 let url = normalize_url(url);
53 self.policy.require_url(&url).await?;
54 self.enforce_polite_navigation(&url).await?;
55 if let Some(interception) = &self.policy_interception
56 && let Some(error) = interception.take_denial().await
57 {
58 return Err(error.into());
59 }
60 let result = async {
61 let mut events = self.cdp.subscribe_events();
62 let started = tokio::time::Instant::now();
63 let navigation = tokio::time::timeout(deadline, self.cdp.navigate(&url))
64 .await
65 .map_err(|_| {
66 wait_timeout("lifecycle", deadline, "navigate_command_pending")
67 })??;
68 if let Some(frame_id) = navigation["frameId"].as_str() {
69 validate_topology_id(frame_id)?;
70 self.topology.lock().await.active_frame_id = Some(frame_id.to_string());
71 self.cdp
72 .set_active_frame_context(Some(frame_id.to_string()), None);
73 }
74 let remaining = deadline.saturating_sub(started.elapsed());
75 self.wait_loop(
76 WaitCondition::Lifecycle("complete".to_string()),
77 remaining,
78 deadline,
79 &mut events,
80 true,
81 )
82 .await?;
83 let remaining = deadline.saturating_sub(started.elapsed());
84 let main_frame = self
85 .list_frames()
86 .await?
87 .into_iter()
88 .find(|frame| frame.parent_id.is_none())
89 .ok_or("navigated target returned no main frame")?;
90 self.select_frame(&main_frame.id).await?;
91 let page = tokio::time::timeout(remaining, self.page_info())
92 .await
93 .map_err(|_| wait_timeout("lifecycle", deadline, "page_info_pending"))??;
94 self.invalidate_observation();
95 self.record_audit("navigate", url);
96 Ok(page)
97 }
98 .await;
99 if let Some(error) = match &self.policy_interception {
100 Some(interception) => interception.take_denial().await,
101 None => None,
102 } {
103 return Err(error.into());
104 }
105 result
106 })
107 .await
108 }
109
110 pub async fn evaluate(&self, expression: &str) -> BrowserResult<Value> {
115 self.policy.require(PolicyCapability::Evaluate)?;
116 self.cdp
117 .with_current_route(async {
118 let result = self.evaluate_value(expression).await;
119 self.invalidate_observation();
123 self.record_audit("evaluate", redact_diagnostic_text(expression));
124 result
125 })
126 .await
127 }
128}