use std::time::Duration;
use serde_json::json;
use tokio::time::Instant;
use crate::browser::interceptor::InterceptedRequest;
use crate::browser::listener::DataPacket;
use crate::browser::tab::{CookieParam, ListenStream, LoadMode, Tab};
use crate::{Error, Result};
pub struct Wait {
tab: Tab,
}
impl Wait {
pub(crate) fn new(tab: Tab) -> Self {
Self { tab }
}
fn timeout_or_default(&self, t: Option<Duration>) -> Duration {
t.unwrap_or_else(|| self.tab.core.timeout())
}
pub async fn secs(&self, secs: f64) {
tokio::time::sleep(Duration::from_secs_f64(secs.max(0.0))).await;
}
pub async fn doc_loaded(&self, timeout: Option<Duration>) -> Result<bool> {
let d = self.timeout_or_default(timeout);
self.tab.core.poll_ready_complete(d).await
}
pub async fn ele_loaded(&self, selector: &str, timeout: Option<Duration>) -> Result<bool> {
let deadline = Instant::now() + self.timeout_or_default(timeout);
loop {
if self.tab.find_once(selector).await?.is_some() {
return Ok(true);
}
if Instant::now() >= deadline {
return Ok(false);
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
pub async fn ele_displayed(&self, selector: &str, timeout: Option<Duration>) -> Result<bool> {
let deadline = Instant::now() + self.timeout_or_default(timeout);
loop {
if let Some(el) = self.tab.find_once(selector).await?
&& el.is_displayed().await.unwrap_or(false)
{
return Ok(true);
}
if Instant::now() >= deadline {
return Ok(false);
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
pub async fn ele_deleted(&self, selector: &str, timeout: Option<Duration>) -> Result<bool> {
let deadline = Instant::now() + self.timeout_or_default(timeout);
loop {
if self.tab.find_once(selector).await?.is_none() {
return Ok(true);
}
if Instant::now() >= deadline {
return Ok(false);
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
pub async fn upload_paths_inputted(&self, timeout: Option<Duration>) -> Result<bool> {
self.tab.wait_upload_paths_inputted(timeout).await
}
}
pub struct Scroll {
tab: Tab,
}
impl Scroll {
pub(crate) fn new(tab: Tab) -> Self {
Self { tab }
}
pub async fn to_top(&self) -> Result<()> {
self.run("window.scrollTo(window.scrollX, 0)").await
}
pub async fn to_bottom(&self) -> Result<()> {
self.run(
"window.scrollTo(window.scrollX, \
Math.max(document.documentElement.scrollHeight, \
document.body ? document.body.scrollHeight : 0))",
)
.await
}
pub async fn to_left(&self) -> Result<()> {
self.run("window.scrollTo(0, window.scrollY)").await
}
pub async fn to_right(&self) -> Result<()> {
self.run(
"window.scrollTo(Math.max(document.documentElement.scrollWidth, \
document.body ? document.body.scrollWidth : 0), window.scrollY)",
)
.await
}
pub async fn to_half(&self) -> Result<()> {
self.run(
"window.scrollTo(window.scrollX, \
Math.max(document.documentElement.scrollHeight, \
document.body ? document.body.scrollHeight : 0) / 2)",
)
.await
}
pub async fn to_location(&self, x: f64, y: f64) -> Result<()> {
self.run(&format!("window.scrollTo({x}, {y})")).await
}
pub async fn up(&self, pixel: f64) -> Result<()> {
self.tab.scroll_by(0.0, -pixel).await
}
pub async fn down(&self, pixel: f64) -> Result<()> {
self.tab.scroll_by(0.0, pixel).await
}
pub async fn left(&self, pixel: f64) -> Result<()> {
self.tab.scroll_by(-pixel, 0.0).await
}
pub async fn right(&self, pixel: f64) -> Result<()> {
self.tab.scroll_by(pixel, 0.0).await
}
async fn run(&self, js: &str) -> Result<()> {
self.tab.run_js(js).await?;
Ok(())
}
}
pub struct SetTab {
tab: Tab,
}
impl SetTab {
pub(crate) fn new(tab: Tab) -> Self {
Self { tab }
}
pub async fn cookies(&self, cookies: Vec<CookieParam>) -> Result<()> {
self.tab.set_cookies(cookies).await
}
pub fn timeout(&self, d: Duration) {
self.tab.core.set_timeout(d);
}
pub fn load_mode(&self, m: LoadMode) {
self.tab.core.set_load_mode(m);
}
pub async fn user_agent(&self, ua: &str) -> Result<()> {
self.tab
.core
.conn
.send(
"Browser.setUserAgentOverride",
json!({
"browserContextId": self.tab.core.browser_context_id,
"userAgent": ua,
}),
None,
)
.await?;
Ok(())
}
pub async fn upload_files(&self, paths: &[&str]) -> Result<()> {
self.tab.set_upload_files(paths).await
}
pub fn window(&self) -> Window {
Window::new(self.tab.clone())
}
}
pub struct Window {
tab: Tab,
}
impl Window {
pub(crate) fn new(tab: Tab) -> Self {
Self { tab }
}
pub async fn size(&self, width: u32, height: u32) -> Result<()> {
self.tab.core.set_viewport_size(width, height).await
}
pub async fn viewport(&self, width: u32, height: u32) -> Result<()> {
self.tab.core.set_viewport_size(width, height).await
}
pub async fn max(&self) -> Result<()> {
let v = self
.tab
.run_js("[screen.availWidth, screen.availHeight]")
.await?;
let w = v.get(0).and_then(|x| x.as_f64()).unwrap_or(1280.0) as u32;
let h = v.get(1).and_then(|x| x.as_f64()).unwrap_or(800.0) as u32;
self.tab.core.set_viewport_size(w, h).await
}
}
pub struct Intercept {
tab: Tab,
}
impl Intercept {
pub(crate) fn new(tab: Tab) -> Self {
Self { tab }
}
pub async fn start(&self, targets: &[&str]) -> Result<()> {
self.tab.intercept_start(targets).await
}
pub async fn start_xhr(&self, targets: &[&str]) -> Result<()> {
self.tab.intercept_xhr(targets).await
}
pub async fn is_intercepting(&self) -> bool {
self.tab.core.interceptor.lock().await.is_some()
}
pub async fn next(&self) -> Result<InterceptedRequest> {
self.tab.intercept_next().await
}
pub async fn next_timeout(&self, timeout: Duration) -> Result<Option<InterceptedRequest>> {
self.tab.intercept_next_timeout(timeout).await
}
pub async fn stop(&self) -> Result<()> {
self.tab.intercept_stop().await
}
}
pub struct Listen {
tab: Tab,
}
impl Listen {
pub(crate) fn new(tab: Tab) -> Self {
Self { tab }
}
pub async fn start(&self, targets: &[&str]) -> Result<()> {
self.tab.listen_start(targets).await
}
pub async fn is_listening(&self) -> bool {
*self.tab.core.listen_active.lock().await
}
pub async fn wait(&self) -> Result<DataPacket> {
self.tab.listen_wait().await
}
pub async fn wait_timeout(&self, timeout: Duration) -> Result<Option<DataPacket>> {
match self.tab.listen_wait_timeout(timeout).await {
Ok(p) => Ok(Some(p)),
Err(Error::Timeout(_)) => Ok(None),
Err(e) => Err(e),
}
}
pub async fn next(&self) -> Result<Option<DataPacket>> {
self.tab.listen_next().await
}
pub async fn wait_count(
&self,
count: usize,
timeout: Option<Duration>,
) -> Result<Vec<DataPacket>> {
let total = timeout.unwrap_or_else(|| self.tab.core.timeout());
let deadline = Instant::now() + total;
let mut out = Vec::with_capacity(count);
while out.len() < count {
let remain = deadline.saturating_duration_since(Instant::now());
if remain.is_zero() {
break;
}
match self.tab.listen_wait_timeout(remain).await {
Ok(p) => out.push(p),
Err(Error::Timeout(_)) => break,
Err(e) => return Err(e),
}
}
Ok(out)
}
pub async fn steps(&self) -> Result<ListenStream> {
self.tab.listen_stream().await
}
pub async fn stop(&self) -> Result<()> {
self.tab.listen_stop().await
}
}