use serde_json::Value;
use tracing::debug;
use crate::error::{Error, Result};
use crate::identifiers::FrameId;
use crate::protocol::{BrowsingContextCommand, Command, Response};
use super::{FrameInfo, Tab};
impl Tab {
pub async fn switch_to_frame(&self, iframe: &crate::browser::Element) -> Result<Tab> {
debug!(tab_id = %self.inner.tab_id, element_id = %iframe.id(), "Switching to frame");
let command = Command::BrowsingContext(BrowsingContextCommand::SwitchToFrame {
element_id: iframe.id().clone(),
});
let response = self.send_command(command).await?;
let frame_id = extract_frame_id(&response)?;
Ok(Tab::new(
self.inner.tab_id,
FrameId::new(frame_id),
self.inner.session_id,
self.inner.window.clone(),
))
}
pub async fn switch_to_frame_by_index(&self, index: usize) -> Result<Tab> {
debug!(tab_id = %self.inner.tab_id, index, "Switching to frame by index");
let command =
Command::BrowsingContext(BrowsingContextCommand::SwitchToFrameByIndex { index });
let response = self.send_command(command).await?;
let frame_id = extract_frame_id(&response)?;
Ok(Tab::new(
self.inner.tab_id,
FrameId::new(frame_id),
self.inner.session_id,
self.inner.window.clone(),
))
}
pub async fn switch_to_frame_by_url(&self, url_pattern: &str) -> Result<Tab> {
debug!(tab_id = %self.inner.tab_id, url_pattern, "Switching to frame by URL");
let command = Command::BrowsingContext(BrowsingContextCommand::SwitchToFrameByUrl {
url_pattern: url_pattern.to_string(),
});
let response = self.send_command(command).await?;
let frame_id = extract_frame_id(&response)?;
Ok(Tab::new(
self.inner.tab_id,
FrameId::new(frame_id),
self.inner.session_id,
self.inner.window.clone(),
))
}
pub async fn switch_to_parent_frame(&self) -> Result<Tab> {
debug!(tab_id = %self.inner.tab_id, "Switching to parent frame");
let command = Command::BrowsingContext(BrowsingContextCommand::SwitchToParentFrame);
let response = self.send_command(command).await?;
let frame_id = extract_frame_id(&response)?;
Ok(Tab::new(
self.inner.tab_id,
FrameId::new(frame_id),
self.inner.session_id,
self.inner.window.clone(),
))
}
#[must_use]
pub fn switch_to_main_frame(&self) -> Tab {
debug!(tab_id = %self.inner.tab_id, "Switching to main frame");
Tab::new(
self.inner.tab_id,
FrameId::main(),
self.inner.session_id,
self.inner.window.clone(),
)
}
pub async fn get_frame_count(&self) -> Result<usize> {
debug!(tab_id = %self.inner.tab_id, "Getting frame count");
let command = Command::BrowsingContext(BrowsingContextCommand::GetFrameCount);
let response = self.send_command(command).await?;
let count = response
.result
.as_ref()
.and_then(|v| v.get("count"))
.and_then(|v| v.as_u64())
.ok_or_else(|| Error::protocol("No count in response"))?;
debug!(tab_id = %self.inner.tab_id, count = count, "Got frame count");
Ok(count as usize)
}
pub async fn get_all_frames(&self) -> Result<Vec<FrameInfo>> {
debug!(tab_id = %self.inner.tab_id, "Getting all frames");
let command = Command::BrowsingContext(BrowsingContextCommand::GetAllFrames);
let response = self.send_command(command).await?;
let frames: Vec<FrameInfo> = response
.result
.as_ref()
.and_then(|v| v.get("frames"))
.and_then(|v| v.as_array())
.map(|arr| arr.iter().filter_map(parse_frame_info).collect())
.unwrap_or_default();
debug!(tab_id = %self.inner.tab_id, count = frames.len(), "Got all frames");
Ok(frames)
}
}
fn extract_frame_id(response: &Response) -> Result<u64> {
response
.result
.as_ref()
.and_then(|v| v.get("frameId"))
.and_then(|v| v.as_u64())
.ok_or_else(|| Error::protocol("No frameId in response"))
}
fn parse_frame_info(v: &Value) -> Option<FrameInfo> {
Some(FrameInfo {
frame_id: FrameId::new(v.get("frameId")?.as_u64()?),
parent_frame_id: v
.get("parentFrameId")
.and_then(|p| p.as_i64())
.and_then(|p| {
if p < 0 {
None
} else {
Some(FrameId::new(p as u64))
}
}),
url: v.get("url")?.as_str()?.to_string(),
})
}