firefox_webdriver/browser/tab/
frames.rs1use serde_json::Value;
4use tracing::debug;
5
6use crate::error::{Error, Result};
7use crate::identifiers::FrameId;
8use crate::protocol::{BrowsingContextCommand, Command, Response};
9
10use super::{FrameInfo, Tab};
11
12impl Tab {
17 pub async fn switch_to_frame(&self, iframe: &crate::browser::Element) -> Result<Tab> {
32 debug!(tab_id = %self.inner.tab_id, element_id = %iframe.id(), "Switching to frame");
33
34 let command = Command::BrowsingContext(BrowsingContextCommand::SwitchToFrame {
35 element_id: iframe.id().clone(),
36 });
37 let response = self.send_command(command).await?;
38
39 let frame_id = extract_frame_id(&response)?;
40
41 Ok(Tab::new(
42 self.inner.tab_id,
43 FrameId::new(frame_id),
44 self.inner.session_id,
45 self.inner.window.clone(),
46 ))
47 }
48
49 pub async fn switch_to_frame_by_index(&self, index: usize) -> Result<Tab> {
55 debug!(tab_id = %self.inner.tab_id, index, "Switching to frame by index");
56
57 let command =
58 Command::BrowsingContext(BrowsingContextCommand::SwitchToFrameByIndex { index });
59 let response = self.send_command(command).await?;
60
61 let frame_id = extract_frame_id(&response)?;
62
63 Ok(Tab::new(
64 self.inner.tab_id,
65 FrameId::new(frame_id),
66 self.inner.session_id,
67 self.inner.window.clone(),
68 ))
69 }
70
71 pub async fn switch_to_frame_by_url(&self, url_pattern: &str) -> Result<Tab> {
79 debug!(tab_id = %self.inner.tab_id, url_pattern, "Switching to frame by URL");
80
81 let command = Command::BrowsingContext(BrowsingContextCommand::SwitchToFrameByUrl {
82 url_pattern: url_pattern.to_string(),
83 });
84 let response = self.send_command(command).await?;
85
86 let frame_id = extract_frame_id(&response)?;
87
88 Ok(Tab::new(
89 self.inner.tab_id,
90 FrameId::new(frame_id),
91 self.inner.session_id,
92 self.inner.window.clone(),
93 ))
94 }
95
96 pub async fn switch_to_parent_frame(&self) -> Result<Tab> {
98 debug!(tab_id = %self.inner.tab_id, "Switching to parent frame");
99
100 let command = Command::BrowsingContext(BrowsingContextCommand::SwitchToParentFrame);
101 let response = self.send_command(command).await?;
102
103 let frame_id = extract_frame_id(&response)?;
104
105 Ok(Tab::new(
106 self.inner.tab_id,
107 FrameId::new(frame_id),
108 self.inner.session_id,
109 self.inner.window.clone(),
110 ))
111 }
112
113 #[must_use]
115 pub fn switch_to_main_frame(&self) -> Tab {
116 debug!(tab_id = %self.inner.tab_id, "Switching to main frame");
117
118 Tab::new(
119 self.inner.tab_id,
120 FrameId::main(),
121 self.inner.session_id,
122 self.inner.window.clone(),
123 )
124 }
125
126 pub async fn get_frame_count(&self) -> Result<usize> {
128 debug!(tab_id = %self.inner.tab_id, "Getting frame count");
129 let command = Command::BrowsingContext(BrowsingContextCommand::GetFrameCount);
130 let response = self.send_command(command).await?;
131
132 let count = response
133 .result
134 .as_ref()
135 .and_then(|v| v.get("count"))
136 .and_then(|v| v.as_u64())
137 .ok_or_else(|| Error::protocol("No count in response"))?;
138
139 debug!(tab_id = %self.inner.tab_id, count = count, "Got frame count");
140 Ok(count as usize)
141 }
142
143 pub async fn get_all_frames(&self) -> Result<Vec<FrameInfo>> {
145 debug!(tab_id = %self.inner.tab_id, "Getting all frames");
146 let command = Command::BrowsingContext(BrowsingContextCommand::GetAllFrames);
147 let response = self.send_command(command).await?;
148
149 let frames: Vec<FrameInfo> = response
150 .result
151 .as_ref()
152 .and_then(|v| v.get("frames"))
153 .and_then(|v| v.as_array())
154 .map(|arr| arr.iter().filter_map(parse_frame_info).collect())
155 .unwrap_or_default();
156
157 debug!(tab_id = %self.inner.tab_id, count = frames.len(), "Got all frames");
158 Ok(frames)
159 }
160}
161
162fn extract_frame_id(response: &Response) -> Result<u64> {
168 response
169 .result
170 .as_ref()
171 .and_then(|v| v.get("frameId"))
172 .and_then(|v| v.as_u64())
173 .ok_or_else(|| Error::protocol("No frameId in response"))
174}
175
176fn parse_frame_info(v: &Value) -> Option<FrameInfo> {
178 Some(FrameInfo {
179 frame_id: FrameId::new(v.get("frameId")?.as_u64()?),
180 parent_frame_id: v
181 .get("parentFrameId")
182 .and_then(|p| p.as_i64())
183 .and_then(|p| {
184 if p < 0 {
185 None
186 } else {
187 Some(FrameId::new(p as u64))
188 }
189 }),
190 url: v.get("url")?.as_str()?.to_string(),
191 })
192}