glass/browser/session/
frame.rs1use super::*;
7
8impl BrowserSession {
9 pub async fn list_frames(&self) -> BrowserResult<Vec<FrameInfo>> {
14 let (target_id, target_session, active) = {
15 let topology = self.topology.lock().await;
16 (
17 topology.active_target_id.clone(),
18 topology.active_target_session_id.clone(),
19 topology.active_frame_id.clone(),
20 )
21 };
22 if target_id.is_none() {
23 return Err(TopologyError::new(
24 TopologyErrorKind::NoTargetSelected,
25 "no active target is selected; call listTargets to discover available pages",
26 )
27 .into());
28 }
29 let target_session = target_session.ok_or_else(|| {
30 TopologyError::new(
31 TopologyErrorKind::NoPageSession,
32 "active target has no CDP session; the session may need to be re-established",
33 )
34 })?;
35 let raw = self
36 .cdp
37 .send_to_session(&target_session, "Page.getFrameTree", None)
38 .await?;
39 let mut frames = Vec::new();
40 collect_frames(&raw["frameTree"], None, active.as_deref(), &mut frames)?;
41 let (attached_sessions, frame_parents) = {
42 let topology = self.topology.lock().await;
43 (
44 topology.frame_sessions.clone(),
45 topology.frame_parents.clone(),
46 )
47 };
48 let mut discovered_frame_sessions = Vec::new();
49 let mut stale_frame_sessions = HashSet::new();
50 let mut queried_sessions = HashSet::new();
51 for (attached_frame_id, session_id) in &attached_sessions {
52 if !queried_sessions.insert(session_id.clone()) {
53 continue;
54 }
55 let oopif_tree = match self
56 .cdp
57 .send_to_session(session_id, "Page.getFrameTree", None)
58 .await
59 {
60 Ok(tree) => tree,
61 Err(error) if error.code == -32_001 => {
62 stale_frame_sessions.insert(session_id.clone());
63 continue;
64 }
65 Err(error) => return Err(error.into()),
66 };
67 let start = frames.len();
68 collect_frames(
69 &oopif_tree["frameTree"],
70 frame_parents.get(attached_frame_id).map(String::as_str),
71 active.as_deref(),
72 &mut frames,
73 )?;
74 for frame in &frames[start..] {
75 discovered_frame_sessions.push((frame.id.clone(), session_id.clone()));
76 }
77 for frame in &mut frames[start..] {
78 frame.out_of_process = true;
79 }
80 }
81 let mut topology = self.topology.lock().await;
82 topology
83 .frame_sessions
84 .retain(|_, session| !stale_frame_sessions.contains(session));
85 for (frame_id, session_id) in discovered_frame_sessions {
86 topology.frame_sessions.insert(frame_id, session_id);
87 }
88 topology.frames = frames.clone();
89 Ok(frames)
90 }
91
92 pub async fn select_frame(&self, frame_id: &str) -> BrowserResult<FrameInfo> {
98 validate_topology_id(frame_id)?;
99 let frame = self
100 .list_frames()
101 .await?
102 .into_iter()
103 .find(|frame| frame.id == frame_id)
104 .ok_or_else(|| {
105 TopologyError::new(
106 TopologyErrorKind::NoSuchFrame,
107 format!("frame {frame_id} was not found; call listFrames to discover available frames"),
108 )
109 })?;
110 let session_id = {
111 let topology = self.topology.lock().await;
112 topology
113 .frame_sessions
114 .get(frame_id)
115 .cloned()
116 .or_else(|| topology.active_target_session_id.clone())
117 .ok_or_else(|| {
118 TopologyError::new(
119 TopologyErrorKind::NoPageSession,
120 "active target has no CDP session; the session may need to be re-established",
121 )
122 })?
123 };
124 let context_id = if frame.parent_id.is_none() {
125 None
126 } else {
127 let world = self
128 .cdp
129 .send_to_session(
130 &session_id,
131 "Page.createIsolatedWorld",
132 Some(serde_json::json!({"frameId": frame_id, "worldName":"glass"})),
133 )
134 .await?;
135 Some(
136 world["executionContextId"]
137 .as_i64()
138 .ok_or("Page.createIsolatedWorld returned no executionContextId")?,
139 )
140 };
141 self.cdp.set_active_route(
142 Some(session_id.clone()),
143 Some(frame_id.to_string()),
144 context_id,
145 );
146 {
147 let mut topology = self.topology.lock().await;
148 topology.active_frame_id = Some(frame_id.to_string());
149 topology.active_session_id = Some(session_id);
150 }
151 self.invalidate_observation();
152 Ok(FrameInfo {
153 active: true,
154 ..frame
155 })
156 }
157}