use std::sync::Arc;
use std::time::Duration;
use serde_json::Value;
use tokio::time::{Instant, sleep};
use crate::browser::element::Element;
use crate::browser::static_element::StaticElement;
use crate::browser::tab::{TabCore, multi_query_expr, single_query_expr};
use crate::locator;
use crate::{Error, Result};
#[derive(Clone)]
pub struct Frame {
core: Arc<TabCore>,
frame_id: String,
}
impl Frame {
pub(crate) fn new(core: Arc<TabCore>, frame_id: String) -> Self {
Self { core, frame_id }
}
pub fn frame_id(&self) -> &str {
&self.frame_id
}
pub async fn run_js(&self, script: &str) -> Result<Value> {
self.core.evaluate_in(&self.frame_id, script, true).await
}
pub async fn html(&self) -> Result<String> {
Ok(self
.core
.evaluate_in(&self.frame_id, "document.documentElement.outerHTML", true)
.await?
.as_str()
.unwrap_or_default()
.to_string())
}
pub async fn url(&self) -> Result<String> {
Ok(self
.core
.evaluate_in(&self.frame_id, "location.href", true)
.await?
.as_str()
.unwrap_or_default()
.to_string())
}
pub async fn ele(&self, selector: &str) -> Result<Element> {
let deadline = Instant::now() + self.core.timeout();
loop {
if let Some(el) = self.find_once(selector).await? {
return Ok(el);
}
if Instant::now() >= deadline {
return Err(Error::ElementNotFound(selector.to_string()));
}
sleep(Duration::from_millis(100)).await;
}
}
pub async fn eles(&self, selector: &str) -> Result<Vec<Element>> {
let query = locator::parse(selector);
let expr = multi_query_expr(&query);
let result = self.core.evaluate_in(&self.frame_id, &expr, false).await?;
let Some(array_object_id) = result.get("objectId").and_then(|v| v.as_str()) else {
return Ok(Vec::new());
};
let oids = self
.core
.node_array_object_ids(&self.frame_id, array_object_id)
.await?;
Ok(oids
.into_iter()
.map(|oid| Element::new_in_frame(self.core.clone(), oid, self.frame_id.clone()))
.collect())
}
async fn find_once(&self, selector: &str) -> Result<Option<Element>> {
let query = locator::parse(selector);
let expr = single_query_expr(&query);
let result = self.core.evaluate_in(&self.frame_id, &expr, false).await?;
Ok(result.get("objectId").and_then(|v| v.as_str()).map(|oid| {
Element::new_in_frame(self.core.clone(), oid.to_string(), self.frame_id.clone())
}))
}
pub async fn s_ele(&self, selector: &str) -> Result<StaticElement> {
let html = self.html().await?;
StaticElement::parse(&html)?.ele(selector)
}
pub async fn s_eles(&self, selector: &str) -> Result<Vec<StaticElement>> {
let html = self.html().await?;
StaticElement::parse(&html)?.eles(selector)
}
}