use tracing::instrument;
use super::Page;
use super::frame::Frame;
use super::frame_locator::FrameLocator;
use crate::error::PageError;
impl Page {
pub fn frame_locator(&self, selector: impl Into<String>) -> FrameLocator<'_> {
FrameLocator::new(self, selector)
}
#[instrument(level = "debug", skip(self))]
pub async fn main_frame(&self) -> Result<Frame, PageError> {
if self.closed {
return Err(PageError::Closed);
}
let result: viewpoint_cdp::protocol::page::GetFrameTreeResult = self
.connection
.send_command("Page.getFrameTree", None::<()>, Some(&self.session_id))
.await?;
let frame_info = result.frame_tree.frame;
Ok(Frame::with_context_registry_and_indices(
self.connection.clone(),
self.session_id.clone(),
frame_info.id,
frame_info.parent_id,
frame_info.loader_id,
frame_info.url,
frame_info.name.unwrap_or_default(),
self.context_registry.clone(),
self.context_index,
self.page_index,
0, ))
}
#[instrument(level = "debug", skip(self))]
pub async fn frames(&self) -> Result<Vec<Frame>, PageError> {
if self.closed {
return Err(PageError::Closed);
}
let result: viewpoint_cdp::protocol::page::GetFrameTreeResult = self
.connection
.send_command("Page.getFrameTree", None::<()>, Some(&self.session_id))
.await?;
let mut frames = Vec::new();
let mut frame_index_counter = 0usize;
self.collect_frames(&result.frame_tree, &mut frames, &mut frame_index_counter);
Ok(frames)
}
fn collect_frames(
&self,
tree: &viewpoint_cdp::protocol::page::FrameTree,
frames: &mut Vec<Frame>,
frame_index_counter: &mut usize,
) {
let frame_info = &tree.frame;
let frame_index = *frame_index_counter;
*frame_index_counter += 1;
frames.push(Frame::with_context_registry_and_indices(
self.connection.clone(),
self.session_id.clone(),
frame_info.id.clone(),
frame_info.parent_id.clone(),
frame_info.loader_id.clone(),
frame_info.url.clone(),
frame_info.name.clone().unwrap_or_default(),
self.context_registry.clone(),
self.context_index,
self.page_index,
frame_index,
));
if let Some(children) = &tree.child_frames {
for child in children {
self.collect_frames(child, frames, frame_index_counter);
}
}
}
#[instrument(level = "debug", skip(self), fields(name = %name))]
pub async fn frame(&self, name: &str) -> Result<Option<Frame>, PageError> {
let frames = self.frames().await?;
for frame in frames {
if frame.name() == name {
return Ok(Some(frame));
}
}
Ok(None)
}
#[instrument(level = "debug", skip(self), fields(pattern = %pattern))]
pub async fn frame_by_url(&self, pattern: &str) -> Result<Option<Frame>, PageError> {
let frames = self.frames().await?;
let regex_pattern = pattern
.replace("**", ".*")
.replace('*', "[^/]*")
.replace('?', ".");
let regex = regex::Regex::new(&format!("^{regex_pattern}$"))
.map_err(|e| PageError::EvaluationFailed(format!("Invalid URL pattern: {e}")))?;
for frame in frames {
if regex.is_match(&frame.url()) {
return Ok(Some(frame));
}
}
Ok(None)
}
}