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 let target_id = topology.active_target_id.clone();
17 (
18 target_id.clone(),
19 topology.active_target_session_id.clone(),
20 topology.active_frame_id.clone(),
21 )
22 };
23 if target_id.is_none() {
24 return Err(TopologyError::new(
25 TopologyErrorKind::NoTargetSelected,
26 "no active target is selected; call listTargets to discover available pages",
27 )
28 .into());
29 }
30 if target_session.is_none() {
31 let mut topology = self.topology.lock().await;
32 topology.active_target_id = None;
33 topology.active_session_id = None;
34 topology.active_target_session_id = None;
35 topology.active_frame_id = None;
36 topology.frames.clear();
37 topology.frame_sessions.clear();
38 topology.frame_parents.clear();
39 self.cdp.set_active_target_route(None, None, None, None);
40 return Err(TopologyError::new(
41 TopologyErrorKind::NoTargetSelected,
42 "no active target is selected; call listTargets to discover available pages",
43 )
44 .into());
45 }
46 let target_session = target_session.ok_or_else(|| {
47 TopologyError::new(
48 TopologyErrorKind::NoPageSession,
49 "active target has no CDP session; the session may need to be re-established",
50 )
51 })?;
52 let raw = match self
53 .cdp
54 .send_to_session(&target_session, "Page.getFrameTree", None)
55 .await
56 {
57 Ok(raw) => raw,
58 Err(error) => {
59 let mut topology = self.topology.lock().await;
64 topology.active_target_id = None;
65 topology.active_session_id = None;
66 topology.active_target_session_id = None;
67 topology.active_frame_id = None;
68 topology.frames.clear();
69 topology.frame_sessions.clear();
70 topology.frame_parents.clear();
71 self.cdp.set_active_target_route(None, None, None, None);
72 let _ = error;
73 return Err(TopologyError::new(
74 TopologyErrorKind::NoTargetSelected,
75 "no active target is selected; call listTargets to discover available pages",
76 )
77 .into());
78 }
79 };
80 let mut frames = Vec::new();
81 collect_frames(&raw["frameTree"], None, active.as_deref(), &mut frames)?;
82 let (attached_sessions, frame_parents) = {
83 let topology = self.topology.lock().await;
84 (
85 topology.frame_sessions.clone(),
86 topology.frame_parents.clone(),
87 )
88 };
89 let mut discovered_frame_sessions = Vec::new();
90 let mut stale_frame_sessions = HashSet::new();
91 let mut queried_sessions = HashSet::new();
92 for (attached_frame_id, session_id) in &attached_sessions {
93 if !queried_sessions.insert(session_id.clone()) {
94 continue;
95 }
96 let oopif_tree = match self
97 .cdp
98 .send_to_session(session_id, "Page.getFrameTree", None)
99 .await
100 {
101 Ok(tree) => tree,
102 Err(error) if error.code == -32_001 => {
103 stale_frame_sessions.insert(session_id.clone());
104 continue;
105 }
106 Err(error) => return Err(error.into()),
107 };
108 let start = frames.len();
109 collect_frames(
110 &oopif_tree["frameTree"],
111 frame_parents.get(attached_frame_id).map(String::as_str),
112 active.as_deref(),
113 &mut frames,
114 )?;
115 for frame in &frames[start..] {
116 discovered_frame_sessions.push((frame.id.clone(), session_id.clone()));
117 }
118 for frame in &mut frames[start..] {
119 frame.out_of_process = true;
120 }
121 }
122 let mut topology = self.topology.lock().await;
123 topology
124 .frame_sessions
125 .retain(|_, session| !stale_frame_sessions.contains(session));
126 for (frame_id, session_id) in discovered_frame_sessions {
127 topology.frame_sessions.insert(frame_id, session_id);
128 }
129 topology.frames = frames.clone();
130 Ok(frames)
131 }
132
133 pub async fn select_frame(&self, frame_id: &str) -> BrowserResult<FrameInfo> {
139 validate_topology_id(frame_id)?;
140 let frame = self
141 .list_frames()
142 .await?
143 .into_iter()
144 .find(|frame| frame.id == frame_id)
145 .ok_or_else(|| {
146 TopologyError::new(
147 TopologyErrorKind::NoSuchFrame,
148 format!("frame {frame_id} was not found; call listFrames to discover available frames"),
149 )
150 })?;
151 let session_id = {
152 let topology = self.topology.lock().await;
153 topology
154 .frame_sessions
155 .get(frame_id)
156 .cloned()
157 .or_else(|| topology.active_target_session_id.clone())
158 .ok_or_else(|| {
159 TopologyError::new(
160 TopologyErrorKind::NoPageSession,
161 "active target has no CDP session; the session may need to be re-established",
162 )
163 })?
164 };
165 let context_id = if frame.parent_id.is_none() {
166 None
167 } else {
168 let world = self
169 .cdp
170 .send_to_session(
171 &session_id,
172 "Page.createIsolatedWorld",
173 Some(serde_json::json!({"frameId": frame_id, "worldName":"glass"})),
174 )
175 .await?;
176 Some(
177 world["executionContextId"]
178 .as_i64()
179 .ok_or("Page.createIsolatedWorld returned no executionContextId")?,
180 )
181 };
182 self.cdp.set_active_route(
183 Some(session_id.clone()),
184 Some(frame_id.to_string()),
185 context_id,
186 );
187 {
188 let mut topology = self.topology.lock().await;
189 topology.active_frame_id = Some(frame_id.to_string());
190 topology.active_session_id = Some(session_id);
191 }
192 self.invalidate_observation();
193 Ok(FrameInfo {
194 active: true,
195 ..frame
196 })
197 }
198}